API
Fetching the configuration
What GET /device/config returns, how to poll cheaply with ETag and 304, and how a device reads the controller.
With GET /device/config your device learns which sensor codes to report and how often to measure. With the ETag you only download it when something has changed.
What it contains
For circuit 201 (collector, tank bottom, tank top, pump), shortened to two sensors:
{
"config_version": "9f2c1a0b7d3e4f56",
"device": {"id": 42, "name": "Attic", "mode": "live"},
"intervals": {"scan_seconds": 60},
"controller": {
"brand": "OEG", "model": "KSW-E, KSW, KS2W", "protocol": "modbus_ascii",
"connection": {"slave_address": 128, "baudrate": 9600, "parity": "E",
"bytesize": 7, "stopbits": 1, "timeout_ms": 1000}
},
"sensors": [
{"sensor": "T1", "name": "Temperatuur zonnepaneel", "type": "collector_temp",
"category": "temperature", "unit": "°C", "position": null,
"address": {"register": 38, "decimals": 1, "signed": true, "function_code": 3},
"plausible_min": -50.0, "plausible_max": 250.0},
{"sensor": "R2", "name": "Pomp", "type": "pump",
"category": "relay", "unit": null, "position": null,
"address": {"register": 58, "decimals": 1, "signed": true, "function_code": 3},
"plausible_min": null, "plausible_max": null}
]
}
| Field | Meaning |
|---|---|
config_version |
A hash of the content. Equal to the ETag, without the quotes |
device |
Id, name and mode (live or simulated) of the device |
intervals.scan_seconds |
How often the device measures, in seconds. 60 by default. Shown in the portal as Scan interval, but you cannot change it there |
controller |
The controller of the circuit: brand, model, protocol (how to read it) and connection (that protocol's settings). null while the device has no circuit |
sensors |
The sensors of the circuit |
Per sensor:
| Field | Meaning |
|---|---|
sensor |
The code you send with every reading, such as T1 or R2 |
name |
The name shown in the portal. Display only; the sensor names in the catalogue are in Dutch |
type |
What the sensor measures, independent of brand: collector_temp, tank_bottom_temp, solar_pump, … |
category |
temperature, relay (an output such as a pump or valve), energy or flow |
unit |
Unit, such as °C, kWh or l/min. null for outputs |
position |
Which tank or panel, for installations with more than one. Otherwise null |
address |
Where the controller keeps the value, in the shape of the protocol. null if the controller does not have this sensor. See below |
plausible_min, plausible_max |
The range in which a value can be right. null for outputs |
Polling with ETag and 304
The answer has an ETag header:
ETag: "9f2c1a0b7d3e4f56"
Send it back unchanged next time:
If-None-Match: "9f2c1a0b7d3e4f56"
Nothing changed: 304 Not Modified without a body. Something changed: 200 with the new configuration and ETag.
- A compressing proxy sometimes turns the ETag into
W/"…"or"…-gzip". Send back what you received: the server recognises those forms too. - Every 15 minutes is enough; the BoilerBuddy Alpo does the same. Every request counts towards your 60 per minute.
- It changes when the owner changes the circuit or the device name, or when BoilerBuddy changes the scan interval or the sensor catalogue.
- Keep the last configuration on disk, so the device can measure after a restart even without internet.
Without a circuit
Right after it is created or paired, a device often has controller set to null and an empty sensors list. Don't send anything yet: every code would go into quarantine. Keep polling until the owner chooses a circuit; the ETag then changes.
Only codes from the configuration
Only send codes listed in sensors; any other code goes into quarantine (see Sending readings). If you briefly cannot read a sensor, send "quality": "no_response" with "value": null.
Reading the controller
controller.protocol says how to read the controller. controller.connection and each sensor's address have the shape of that protocol. Today that is always Modbus; more protocols may follow. If you don't know the protocol, read nothing and keep polling.
Modbus
For modbus_ascii and modbus_rtu, as on the Alpo:
- Open the serial port with the settings in
connection:slave_address,baudrate,parity(E,OorN),bytesizeandstopbits.timeout_msis the time to wait per request. - For each sensor, read one 16-bit register at
address.registerwithaddress.function_code(3 is "read holding registers", 4 "read input registers").signed: truemeans two's complement. Skip sensors without anaddress. - Divide by 10 to the power of
decimals: raw 614 withdecimals: 1is 61.4. - 32767, -32768, 65535 or -9999 means "no sensor": send
sensor_faultwithout a value. - Outside
plausible_minandplausible_max: send the value without_of_range. - No answer: send
no_responsewithout a value.
For the OEG KSW controllers (KSW-E, KSW and KS2W), T1 to T6 are at registers 38 to 43, R1 at 57, R2 at 58 and R3 at 31, all with one decimal, signed and function code 3.
If you read your sensors another way, ignore controller and address and only use sensor, type, category, unit and the plausible range. See Building your own BoilerBuddy.