UMEC MQTT v3 — normative reference
This document fixes only fields and limits confirmed by the current bridge contract and JSON Schema. A field not described here is not a public contract.
JSON-RPC envelope
| Field | Type | Required | Rule |
|---|---|---|---|
jsonrpc | string | yes | exactly "2.0" |
id | number | yes | correlation ID; command ID is deduplicated for 300 seconds per tenant/deviceId |
method | string | yes | inventory, state, config, command, ack |
params | object | yes | contains at least timestamp |
params.timestamp | integer | yes | Unix epoch milliseconds |
params.device | object | no | device section of a message |
params.modules | array | no | module sections of a message |
The top-level object does not allow extra fields. Schema: umec-mqtt-v3.schema.json.
Methods and direction
| Method | Direction | Payload policy |
|---|---|---|
inventory | D2P | identity/schema description; it does not confirm measurement freshness |
state | D2P | measured state, delta or full snapshot |
config | P2D | configuration/control prepared by the platform |
command | P2D | writable parameter or action command |
ack | P2D | acknowledgement/result flow |
Every flow uses QoS 1 and retain=false. An MQTT message is at most 1 MiB. The sum of sensors, parameters and errors in device and modules is at most 1000.
State
{
"jsonrpc": "2.0",
"id": 1001,
"method": "state",
"params": {
"timestamp": 1760000000000,
"device": {
"id": "<deviceId>",
"sensors": [{"id": "temperature", "value": 21.6}],
"parameters": [{"id": "target", "value": 22}],
"errors": []
},
"modules": []
}
}
This is a conformance shape: replace values and codes with the specific device schema. The bridge keeps observed state distinct from inventory; offline/unavailable state must not fabricate a source timestamp or a history datapoint.
Inventory
{
"jsonrpc": "2.0",
"id": 1002,
"method": "inventory",
"params": {"timestamp": 1760000000000, "device": {"id": "<deviceId>"}, "modules": []}
}
Inventory reconciles identity/schema. It is not a replacement for state and does not make a controller or sensor fresh.
Config, command and ACK
P2D config/commands keep device controls at the top level of params; do not use legacy params.device for those messages. Every writable parameter, module parameter and action command is a separate request with an independent numeric ID.
{
"jsonrpc": "2.0",
"id": 2001,
"method": "command",
"params": {"timestamp": 1760000000000}
}
The successful admission response has shape { "accepted": 1 }. applied needs a matching state echo. If a command is invalid, ACL-denied or cannot be applied, use a JSON-RPC error rather than a success object.
ACL
| Principal | Permitted | Prohibited |
|---|---|---|
| device | write only <tenant>/<deviceId>/d2p; read only <tenant>/<deviceId>/p2d | another device, reverse direction, wildcard |
| ingest | read only its D2P topic | P2D and publish |
| publisher/server | write only its P2D topic | D2P and subscribe |
An ACL rule contains neither + nor #, has exactly three topic segments and its tenant/deviceId match its principal scope.
Retry and timeout
Timeout and retry must be bounded by the device policy. Persist id, sent time and terminal result. Retry only an idempotent command with the same ID; escalate a command with an unknown effect. Do not log payload values, passwords, tokens or private keys.
Release verification
- Validate every JSON object against the schema.
- Check topic, QoS 1, retain false and payload ≤1 MiB.
- Check actual device ACL without wildcard.
- Exercise D2P inventory/state, P2D command and matching state echo in staging.
- Rotate credentials via the provisioning flow; revoke old credentials before publishing the new pair.