MQTT API
Topic Scheme​
The thin-edge.io MQTT topic structure adopts a flexible and descriptive semantics, whilst keeping it consistent and predictable to enable other clients/mappers to interact with the thin-edge.io components.
Whilst the topic structure is flexible, the document will focus on the typical use case. Advanced use-cases can be viewed in the advanced section, but it is strongly encouraged to start out simple and only go to the advanced section if needed.
The MQTT topics are represented by three distinct groups; root, identifier and channel. Each group contains one or more segments.
The typical topic structure is visualized in the following diagram.
Where the groups are described as follows:
Group | Description |
---|---|
root | Base topic to group the identifier and channel under one common namespace |
identifier | A descriptor which represents which device/service the channel data is related to |
channel | Represents the information, such as telemetry data and commands, related to the identifier. Each channel type defines its own sub topic structure and corresponding payload format. |
The specifics of each group are detailed in the following sections.
Root​
The root/base topic prefix is used to group all data related to one thin-edge.io instance. The root topic is used to avoid conflicts with other clients communicating on the same MQTT broker.
The convention is to use a fixed value of te
(short for "thin-edge").
Identifier​
The identifier group is made up of four segments. Together these four segments reflect which device/service the channel information relates to. Such channel data includes telemetry data and commands.
The identifier can be visually represented by the following diagram:
Each segment is described as follows:
Segment | Description | Required |
---|---|---|
device | A literal string, "device", indicating that the information is related to a device | Yes |
<device_id> | id/name of the device. A value of main is used to represent the main device and any other value represents the device id of a child device. e.g. child01 , mycustomdevice | Yes |
service | A literal string, "service", indicating that the information is related to a service | No |
<service_id> | Service id/name which the information is related to | No |
An empty segment value is used to represent "not applicable". For instance publishing telemetry data of a device involves setting both the "service" and "service_id" segments to an empty string. See the identifier-example for more details.
Identifier Example​
To help better understand how the identifier is used in the topics, let's image the following setup. You have two devices; the main device and a single child device called "child01". Both the main device (where thin-edge.io is running), and the child device have a single service called "node_red". thin-edge.io will build digital twins for the devices (main and child device), and the "node_red" services associated with each of the devices.
The following diagram details the device hierarchy of the fictional setup.
The diagram shows that there are two devices; the "main" device (the digital twin representation of thin-edge.io), and a child device called "child01".
The following table shows how the identifier is used to represent the different combination of devices and services, e.g. main device, node_red service running on the main device, child device and the node_red service running on the child device.
Entity | Structure | Identifier topic prefix | |
---|---|---|---|
Main | |||
Device | te/device/main// | ||
Service | te/device/main/service/node_red | ||
Child ("child01") | |||
Device | te/device/child01// | ||
Service | te/device/child01/service/node_red |
Channel​
The channel group represents the information which is associated to the identifier. Information includes both telemetry data (e.g. measurement, events and alarms) as well as commands which are used to execute actions on the device.
The channel group is represented by the following segments:
The individual categories dictate the subsequent topic structure and payload schema, however the MQTT topic schema strives to keep some consistency amongst the different categories by applying similar concepts where possible.
Segment | Description |
---|---|
<category> | Category of data (telemetry or commands) which is related to the identifier. A fixed list of categories is used to represent data types such as; measurements, alarms, events, commands etc. |
<type> | A unique type/name used to identify the information being pushed or received. Types allow users to filter/subscribe to data which interests them. For example, a measurement could be published under a type called "flow_rate", then other clients interested in the flow rate can subscribe to that single typed topic. |
... | Additional channel specific topic segments. Each category is responsible for defining the number and meaning of the remaining topic segments. |
Categories​
The following is an overview of the channel categories which are available.
Category | Description |
---|---|
m | Measurements |
e | Events |
a | Alarms |
cmd | Commands |
twin | Entity twin metadata |
status | Service status |
Entity registration​
Since thin-edge.io doesn't enforce what each entity identification level means,
an explicit registration is required to register every entity that is going to send data or receive commands.
For example, before a measurement can be sent from a service named tedge-agent
from the device rpi1001
,
the entity named rpi1001
must be registered as a device
,
and tedge-agent
must be registered as a service
linked to that device.
An entity can be registered with thin-edge.io by publishing a retained message to the entity identification topic prefix with the entity type and other metadata that defines that entity. To model the example mentioned above, if an entity identification topic scheme like the following is used:
te/device/<device_id>/service/<service_id>
Examples​
Here are a few examples of how various entities can be registered.
Register a device​
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/main//' '{
"@type": "device",
"type": "Gateway"
}'
mosquitto_pub -r -t 'te/device/main//' -m '{
"@type": "device",
"type": "Gateway"
}'
te/device/main//
{
"@type": "device",
"type": "Gateway"
}
Or the device can be registered using an explicit id:
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/main//' '{
"@type": "device",
"@id": "tedge001",
"type": "Gateway"
}'
mosquitto_pub -r -t 'te/device/main//' -m '{
"@type": "device",
"@id": "tedge001",
"type": "Gateway"
}'
te/device/main//
{
"@type": "device",
"@id": "tedge001",
"type": "Gateway"
}
Register a service of the main device​
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/main/service/nodered' '{
"@type": "service",
"name": "nodered",
"type": "systemd"
}'
mosquitto_pub -r -t 'te/device/main/service/nodered' -m '{
"@type": "service",
"name": "nodered",
"type": "systemd"
}'
te/device/main/service/nodered
{
"@type": "service",
"name": "nodered",
"type": "systemd"
}
The service is implicitly linked to the parent derived from the topic, main
in this example.
But the parent can be explicitly provided as well with the @parent
key,
if the parent can not be derived from the topic directly:
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/component_namespace/service/nodered/instance-1' '{
"@type": "service",
"@parent": "device/main//",
"name": "nodered",
"type": "systemd"
}'
mosquitto_pub -r -t 'te/component_namespace/service/nodered/instance-1' -m '{
"@type": "service",
"@parent": "device/main//",
"name": "nodered",
"type": "systemd"
}'
te/component_namespace/service/nodered/instance-1
{
"@type": "service",
"@parent": "device/main//",
"name": "nodered",
"type": "systemd"
}
Register a child device​
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/child01//' '{
"@type": "child-device",
"name": "child01",
"type": "SmartHomeHub"
}'
mosquitto_pub -r -t 'te/device/child01//' -m '{
"@type": "child-device",
"name": "child01",
"type": "SmartHomeHub"
}'
te/device/child01//
{
"@type": "child-device",
"name": "child01",
"type": "SmartHomeHub"
}
If the @parent
info is not provided, it is assumed to be an immediate child of the main device.
Register a nested child device​
Nested child devices are registered in a similar fashion as an immediate child device, however the registration message requires the additional @parent
property to be set, indicating which parent the child device should be related to.
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/nested_child01//' '{
"@type": "child-device",
"@parent": "device/child01//",
"name": "nested_child01"
}'
mosquitto_pub -r -t 'te/device/nested_child01//' -m '{
"@type": "child-device",
"@parent": "device/child01//",
"name": "nested_child01"
}'
te/device/nested_child01//
{
"@type": "child-device",
"@parent": "device/child01//",
"name": "nested_child01"
}
Register a service of a child device​
Service registration for child devices also follow the same rules as the main device,
where the @parent
device info is derived from the topic itself, by default.
But, it is advised to declare it explicitly as follows:
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/child01/service/nodered' '{
"@type": "service",
"@parent": "device/child01//",
"name": "nodered",
"type": "systemd"
}'
mosquitto_pub -r -t 'te/device/child01/service/nodered' -m '{
"@type": "service",
"@parent": "device/child01//",
"name": "nodered",
"type": "systemd"
}'
te/device/child01/service/nodered
{
"@type": "service",
"@parent": "device/child01//",
"name": "nodered",
"type": "systemd"
}
A service is always owned by a device. The @parent
property in the registration message is used to declare the service's owner. The lifecycle of the service is tied to the device's lifecycle.
For example, a linux service runs on a device as it relies on physical hardware to run. A service can have its own telemetry data (e.g. tracking RAM usage of single process), however when the device ceases to exist, then so does the service.
Register a service of a nested child device​
- tedge
- mosquitto
- mqtt
tedge mqtt pub -r 'te/device/nested_child01/service/nodered' '{
"@type": "service",
"@parent": "device/nested_child01//",
"name": "nodered",
"type": "systemd"
}'
mosquitto_pub -r -t 'te/device/nested_child01/service/nodered' -m '{
"@type": "service",
"@parent": "device/nested_child01//",
"name": "nodered",
"type": "systemd"
}'
te/device/nested_child01/service/nodered
{
"@type": "service",
"@parent": "device/nested_child01//",
"name": "nodered",
"type": "systemd"
}