n | ## Table of Contents | n | |
| | | |
| * [Motivation](#motivation) | | |
| * [MQTT restrictions](#mqtt-restrictions) | | |
| * [Topic IDs](#topic-ids) | | |
| * [Payload](#payload) | | |
| * [QoS and retained messages](#qos-and-retained-messages) | | |
| * [Topology](#topology) | | |
| * [Base topic](#base-topic) | | |
| * [Devices](#devices) | | |
| * [Device attributes](#device-attributes) | | |
| * [Device behavior](#device-behavior) | | |
| * [Device statistics](#device-statistics) | | |
| * [Nodes](#nodes) | | |
| * [Node attributes](#node-attributes) | | |
| * [Properties](#properties) | | |
| * [Property attributes](#property-attributes) | | |
| * [Arrays](#arrays) | | |
| * [Broadcast channel](#broadcast-channel) | | |
| * [FAQ and Rationale](#faq) | | |
| | | |
| | | |
| ## Motivation | | |
| | | |
| The Homie convention strives to be a **communication definition on top of MQTT** between IoT devices and controlling entities. | | |
| | | |
| > [MQTT](http://mqtt.org) is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. | | |
| > It was designed as an extremely lightweight publish/subscribe messaging transport. | | |
| | | |
| MQTT supports easy and unrestricted message-based communication. | | |
| However, MQTT doesn't define the structure and content of these messages and their relation. | | |
| An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device. | | |
| | | |
| The Homie convention defines a **standardized way** of how IoT devices and services announce themselves and their data on the communication channel. | | |
| The Homie convention is thereby a crucial aspect in the support of **automatic discovery, configuration and usage** of devices and services over the MQTT protocol. | | |
| | | |
| ---- | | |
| | | |
n | Every MQTT message payload MUST be sent as string. | n | - Every MQTT message payload MUST be sent as a UTF-8 encoded string |
| If a value is of a numeric data type, it MUST be converted to string. | | - The value published as payload MUST be valid for the respective property/attribute type as per the list below |
| Booleans MUST be converted to "true" or "false". | | |
| All values MUST be encoded as UTF-8 strings. | | #### String |
| | | |
| | | - String types are limited to 268,435,456 characters |
| | | - An empty string ("") is a valid payload |
| | | |
| | | #### Integer |
| | | |
| | | - Integer types are UTF-8 encoded string literal representations of 64-bit signed whole numbers |
| | | - Integers range from -9,223,372,036,854,775,808 (-2<sup>63</sup>) to 9,223,372,036,854,775,807 (2<sup>63</sup>-1) |
| | | - The payload may only contain whole numbers and the negation character "-". No other characters including spaces (" ") are permitted |
| | | - A string with just a negation sign ("-") is not a valid payload |
| | | - An empty string ("") is not a valid payload |
| | | |
| | | #### Float |
| | | |
| | | - Float types are UTF-8 encoded string literal representations of 64-bit signed floating point numbers |
| | | - Floats range from 2<sup>-1074</sup> to (2-2<sup>-52</sup>)*2<sup>1023</sup> |
| | | - The payload may only contain whole numbers, the negation character "-", the exponent character "e" or "E" and the decimal separator ".", no other characters, including spaces (" ") are permitted |
| | | - The dot character (".") is the decimal separator (used if necessary) and may only have a single instance present in the payload |
| | | - Representations of numeric concepts such as "NaN" (Not a Number) and "Infinity" are not a valid payload |
| | | - A string with just a negation sign ("-") is not a valid payload |
| | | - An empty string ("") is not a valid payload |
| | | |
| | | #### Boolean |
| | | |
| | | - Booleans must be converted to the string literals "true" or "false" |
| | | - Representation is case sensitive, e.g. "TRUE" or "FALSE" are not valid payloads. |
| | | - An empty string ("") is not a valid payload |
| | | |
| | | #### Enum |
| | | |
| | | - Enum payloads must be one of the values specified in the format definition of the property |
| | | - Enum payloads are case sensitive, e.g. "Car" will not match a format definition of "car" |
| | | - Payloads should have leading and trailing whitespace removed |
| | | - An empty string ("") is not a valid payload |
| | | |
| | | #### Color |
| | | |
| | | - Color payload validity varies depending on the property format definition of either "rgb" or "hsv" |
| | | - Both payload types contain comma separated whole numbers of differing restricted ranges |
| | | - The encoded string may only contain whole numbers and the comma character ",", no other characters are permitted, including spaces (" ") |
| | | - Payloads for type "rgb" contains 3 comma separated values of numbers with a valid range between 0 and 255. e.g. 100,100,100 |
| | | - Payloads for type "hsv" contains 3 comma separated values of numbers. The first number has a range of 0 to 360, the second and third numbers have a range of 0 to 100. e.g. 300,50,75 |
| | | - An empty string ("") is not a valid payload |
| | | |
n | Properties can be **settable**. | n | |
| For example, you don't want your `temperature` property to be settable in case of a temperature sensor (like the car example), but to be settable in case of a thermostat. | | |
| | | |
| Properties can be **retained**. | | |
| A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed). | | |
| | | |
| A combination of those flags compiles into this list: | | |
| | | |
| * **retained + non-settable**: The node publishes a property state (temperature sensor) | | |
| * **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power) | | |
| * **non-retained + non-settable**: The node publishes momentary events (door bell pressed) | | |
| * **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee) | | |
| | | |
n | <table> | n | The following device attributes are mandatory and MUST be send, even if it is just an empty string. |
| <tr> | | |
| <th>Topic</th> | | |
| <th>Direction</th> | | |
| <th>Description</th> | | |
| <th>Retained</th> | | |
| <th>Required</th> | | |
| </tr> | | |
| <tr> | | |
| <td>$homie</td> | | |
| <td>Device → Controller</td> | | |
| <td>Version of the Homie convention the device conforms to</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$name</td> | | |
| <td>Device → Controller</td> | | |
| <td>Friendly name of the device</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$state</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| See <a href="#device-behavior">Device behavior</a> | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$localip</td> | | |
| <td>Device → Controller</td> | | |
| <td>IP of the device on the local network</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$mac</td> | | |
| <td>Device → Controller</td> | | |
| <td>Mac address of the device network interface. The format MUST be of the type <code>A1:B2:C3:D4:E5:F6</code></td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$fw/name</td> | | |
| <td>Device → Controller</td> | | |
| <td>Name of the firmware running on the device. Allowed characters are the same as the device ID</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$fw/version</td> | | |
| <td>Device → Controller</td> | | |
| <td>Version of the firmware running on the device</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$nodes</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| Nodes the device exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes. | | |
| To make a node an array, append <code>[]</code> to the ID. | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$implementation</td> | | |
| <td>Device → Controller</td> | | |
| <td>An identifier for the Homie implementation (example <code>esp8266</code>)</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$implementation/#</td> | | |
| <td>Controller → Device or Device → Controller</td> | | |
| <td>You can use any subtopics of <code>$implementation</code> for anything related to your specific Homie implementation.</td> | | |
| <td>Yes or No, depending of your implementation</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats</td> | | |
| <td>Device → Controller</td> | | |
| <td>Specify all optional stats that the device will announce, with format <code>stats</code> separated by a <code>,</code> if there are multiple stats. See next section for an example</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/interval</td> | | |
| <td>Device → Controller</td> | | |
| <td>Interval in seconds at which the device refreshes its <code>$stats/+</code>: See next section for details about statistical attributes</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| </table> | | |
n | | n | | Topic | Description | |
| | | |-------------|--------------------------------------------------------------------------:| |
| | | | $homie | The implemented Homie convention version | |
| | | | $name | Friendly name of the device | |
| | | | $state | See [Device Lifecycle](#device-lifecycle) | |
| | | | $nodes | [Nodes](#nodes) the device exposes, separated by `,` for multiple ones. | |
| | | | $extensions | Supported extensions, separated by `,` for multiple ones. | |
| | | |
| | | Optional topics include: |
| | | |
| | | | Topic | Description | |
| | | |-----------------|-------------------------------| |
| | | | $implementation | An identifier for the Homie implementation (example "esp8266") | |
| | | |
| For example, a device with an ID of `super-car` that comprises off a `wheels`, `engine` and a `lights` node would send: | | For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine` and a `lights` node would send: |
n | #### Device Statistics | n | |
| | | |
| * `homie` / `device ID` / `$stats`/ **`$device-statistic-attribute`**: | | |
| The `$stats/` hierarchy allows to send device attributes that change over time. The device MUST send them every `$stats/interval` seconds. | | |
| | | |
| <table> | | |
| <tr> | | |
| <th>Topic</th> | | |
| <th>Direction</th> | | |
| <th>Description</th> | | |
| <th>Retained</th> | | |
| <th>Required</th> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/uptime</td> | | |
| <td>Device → Controller</td> | | |
| <td>Time elapsed in seconds since the boot of the device</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/signal</td> | | |
| <td>Device → Controller</td> | | |
| <td>Signal strength in %</td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/cputemp</td> | | |
| <td>Device → Controller</td> | | |
| <td>CPU Temperature in °C</td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/cpuload</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| CPU Load in %. | | |
| Average of last <code>$interval</code> including all CPUs | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/battery</td> | | |
| <td>Device → Controller</td> | | |
| <td>Battery level in %</td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/freeheap</td> | | |
| <td>Device → Controller</td> | | |
| <td>Free heap in bytes</td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$stats/supply</td> | | |
| <td>Device → Controller</td> | | |
| <td>Supply Voltage in V</td> | | |
| <td>Yes</td> | | |
| <td>No</td> | | |
| </tr> | | |
| </table> | | |
| | | |
| For example, our `super-car` device with `$stats/interval` value "60" is supposed to send its current values every 60 seconds: | | |
| | | |
| ```java | | |
| homie/super-car/$stats → "uptime,cputemp,signal,battery" | | |
| homie/super-car/$stats/uptime → "120" | | |
| homie/super-car/$stats/cputemp → "48" | | |
| homie/super-car/$stats/signal → "24" | | |
| homie/super-car/$stats/battery → "80" | | |
| ``` | | |
| | | |
| ---- | | |
| | | |
n | <table> | n | All listed attributes are **required**. A node attribute MUST be one of these: |
| <tr> | | |
| <th>Topic</th> | | | Topic | Description | |
| <th>Direction</th> | | |-------------|-------------------------------------------------------------------------------------------| |
| <th>Description</th> | | | $name | Friendly name of the Node | |
| <th>Retained</th> | | | $type | Type of the node | |
| <th>Required</th> | | | $properties | Exposed properties, separated by `,` for multiple ones. | |
| </tr> | | |
| <tr> | | |
| <td>$name</td> | | |
| <td>Device → Controller</td> | | |
| <td>Friendly name of the Node</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$type</td> | | |
| <td>Device → Controller</td> | | |
| <td>Type of the node</td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$properties</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| Properties the node exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes. | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>Yes</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$array</td> | | |
| <td>Device → Controller</td> | | |
| <td>Range separated by a <code>-</code>. e.g. <code>0-2</code> for an array with the indexes <code>0</code>, <code>1</code> and <code>2</code></td> | | |
| <td>Yes</td> | | |
| <td>Yes, if the node is an array</td> | | |
| </tr> | | |
| </table> | | |
n | | n | |
| | | * Properties can be **settable**. |
| | | For example, you don't want your `temperature` property to be settable in case of a temperature sensor |
| | | (like the car example), but to be settable in case of a thermostat. |
| | | |
| | | * Properties can be **retained**. |
| | | A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed). |
| | | |
| | | A combination of those flags compiles into this list: |
| | | |
| | | * **retained + non-settable**: The node publishes a property state (temperature sensor) |
| | | * **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power) |
| | | * **non-retained + non-settable**: The node publishes momentary events (door bell pressed) |
| | | * **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee) |
| | | |
n | <table> | n | The following attributes are required: |
| <tr> | | |
| <th>Topic</th> | | | Topic | Description | Payload type | |
| <th>Direction</th> | | |-----------|------------------------------------------------------|---------------------------------------------| |
| <th>Description</th> | | | $name | Friendly name of the property. | String | |
| <th>Valid values</th> | | | $datatype | The data type. See [Payloads](#payloads). | Enum: \[integer, float, boolean,string, enum, color\] | |
| <th>Retained</th> | | |
| <th>Required (Default)</th> | | The following attributes are optional: |
| </tr> | | |
| <tr> | | | Topic | Description | Payload type | |
| <td>$name</td> | | |-----------|------------------------------------------------------|---------------------------------------------| |
| <td>Device → Controller</td> | | | $format | Specifies restrictions or options for the given data type | See below | |
| <td>Friendly name of the property.</td> | | | $settable | Settable (<code>true</code>). Default is read-only (<code>false</code>) | Boolean | |
| <td>Any String</td> | | | $retained | Non-retained (<code>false</code>). Default is Retained (<code>true</code>). | Boolean | |
| <td>Yes</td> | | | $unit | Optional unit of this property. See list below. | String | |
| <td>No ("")</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$settable</td> | | |
| <td>Device → Controller</td> | | |
| <td>Specifies whether the property is settable (<code>true</code>) or readonly (<code>false</code>)</td> | | |
| <td><code>true</code> or <code>false</code></td> | | |
| <td>Yes</td> | | |
| <td>No (<code>false</code>)</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$retained</td> | | |
| <td>Device → Controller</td> | | |
| <td>Specifies whether the property is retained (<code>true</code>) or non-retained (<code>false</code>). Publishing to a non-retained property topic MUST always happen with the MQTT 'retain' flag off.</td> | | |
| <td><code>true</code> or <code>false</code></td> | | |
| <td>Yes</td> | | |
| <td>No (<code>true</code>)</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$unit</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| A string containing the unit of this property. | | |
| You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer. | | |
| </td> | | |
| <td> | | |
| Recommended:<br> | | |
| <code>°C</code> Degree Celsius<br> | | |
| <code>°F</code> Degree Fahrenheit<br> | | |
| <code>°</code> Degree<br> | | |
| <code>L</code> Liter<br> | | |
| <code>gal</code> Galon<br> | | |
| <code>V</code> Volts<br> | | |
| <code>W</code> Watt<br> | | |
| <code>A</code> Ampere<br> | | |
| <code>%</code> Percent<br> | | |
| <code>m</code> Meter<br> | | |
| <code>ft</code> Feet<br> | | |
| <code>Pa</code> Pascal<br> | | |
| <code>psi</code> PSI<br> | | |
| <code>#</code> Count or Amount | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>No ("")</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$datatype</td> | | |
| <td>Device → Controller</td> | | |
| <td>Describes the format of data.</td> | | |
| <td> | | |
| <code>integer</code>, | | |
| <code>float</code>, | | |
| <code>boolean</code>, | | |
| <code>string</code>, | | |
| <code>enum</code>, | | |
| <code>color</code> | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>No (<code>string</code>)</td> | | |
| </tr> | | |
| <tr> | | |
| <td>$format</td> | | |
| <td>Device → Controller</td> | | |
| <td> | | |
| Describes what are valid values for this property. | | |
| </td> | | |
| <td> | | |
| <ul> | | |
| <li> | | |
| <code>from:to</code> Describes a range of values e.g. <code>10:15</code>. | | |
| <br>Valid for datatypes <code>integer</code>, <code>float</code> | | |
| </li> | | |
| <li> | | |
| <code>value,value,value</code> for enumerating all valid values. | | |
| Escape <code>,</code> by using <code>,,</code>. e.g. <code>A,B,C</code> or <code>ON,OFF,PAUSE</code>. | | |
| <br>Valid for datatypes <code>enum</code> | | |
| </li> | | |
| <li> | | |
| <code>rgb</code> to provide colors in RGB format e.g. <code>255,255,0</code> for yellow. | | |
| <code>hsv</code> to provide colors in HSV format e.g. <code>60,100,100</code> for yellow. | | |
| <br>Valid for datatype <code>color</code> | | |
| </li> | | |
| </ul> | | |
| </td> | | |
| <td>Yes</td> | | |
| <td>No for $datatype <code>string</code>,<code>integer</code>,<code>float</code>,<code>boolean</code>. Yes for <code>enum</code>,<code>color</code></td> | | |
| </tr> | | |
| </table> | | |
n | | n | Format: |
| | | |
| | | * For `integer` and `float`: Describes a range of payloads e.g. `10:15` |
| | | * For `enum`: `payload,payload,payload` for enumerating all valid payloads. |
| | | * For `color`: |
| | | - `rgb` to provide colors in RGB format e.g. `255,255,0` for yellow. |
| | | - `hsv` to provide colors in HSV format e.g. `60,100,100` for yellow. |
| | | |
| | | Recommended unit strings: |
| | | |
| | | * `°C`: Degree Celsius |
| | | * `°F`: Degree Fahrenheit |
| | | * `°`: Degree |
| | | * `L`: Liter |
| | | * `gal`: Galon |
| | | * `V`: Volts |
| | | * `W`: Watt |
| | | * `A`: Ampere |
| | | * `%`: Percent |
| | | * `m`: Meter |
| | | * `ft`: Feet |
| | | * `Pa`: Pascal |
| | | * `psi`: PSI |
| | | * `#`: Count or Amount |
| | | |
| | | You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer. |
| | | |
| | | #### Property command topic |
| | | |
| * `homie` / `device ID` / `node ID` / `property ID` / **`set`**: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators. | | * `homie` / `device ID` / `node ID` / `property ID` / **`set`**: The device must subscribe to this topic if the property is **settable** (in case of actuators for example). |
n | ### Arrays | n | |
| | | |
| A node can be an array if you've added `[]` to its ID in the `$nodes` device attribute, and if its `$array` attribute is set to the range of the array. | | |
| Let's consider we want to control independently the front lights and back lights of our `super-car`. Our `lights` node array would look like this. Note that the topic for an element of the array node is the name of the node followed by a `_` and the index getting updated: | | |
| | | |
| ```java | | |
| homie/super-car/$nodes → "lights[]" | | |
| | | |
| homie/super-car/lights/$name → "Lights" | | |
| homie/super-car/lights/$properties → "intensity" | | |
| homie/super-car/lights/$array → "0-1" | | |
| | | |
| homie/super-car/lights/intensity/$name → "Intensity" | | |
| homie/super-car/lights/intensity/$settable → "true" | | |
| homie/super-car/lights/intensity/$unit → "%" | | |
| homie/super-car/lights/intensity/$datatype → "integer" | | |
| homie/super-car/lights/intensity/$format → "0:100" | | |
| | | |
| homie/super-car/lights_0/$name → "Back lights" | | |
| homie/super-car/lights_0/intensity → "0" | | |
| homie/super-car/lights_1/$name → "Front lights" | | |
| homie/super-car/lights_1/intensity → "100" | | |
| ``` | | |
| | | |
| Note that you can name each element in your array individually ("Back lights", etc.). | | |
| | | |
| ---- | | |
| | | |
| ### Broadcast Channel | | ## Broadcast Channel |