Skip to content

MQTT Access ​

MQTT Access connects existing firmware, a gateway, or a backend service to a selected device record that a Device Agent can control and query. The device side does not need to use the Device Agent SDK or rewrite existing drivers. Add an MQTT adapter that subscribes to commands, calls the existing device capability, returns execution results, and reports state and events according to the current native model.

Confirm the device protocol ​

MQTT access uses the topics, payloads, and authentication provided by the current IoT platform adapter. Devices already connected to that platform keep their existing path. Complete IoT Platform Integration to add Agent queries and control without changing the device protocol.

When adapting device software, open Existing Device for the selected device to obtain its protocol. Implement commands, desired-state updates, events, and response correlation as specified by that adapter. The model defines fields and constraints; the protocol defines delivery.

The fixed topics, JSON payloads, and MQTTX examples below apply to devices managed directly by Device Agent. For devices connected through an IoT platform, use the protocol shown in the console or generated package.

The examples use the DeviceSpec model example: the current_temperature and target_temperature properties, set_target_temperature(value) command, and over_temperature event with a current_temperature field. Use the exact fields declared by your own model when adapting them.

Get Connection Details ​

In the Device Agent workspace, create or select a device record, click its Download access package action, then choose Existing. The console shows the details for that selected record:

  • productId: the current Device Agent identifier.
  • namespace: the current runtime namespace. The default is default.
  • deviceId: the selected device record's unique identifier. The MQTT client must use this same value.
  • MQTT broker URL, username, and password status.
  • Command, response, telemetry, event, and time synchronization topics.
  • Payload examples for commands, responses, online status, state telemetry, and device events.
  • Commands, properties, and events from the current DeviceSpec.

If MQTT topic templates have been changed in configuration, use the topics shown in the console.

Choose the Adapter Location ​

Place the adapter where it can read state and execute actions most reliably:

  • Device firmware: the firmware already supports MQTT, so it subscribes to commands and reports state directly.
  • Edge gateway: the gateway talks to the real device through Modbus, BLE, serial, or a private protocol, then converts it to MQTT.
  • Backend service: an existing cloud or local service can already control the device, so an MQTT adapter process translates commands into existing API calls.

Regardless of where the adapter runs, the Device Agent uses the same MQTT contract.

Adaptation Steps ​

First align the DeviceSpec with existing device capabilities:

DeviceSpecExisting device mappingMQTT action
CommandsDriver calls, HTTP APIs, serial instructions, or business functionsSubscribe to commands and publish responses
PropertiesCurrent state, sensor readings, operating modePublish online status and state telemetry
EventsAlerts, failures, task completion, button callbacksPublish event messages

The minimal adapter loop is:

text
Startup:
  Connect to MQTT
  Subscribe to the command topic
  Publish online status and the current state snapshot

On command:
  Call the existing device capability with cmd and params
  Publish a command response with the same requestId
  If state changed, publish the latest state snapshot

On device state change:
  Publish state telemetry

On alert, failure, or task completion:
  Publish an event

Device Agent resolves productId and deviceId from the topic path. Keep metadata.productId in reports as well; it helps binding, debugging, and custom topic template setups. If MQTT Client ID must be bound to the device ID, enforce that in broker authentication or ACL rules.

Topics ​

The default topics are listed below. Copy the topics from the console when integrating.

DirectionPurposeDefault topic
Chat client publishesProduct-level chat requestdevice-agent/{productId}/in
Chat client subscribesProduct-level chat replydevice-agent/{productId}/out
Chat client publishesDevice-scoped chat requestdevice-agent/{productId}/device/{deviceId}/in
Chat client subscribesDevice-scoped chat replydevice-agent/{productId}/device/{deviceId}/out
Device subscribesReceive commandsdevice-agent/{productId}/device/{deviceId}/commands
Device publishesReturn command responsesdevice-agent/{productId}/device/{deviceId}/responses
Device publishesReport online status and telemetryv1/{productId}/{deviceId}/telemetry
Device publishesReport raw device eventsv1/{productId}/{deviceId}/event
Gateway publishesPublish normalized realtime eventsv1/{productId}/{deviceId}/event
Device publishesRequest time synchronizationdevice-agent/{productId}/device/{deviceId}/ntp/request
Device subscribesReceive time synchronization responsesdevice-agent/{productId}/device/{deviceId}/ntp/response

Regular Gateway MQTT publishes and subscriptions currently use QoS 0 with retain: false. Generated device SDKs use QoS 1, but the effective delivery level still depends on the publisher, broker, and Gateway subscription. Do not rely on end-to-end QoS 1.

Payload Rules ​

All payloads are UTF-8 JSON objects.

FieldDescription
tsProducer Unix timestamp in milliseconds. Reports in Platform deployments require ts or eventId; reuse the same value when retrying the same report.
eventIdOptional stable producer ID when no timestamp is available. Reuse it when retrying the same report.
metadataOptional object. Include at least productId, for example { "productId": "thermostat", "source": "existing-device" }.
dataReported data object. Status and property fields must come from the current DeviceSpec.

Platform deployments reject a producer ts older than the MQTT dedupe window before updating Device state or running Workflows. The production window is at least 48 hours and at least the MQTT session expiry plus 24 hours. Reports that use only eventId, such as an offline Will, start the same bounded receipt window when the platform admits them. Use a new ts or eventId for a new report and preserve it for retries. Timestamps in the JSON examples only illustrate the structure; replace them with the current Unix time in milliseconds before sending.

Current validation behavior is:

  • Command names and parameters must match command definitions.
  • type: "state" telemetry is validated strictly against the DeviceSpec. Unknown fields or invalid types do not update current state.
  • data.state in a type: "status" message is not currently validated strictly against the DeviceSpec. Report only defined properties with valid types to avoid writing invalid fields into current state.
  • Event names and fields in type: "event" messages are validated strictly. A failure produces a normalized validation_error event.

Online Status ​

After the device connects to MQTT, publish online status to the telemetry topic. After the first accepted report, the same device record shows as online and updates its current state.

json
{
  "type": "status",
  "data": {
    "status": "online",
    "state": {
      "current_temperature": 26.5,
      "target_temperature": 24
    }
  },
  "ts": 1710000000000,
  "metadata": {
    "productId": "thermostat",
    "source": "existing-device"
  }
}

Use online, offline, or error for status. Include a full state snapshot when possible. Before shutdown, publish offline; if the device supports MQTT Will Message, use the offline status payload as the will payload. Give each new MQTT connection cycle a fresh Will eventId and omit ts, because a timestamp set at connection time may have expired by disconnection. Keep that identity unchanged when the broker redelivers the same Will.

State Telemetry ​

Publish state changes to the telemetry topic. The usual type is state. Put only property fields defined in the DeviceSpec into data.

json
{
  "type": "state",
  "data": {
    "current_temperature": 27.1,
    "target_temperature": 24
  },
  "ts": 1710000005000,
  "metadata": {
    "productId": "thermostat",
    "source": "existing-device"
  }
}

Report a full snapshot after startup, after command execution, and after important state changes. If a device reports only partial fields, the console can only update those fields.

Command Responses ​

When a user controls a device through conversation, the Device Agent publishes a command to the command topic.

json
{
  "cmd": "set_target_temperature",
  "params": {
    "value": 24
  },
  "requestId": "req-001",
  "ts": 1710000010000
}
FieldTypeRequiredDescription
cmdstringYesCommand name defined in the DeviceSpec.
paramsobjectNoCommand parameters. They must match the command parameter definition. Use {} or omit it when the command has no parameters.
requestIdstringYesRequest ID for this command.
tsnumberNoUnix timestamp in milliseconds.

This example uses the set_target_temperature(value) definition in Define a Device Agent. Copy the model's parameter name exactly; the reported property target_temperature is a separate field.

After execution, publish the result to the response topic. requestId must match the received command.

json
{
  "code": 0,
  "msg": "ok",
  "requestId": "req-001",
  "data": {
    "target_temperature": 24
  },
  "ts": 1710000011000,
  "metadata": {
    "productId": "thermostat",
    "source": "existing-device"
  }
}
FieldTypeRequiredDescription
codenumberYes0 means success. Non-zero values mean failure.
msgstringYesExecution result message.
requestIdstringYesMust equal the command requestId.
dataanyNoExecution result returned to the Device Agent.
tsnumberNoUnix timestamp in milliseconds.

If a command changes device state, publish the latest status or state immediately after the response so the status panel reflects the result.

Device Events ​

Events are one-time device-originated changes, such as alerts, failures, threshold triggers, task completion, or button presses. Publish type: "event" to the event topic.

json
{
  "type": "event",
  "data": {
    "event": "over_temperature",
    "current_temperature": 38.5
  },
  "ts": 1710000020000,
  "metadata": {
    "productId": "thermostat",
    "source": "existing-device"
  }
}

data.event must be an event name defined in the DeviceSpec. Other fields must be output fields defined by that event. Events do not automatically update current data, but they appear in recent events. For threshold events, use edge triggering or a cooldown to avoid duplicate reports.

After processing a device report, the Gateway publishes a normalized realtime event to the same event topic. Device-originated messages use type and data; normalized messages use eventId, kind, namespace, deviceId, occurredAt, and summary. Subscribers must distinguish the two shapes and must not report normalized messages as device events again.

Chat over MQTT ​

Publish requests to the product-level input topic. Use the input topic with deviceId when the chat must target one device. Receive replies from the matching output topic.

json
{
  "id": "req-001",
  "sessionId": "terminal-001",
  "prompt": "Set the target temperature to 24 degrees",
  "metadata": {
    "source": "mqtt-client"
  }
}

Use a unique id for each request. Reuse sessionId to continue the conversation.

One chat turn produces multiple messages. Progress messages have an empty top-level text and carry progress in metadata.status:

metadata.status.typeKey fields
start, thinkingCurrent execution stage.
text_blockIncremental text in metadata.status.text.
tool_start, tool_endTool name, input, output, or error.
complete, errorCurrent execution result; completion text is in metadata.status.text.

For status messages, metadata.replyTo matches the request id. The Gateway also sends a final reply in top-level text:

json
{
  "sessionId": "terminal-001",
  "text": "The target temperature is now 24 degrees.",
  "metadata": {
    "timestamp": "2026-05-11T10:00:00.000Z"
  },
  "timestamp": "2026-05-11T10:00:00.000Z"
}

Consume either incremental status or the final reply instead of appending both. The final reply does not currently guarantee replyTo. Send the next request on a shared sessionId only after the current turn completes. Use different session IDs for parallel conversations.

To interrupt or clear a session, publish the same sessionId to the original input topic with an empty prompt and set metadata.interrupt: true or metadata.clearSession: true. Clear archives the current persisted conversation before resetting its context; subsequent requests can keep the same sessionId. If archiving fails or a turn is active, Clear returns an error and does not reset the conversation. The acknowledgement uses metadata.status.type: complete on success or error on failure; metadata.replyTo matches the request id.

Test with MQTTX ​

Before adapting a real device, you can use MQTTX CLI to simulate one device. The examples below assume:

  • MQTT broker: 127.0.0.1:1883
  • productId: thermostat
  • deviceId: thermostat-001

If the MQTT broker does not require username and password, remove -u and -P.

First, open one terminal and subscribe to the command topic. This lets you see commands sent by the Device Agent:

Replace the example eventId for each new test report; retain it when retrying the same report. These commands use the MQTT protocol described above and require the same fields in the model.

bash
mqttx sub \
  -h 127.0.0.1 \
  -p 1883 \
  -u your-username \
  -P 'your-password' \
  -q 0 \
  -t 'device-agent/thermostat/device/thermostat-001/commands'

Then use another terminal to simulate the device coming online:

bash
mqttx pub \
  -h 127.0.0.1 \
  -p 1883 \
  -u your-username \
  -P 'your-password' \
  -q 0 \
  -t 'v1/thermostat/thermostat-001/telemetry' \
  -m '{"type":"status","data":{"status":"online","state":{"current_temperature":26.5,"target_temperature":24}},"metadata":{"productId":"thermostat","source":"mqttx"},"eventId":"mqttx-online-001"}'

After the existing thermostat-001 record shows as online, simulate a state update:

bash
mqttx pub \
  -h 127.0.0.1 \
  -p 1883 \
  -u your-username \
  -P 'your-password' \
  -q 0 \
  -t 'v1/thermostat/thermostat-001/telemetry' \
  -m '{"type":"state","data":{"current_temperature":27.1,"target_temperature":24},"metadata":{"productId":"thermostat","source":"mqttx"},"eventId":"mqttx-state-001"}'

You can also simulate a device-originated event:

bash
mqttx pub \
  -h 127.0.0.1 \
  -p 1883 \
  -u your-username \
  -P 'your-password' \
  -q 0 \
  -t 'v1/thermostat/thermostat-001/event' \
  -m '{"type":"event","data":{"event":"over_temperature","current_temperature":38.5},"metadata":{"productId":"thermostat","source":"mqttx"},"eventId":"mqttx-event-001"}'

Finally, ask the Device Agent to control this device in the conversation. The subscription terminal receives a command with requestId. Copy that requestId into the response payload:

bash
mqttx pub \
  -h 127.0.0.1 \
  -p 1883 \
  -u your-username \
  -P 'your-password' \
  -q 0 \
  -t 'device-agent/thermostat/device/thermostat-001/responses' \
  -m '{"code":0,"msg":"ok","requestId":"replace-with-received-requestId","data":{"target_temperature":24},"metadata":{"productId":"thermostat","source":"mqttx"},"eventId":"mqttx-response-001"}'

These commands validate the full path: device online status, telemetry reporting, event reporting, command reception, and command response. For a real device, replace the MQTTX commands with MQTT publish and subscribe logic in firmware, a gateway, or a backend service.

Time Synchronization ​

If the device needs to estimate local clock drift from Agent Gateway, use the time synchronization topics.

Publish this payload to the request topic:

json
{
  "deviceSendTime": 1710000030000
}

Receive this payload from the response topic:

json
{
  "deviceSendTime": 1710000030000,
  "serverRecvTime": 1710000030120,
  "serverSendTime": 1710000030122
}

deviceSendTime is the device's local Unix timestamp in milliseconds when it sends the request. serverRecvTime and serverSendTime are Agent Gateway timestamps for receiving the request and sending the response.

Verify Access ​

After adapting the device side, check the workspace in this order:

  1. The device is connected to MQTT and has subscribed to the command topic.
  2. After the telemetry topic receives the first status or state report, the same device record shows as online.
  3. Current state shows property fields defined in the DeviceSpec.
  4. A conversation control request reaches the device, and the device publishes a response with the same requestId.
  5. After a command changes state, the device publishes the latest status or telemetry.
  6. Device-originated events appear in recent events.

If the record does not come online, check the MQTT broker and credentials first, then verify productId, deviceId, topic paths, and metadata.productId. If it is online but current state does not update, check whether property field names and types match the DeviceSpec. See Configuration for runtime settings.

Next Steps ​

  • Read SDK Access to connect real hardware with a generated device-side project.
  • Read Device Simulator to validate the DeviceSpec without real hardware.
  • Read Use a Device Agent to select devices and control them through conversation.