MQTT ingest
Topics, payload, QoS, last-will
OpenSense exposes a multi-tenant MQTT 5 bridge for customers who run their own broker on-premises. See the DIY ESP32 + MQTT page for the device-side guide. This page is the topic / payload / QoS contract.
Bridge endpoint
mqtts://mqtt.opensense.murzin.digital:8883
- TLS 1.2 or 1.3, mTLS required.
- Cert + key + CA bundle is downloaded from the dashboard when you add an MQTT bridge.
- MQTT 5 only. MQTT 3.1.1 is rejected at handshake.
Topics
Each account has a dedicated topic root: opensense/<account_id>/.
Publish topics (your broker → our broker):
| Topic | Purpose |
|---|---|
opensense/<acc>/<device_id> | Measurement payload (JSON) |
opensense/<acc>/<device_id>/lwt | Last-will / online-status payload |
opensense/<acc>/<device_id>/info | Firmware version, model (one-shot) |
Subscribe topics (our broker → your broker, optional):
| Topic | Purpose |
|---|---|
opensense/<acc>/<device_id>/cmd/cadence | Server-side request to change cadence |
opensense/<acc>/<device_id>/cmd/identify | "Make the LED blink" — for finding it |
The cmd/* topics are placeholders today; only cmd/identify is
implemented. We may extend later.
Measurement payload
Same as POST /v1/ingest:
{
"ts": "2026-05-17T08:22:00Z",
"measurements": [
{ "type": "temperature", "value": 4.2 },
{ "type": "humidity", "value": 64.1 }
]
}
device field is omitted — the device id is the second path segment
of the topic. ts is optional; if omitted, server-receive time is used.
QoS
| Topic | Required QoS | Retain | Why |
|---|---|---|---|
| Measurement | 1 | false | Stream, not state |
| LWT (last-will) | 1 | true | Survive broker restarts |
| Info | 1 | true | One-shot retained presence |
| Cmd (downlink) | 1 | false | Single-shot |
QoS 0 is rejected (silent drop) for measurement topics — the bridge will not let you race the network for free. QoS 2 is accepted but downgraded to 1 at the ingest point; the storage layer dedups by content anyway.
Last-will format
{
"status": "offline",
"reason": "device-disconnected",
"at": "2026-05-17T08:22:00Z"
}
Set as the LWT of your device's MQTT client when connecting to your local broker. Forward via bridge with retain=true. When the local broker promotes the LWT (TCP close on the device), it propagates through the bridge and OpenSense flips the device's connection state.
On reconnect, the device publishes:
{ "status": "online", "at": "2026-05-17T08:23:00Z" }
…and the connection state flips back.
Authentication
mTLS only. The client cert subject CN identifies the account; the topic prefix is enforced on the bridge side against that subject. A client cert for account A cannot publish to account B's topic tree even if it tries.
There is no password-based auth on the MQTT bridge. If you cannot do mTLS, use HTTPS ingest instead — we are not going to weaken MQTT multi-tenancy to ease setup.
Why bridge, not direct device-to-OpenSense MQTT?
Direct device connections are a Team-tier feature. Solo is priced for HTTP-first integrations and the on-prem MQTT bridge is the workaround for customers who cannot do outbound HTTPS but can do MQTT.
For scale (>100 devices), revisit: at that scale you want direct sessions on our broker, which means individual device certificates, which means provisioning workflow — none of which is free.