MQTT
Broker
| Environment | URL |
|---|---|
| Staging | mqtts://mqtt.linter-iot-stg.icent.digital:8883 |
| Local development | mqtts://localhost:8883 |
| Self-hosted Docker stack | mqtts://<STACK_HOST>:<MQTT_PORT> (default port 8883) |
The connection uses mutual TLS: a private CA + one X.509 certificate per device. Linter has the connection bundle (CA, client certificate and key, plus a sample Python script).
Topics
| Topic | Direction | Use |
|---|---|---|
telemetry/<partNumber>/<deviceId> | device → platform | Readings |
commands/<deviceId> | platform → device | Commands sent from the platform |
commands/<deviceId>/ack | device → platform | The device’s confirmation of each command |
partNumber— serial of the model (device-type catalog). If it is not registered in the catalog, readings are stored but cannot be displayed in a typed way.deviceId— MAC, lowercase, without separators (a4cf12bb73d2).
Use QoS 1 in both directions, with a fixed client id and a persistent session (clean session off): the broker then queues messages while either side is briefly offline instead of dropping them.
Telemetry payload
Flat JSON with the device’s metrics; fields are free-form per type:
{ "level_pct": 62, "consumption_l": 12.4 }Every message is persisted in telemetry_raw with receivedAt and triggers
two processes:
- Alert engine — evaluates the type’s threshold rules (defined by ROOT
in the catalog). Breaching one creates an
ACTIVEalert; returning to range resolves it automatically. - Notifications — if there are email rules for
alert.raised, they are queued and sent.
Commands
A device that accepts commands subscribes to commands/<deviceId> and
answers each one on commands/<deviceId>/ack:
{ "commandId": "6718a0c2e4b0a1b2c3d4e5f6", "status": "ok" }The packet is exactly the one configured on the platform (JSON or text), published with QoS 1 and without retain. Device commands covers templates, statuses, deduplication and the topic ACL recommended for production.
HTTP alternative
For devices without MQTT capability (e.g. one-off integrations or tests), the ingestion service accepts the same reading over HTTP:
curl -X POST https://ingest.linter-iot-stg.icent.digital/v1/telemetry/<partNumber>/<deviceId> \ -H "Content-Type: application/json" \ -d '{ "pressure": 5.2, "hours": 1420 }'It returns 202 and persists the reading exactly like the MQTT path.
Commands are only delivered over MQTT. The recommended channel for devices in
the field is still MQTT over mutual TLS.
Connecting a new device
- ROOT registers the model in the catalog (part number → type) if it does not exist yet.
- Optionally, the device is registered at the factory when its label is printed, under its subsidiary (and distributor or client, if known); see factory registration.
- The device publishes on its topic with its certificate and, if it accepts commands, subscribes to its command topic.
- Readings show up in Telemetry (ROOT) immediately, marked Unregistered, Registered (the device exists, without a client) or Bound. The device becomes operational for the client once the binding is completed.