homieiot/convention

Changes

Changes from v4.0.0 to develop

Commits

Commit title Date Hash
fix(links): https by default (#329) 2025-05-11 18:06:49 +0200 de48bf4
change(definitions): adds top-level ‘definitions’ section (#325) 2025-04-10 21:38:10 +0200 f23d619
fix(step): add calculations for step rounding (#313) 2024-12-09 07:54:48 +0100 c782375
fix(json): default should be an escaped json string (#308) 2024-11-19 13:04:17 +0100 f029eca
change(version): remove ‘higher’ constraint (#304) 2024-10-27 19:32:53 +0100 db70d60
minor clarification in the base-topic (#303) 2024-10-17 10:22:43 +0200 063301d
fix(enum): no duplicates (#302) 2024-10-13 01:27:34 +0200 aee10f3
chor(node/prop): make ’name’ optional (#300) 2024-09-07 13:33:16 +0200 7cfd0b5
fix: color encoding and brightness (#298) 2024-09-04 22:17:02 +0200 86f35d9
chore(convention): minor cleanup and consistency updates 2024-07-06 14:23:54 +0200 0d7baf5
Trigger website deployment on convention changes (#293) 2024-07-04 10:25:15 +0200 ce4f54a
fix(logging): one level up in doc structure 2024-07-03 14:54:46 +0200 ce5e371
chore(headings): move some headings 2024-06-30 11:05:43 +0200 63f21bd
Correct topic pattern for empty string payload (#288) 2024-02-17 18:20:16 +0200 133c6b8
homie5(alerts): replace ‘alert’ state with alert-topic (#283) 2023-11-15 23:38:52 +0100 eb0b437
fix(target): equality of received commands (#277) 2023-11-13 11:23:55 +0100 3715512
homie5(type): add type field for device and node (#282) 2023-11-05 12:12:29 +0100 11f0378
chore(state): add some more details around state management (#272) 2023-11-05 10:59:14 +0100 b314544
fix(broadcast): broadcasted messages should be non-retained (#280) 2023-11-05 10:48:22 +0100 ed50bc6
feat(unit): add ‘rpm’ (revolutions per minute) (#279) 2023-11-05 10:47:22 +0100 1331fcc
Added information about unpublishing/removing a device (#278) 2023-11-05 10:45:02 +0100 250d31a
fix(log): clarify all messages must be non-retained (#276) 2023-07-26 13:52:23 +0200 c8201c8
feat(color): allow to set a preferred format (#275) 2023-07-06 11:07:52 +0200 6eb4639
feat(json-type): add a JSON data type (#273) 2023-06-25 20:01:12 +0200 566a2d6
feat(log): change “alert” state, add logging (#262) 2023-06-25 15:13:31 +0200 adc710e
feat(property): add an optional $target property (#263) 2023-06-25 14:29:22 +0200 611ec27
clarify(enum): clarify that whitesapce is significant (#268) 2023-06-25 10:00:24 +0200 4bb1287
chore(id): simplify ID checks (#261) 2023-06-24 12:08:34 +0200 da23e1f
change(description): change arrays to objects (#270) 2023-06-24 12:08:17 +0200 015b328
feat(format): add color format ‘xyz’ (#274) 2023-06-24 12:07:55 +0200 1c15005
fix(validation): specify float/int validation order (#269) 2023-06-18 20:06:37 +0200 13e241e
fix(hierarchy): root device should not have root (#271) 2023-06-18 20:05:06 +0200 d6b4ad8
chore(properties): align the 3 property tables in the spec (#267) 2023-06-16 11:28:17 +0200 35837c2
chore(format): reformat table (flip-axis) for readability (#266) 2023-06-16 09:28:16 +0200 dbff282
fix(example): no description update in ‘ready’ state (#265) 2023-06-15 09:32:00 +0200 06446c0
fix(format): properly format the integer format template (#264) 2023-06-15 00:23:36 +0200 c1a58ce
chore(spec): spelling corrections (#260) 2023-06-15 00:13:04 +0200 9fa98ce
fix(base-topic): some occurences of homie/5/ were missing (#259) 2023-03-19 13:58:20 +0100 6afa235
feat(formats): add step size to integer/float formats (#257) 2023-03-17 13:45:02 +0100 953a1e2
feat(units): add airquality units; ppm (co2 / co) 2023-03-12 13:20:46 +0100 da5f1b4
feat(units): add windspeed units; m/s + knots 2023-03-07 11:10:07 +0100 ce7a3ee
fix(utf8): more explicit encoding details 2023-03-07 11:01:06 +0100 edf94b1
change(description-doc): relax language about omitting defaults (#258) 2023-03-17 13:42:05 +0100 67e0ad7
feat(units): add recommended units (#254) 2023-03-04 15:29:08 +0100 2fe6f7a
change(QoS) clarify QoS settings and switch to 2 (#253) 2023-01-26 10:32:56 +0100 063578d
refactor(mqtt) relocate all info wrt retained and qos (#252) 2023-01-26 10:30:50 +0100 c3f03bf
fix(strings) use 0x00 instead of 0x00 (#251) 2023-01-19 07:46:58 +0100 158b873
fix(version) device example missing required attribute (#250) 2023-01-18 23:20:50 +0100 d59d163
feat(version) add version to description document 2023-01-18 00:12:25 +0100 4accccd
feat(versioning) add improved versioning and compatibility 2023-01-17 23:29:52 +0100 4d10316
fix(property) handling empty-string values (#239) 2023-01-17 23:39:16 +0100 734d6ff
change(formats) float-colors, improved numbers, add booleans 2023-01-17 07:46:27 +0100 7e50cb1
feat(units) add kW, kWh and m3 as units 2023-01-17 06:50:42 +0100 0f8439f
readability(retained) reorder more logically (#245) 2023-01-17 09:11:35 +0100 1f73c11
fix(device) be more specific about empty attributes (#242) 2023-01-16 23:24:07 +0100 f974349
describe parent-child hierarchy and handling (#240) 2023-01-16 23:23:29 +0100 11dc08a
fix(percent) drop the percent type (#243) 2023-01-16 23:21:58 +0100 0155f38
drop node.$type field (#238) 2023-01-16 20:12:34 +0100 1c39612
format required for enum and color types 2023-01-15 22:06:14 +0100 e2e8ce7
drop separate v5 version 2023-01-15 21:18:09 +0100 5a34283
readability; move long payload list to bottom of MQTT chapter 2023-01-15 19:40:58 +0100 9641bd0
minor description-size improvements 2023-01-15 02:08:15 +0100 80f1772
implement single description topic (JSON) 2022-11-27 21:44:27 +0100 c97eea9
added 4.0 as the new 5.0 basis document 2022-11-27 20:22:51 +0100 1e0ec36
fix(device) be more specific about empty attributes (#241) 2023-01-16 23:22:30 +0100 451c995
chore(*) add design principles (#234) 2023-01-15 19:20:56 +0100 c0ba283
clarify $format being required for enum and color (#224) 2023-01-15 19:13:51 +0100 bb5efc7
chore(LICENSE): rename to get markdown rendering (#230) 2022-10-06 18:36:48 +0200 fd95584
Update logo link 2022-10-06 15:58:44 +0200 84c1c13
Fix: Added negative values to the valid range specification for floats 2021-01-25 04:17:45 -0500 fc643d6
Add datetime and duration to datatype enum. 2020-06-28 12:42:22 +0200 1b3efa4
Update convention.md 2020-05-11 07:38:16 -0500 dd6ee8e
Update convention.md 2020-05-11 07:31:26 -0500 449d952
Add implementation details and other things (#200) 2020-05-06 13:21:50 +0200 6d852df
clarify broadcast topics 2020-05-05 12:50:47 -0500 d68076d
added duration property 2020-05-05 12:34:17 -0500 9905706
clarify percentage values 2020-05-04 11:14:07 -0500 8824393
ready state clarification 2020-04-28 13:20:44 -0500 2c41d72
added datetime payload 2020-04-28 13:10:21 -0500 b813a15
clarification on allowed characters in topic ID 2020-04-26 11:09:58 -0500 2c28a5e
Fix: Grammar fix (#187) 2020-01-03 03:26:12 -0600 6f3f98a

Differences


v4.0.0
develop
n3version: v4.0.0n3version: develop
4releasedate: 27. August 20194releasedate: 11. May 2025
nn11## Introduction
12
13​The Homie convention is an open standard framework designed to facilitate the
14communication and integration of Internet of Things (IoT) devices using the
15[MQTT protocol](https://mqtt.org).
16
17In particular, the Homie convention defines a consistent topic structure and
18messaging format that enables devices to represent themselves, their data and
19their supported controls/commands in a uniform manner, enabling automatic
20discovery.
21
22## Roles
23
24When interacting through a shared MQTT broker, implementations of the Homie
25convention can take on one or both of the following roles simultaneously:
26
27- **Device**: an implementation that publishes the representation of a physical
28 appliance or logical entity, such as a car, a coffee machine or a protocol
29 bridge, to an MQTT broker.
30- **Controller**: an implementation that discovers and interacts with *devices*
31 over MQTT. For example a mobile app, or an if-this-then-that rules engine.
32
33Note that, for brevity and simplicity, the word "device" is often used
34to denote the combination of an appliance, the computer managing the appliance's
35MQTT representation and the representation itself.
36
n13Homie communicates through [MQTT](http://mqtt.org) and is hence based on the basic principles of MQTT topic publication and subscription.n39Homie communicates through [MQTT](https://mqtt.org) and is hence based on the basic principles of MQTT topic publication and subscription.
n18A topic level ID MAY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).n44A topic level ID MAY ONLY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).
n20A topic level ID MUST NOT start or end with a hyphen (`-`).n
nn48### QoS and retained messages
49
50The recommended QoS level is **Exactly once (QoS 2)** (except for non-retained, see below).
51
52* All messages MUST be sent as **retained**, UNLESS stated otherwise.
53* Controllers setting values for device properties publish to the Property `set` topic with **non-retained** messages only.
54* Controllers setting values for **non-retained** device properties should publish to the Property `/set` topic with a QoS of **At most once (QoS 0)**.
55* Devices publishing values for their **non-retained** properties must use **non-retained** messages, with a QoS of **At most once (QoS 0)**.
56
57For QoS details see [the explanation](#qos-choices-explained).
58
59### Last Will
60
61Homie requires the last will (LWT) to set the `homie` / `5` / `[device ID]` / `$state` attribute to the value **`lost`**, see [Device Lifecycle](#device-lifecycle).
62MQTT only allows one last will message per connection, but since a device can have children, the LWT message MUST be set on the
63root device (the device at the root of the parent-child tree).
64
65### Empty string values
66
67MQTT will treat an empty string payload as a "delete" instruction for the topic, therefor an
68empty string value is represented by a 1-character string containing a single byte value 0 (Hex: `0x00`, Dec: `0`).
69
70The empty string (passed as an MQTT payload) can only occur in 3 places;
71
72- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]`; reported property values (for string types)
73- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `set`; the topic to set properties (of string types)
74- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `$target`; the target property value (for string types)
75
76This convention specifies no way to represent an actual value of a 1-character string with a single byte 0. If a device
77needs this, then it should provide an escape mechanism on the application level.
78
23### Payload79## Payloads
nn82- The message MUST NOT include the UTF-8 [BOM](https://en.wikipedia.org/wiki/Byte_order_mark)
n28#### Stringn85### String
n31- An empty string ("") is a valid payloadn88- An [empty string](#empty-string-values) ("") is a valid payload
n33#### Integern90### Integer
n35- Integer types are UTF-8 encoded string literal representations of 64-bit signed whole numbersn92- Integer types are string literal representations of 64-bit signed whole numbers
n39- An empty string ("") is not a valid payloadn96- An [empty string](#empty-string-values) ("") is not a valid payload
n41#### Floatn98### Float
n43- Float types are UTF-8 encoded string literal representations of 64-bit signed floating point numbersn100- Float types are string literal representations of 64-bit signed floating point numbers
44- Floats range from 2<sup>-1074</sup> to (2-2<sup>-52</sup>)&ast;2<sup>1023</sup>101- Floats range from +/-(2^-1074) to +/-((2 - 2^-52) * 2^1023)
n49- An empty string ("") is not a valid payloadn106- An [empty string](#empty-string-values) ("") is not a valid payload
n51#### Booleann108### Boolean
n55- An empty string ("") is not a valid payloadn112- An [empty string](#empty-string-values) ("") is not a valid payload
n57#### Enumn114### Enum
n61- Payloads should have leading and trailing whitespace removedn118- Leading- and trailing-whitespace is significant, e.g. "Car" will not match " Car".
62- An empty string ("") is not a valid payload119- An [empty string](#empty-string-values) ("") is not a valid payload
n64#### Colorn121### Color
n66- Color payload validity varies depending on the property format definition of either "rgb" or "hsv"n123- Color payload validity varies depending on the property format definition of either "rgb", "hsv", or "xyz"
67- Both payload types contain comma separated whole numbers of differing restricted ranges124- All payload types contain comma-separated data of differing restricted ranges. The first being the type, followed by numbers. The numbers must conform to the [float](#float) format
68- The encoded string may only contain whole numbers and the comma character ",", no other characters are permitted, including spaces (" ")125- The encoded string may only contain the type, the [float](#float) numbers and the comma character ",", no other characters are permitted, including spaces (" ")
69- Payloads for type "rgb" contains 3 comma separated values of numbers with a valid range between 0 and 255. e.g. 100,100,100126- Payloads for type "rgb" contain 3 comma-separated values of [floats](#float) (`r`, `g`, `b`) with a valid range between 0 and 255 (inclusive). e.g. `"rgb,100,100,100"`
70- 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,75127- Payloads for type "hsv" contain 3 comma-separated values of [floats](#float). The first number (`h`) has a range of 0 to 360 (inclusive), and the second and third numbers (`s` and `v`) have a range of 0 to 100 (inclusive). e.g. `"hsv,300,50,75"`
128- Payloads for type "xyz" contain 2 comma separated values of [floats](#float) (`x`, `y`) with a valid range between 0 and 1 (inclusive). The "z" value can be calculated via `z=1-x-y` and is therefore not transmitted. (see [CIE_1931_color_space](https://en.wikipedia.org/wiki/CIE_1931_color_space)). e.g. `"xyz,0.25,0.34"`
129- *Note*: The `rgb` and `hsv` formats encode both color and brightness, whereas `xyz` only encodes the color, so;
130 - when brightness encoding is required: do not use `xyz`, or optionally add another property for the brightness (such that setting `hsv` and `rgb` values changes both the color property and the brightness one if required)
131 - if color only is encoded: ignore the `v` value in `hsv`, and use the relative colors of `rgb`
132 eg. `color_only_r = 255 * r / max(r, g, b)`, etc.
71- An empty string ("") is not a valid payload133- An [empty string](#empty-string-values) ("") is not a valid payload
72
n74### QoS and retained messagesn135### DateTime
n76The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is **QoS 1**.n137- DateTime payloads must use the ISO [8601 format](https://en.wikipedia.org/wiki/ISO_8601).
77All messages MUST be sent as **retained**, UNLESS stated otherwise.138- An [empty string](#empty-string-values) ("") is not a valid payload
n79### Last willn140### Duration
n81MQTT only allows one last will message per connection.n142- Duration payloads must use the [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations)
82Homie requires the last will (LWT) to set the `homie` / `device ID` / `$state` attribute to the value **`lost`**, see [Device Lifecycle](#device-lifecycle).143- The format is `PTxHxMxS`, where:
83As a consequence a new MQTT connection to the broker is required per published device.144`P`: Indicates a period/duration (required).
145`T`: Indicates a time (required).
146`xH`: Hours, where `x` represents the number of hours (optional).
147`xM`: Minutes, where `x` represents the number of minutes (optional).
148`xS`: Seconds, where `x` represents the number of seconds (optional).
149- Examples: `PT12H5M46S` (12 hours, 5 minutes, 46 seconds), `PT5M` (5 minutes)
150- An [empty string](#empty-string-values) ("") is not a valid payload
n85## Base Topicn152### JSON
n87The root topic in this document is `homie/`.n154- Contains a JSON string for transporting complex data formats that cannot be exposed as single value attributes.
88If this root topic does not suit your needs (in case of, e.g., a public broker or because of branding),155- The payload MUST be either a JSON-Array or JSON-Object type, for other types the standard Homie types should be used.
89you can choose another.
nn157## Domain and Root Topic
158
159The root topic in this convention is `"homie/5/"`. It consists of 2 segments, the first being the homie-domain,
160and the second indicating the major version number of this convention.
161The homie-domain must be a single segment and defaults to `"homie"`. If it does not suit your needs (in case of,
162e.g., a public broker or because of branding), you can change the domain part. The second segment, containing the version, may not be customized. This allows controllers to subscribe to only the devices they are compatible with.
163
164## Auto-Discovery
165
91Homie controllers must by default perform auto-discovery on the wildcard topic "+/+/$homie".166Homie 5 controllers must by default perform auto-discovery on the wildcard topic `"+/5/+/$state"`.
92Controllers are free to restrict discovery to a specific root topic, configurable by the user.167Controllers are free to restrict discovery to a specific homie-domain, configurable by the user.
168A zero length payload published on the `$state` topic indicates a device removal, see [device lifecycle](#device-lifecycle).
n94## Topologyn170## Topology and structure
n97An instance of a physical piece of hardware is called a *device*.n173Within the convention devices can be modelled to have children. For example, bridge
98For example, a car, an Arduino/ESP8266 or a coffee machine.174devices; a zwave bridge device (the parent) exposes many child devices (the
175zwave devices). There is no depth limit set on additionally nested children.
n103For example, a car might expose a `wheels` node, an `engine` node and a `lights` node.n180For example, a car might expose a `wheels` node, an `engine` node, and a `lights` node.
n107Properties represent basic characteristics of the node/device, often given as numbers or finite states.n184Properties represent basic characteristics of the node, often given as numbers or finite states.
108For example the `wheels` node might expose an `angle` property.185For example, the `wheels` node might expose an `angle` property.
109The `engine` node might expose a `speed`, `direction` and `temperature` property.186The `engine` node might expose a `speed`, `direction`, and `temperature` property.
n114Attributes are represented by topic identifier starting with `$`.n191Attributes are represented by a topic identifier starting with `$`.
n121* `homie` / **`device ID`**: this is the base topic of a device.n198* `homie` / `5` / **`[device ID]`**: this is the base topic of a device.
122Each device must have a unique device ID which adhere to the [ID format](#topic-ids).199Each device must have a unique device ID that adheres to the [ID format](#topic-ids).
nn203The following topic structure will be used to expose the device attributes:
204
126* `homie` / `device ID` / **`$device-attribute`**:205* `homie` / `5` / `[device ID]` / **`[$device-attribute]`**:
n128The following device attributes are mandatory and MUST be send, even if it is just an empty string.n207Devices have the following attributes:
n130| Topic | Description |n209| Attribute | Required | Description |
131|-------------|--------------------------------------------------------------------------:|210|-------------|----------|----------------------------------------------------------------|
132| $homie | The implemented Homie convention version |211| `$state` | yes | Reflects the current state of the device. See [Device Lifecycle](#device-lifecycle) |
133| $name | Friendly name of the device |212| `$description`| yes | The description document (JSON), describing the device, nodes, and properties of this device. **Important**: this value may only change when the device `$state` is either `init`, `disconnected`, or `lost`. |
134| $state | See [Device Lifecycle](#device-lifecycle) |213| `$log` | no | A topic that allows devices to log messages. See [Logging](#logging) |
135| $nodes | [Nodes](#nodes) the device exposes, separated by `,` for multiple ones. |
136| $extensions | Supported extensions, separated by `,` for multiple ones. |
n138Optional topics include:n215The JSON description document is a JSON object with the following fields;
n140| Topic | Description |n217| Field | Type | Required | Default | Nullable | Description |
141|-----------------|-------------------------------|218|-----------|--------------|----------|---------|----------|-------------|
142| $implementation | An identifier for the Homie implementation (example "esp8266") |219| `homie` |string | yes | | no | The implemented Homie convention version, without the "patch" level. So the format is `"5.x"`, where the `'x'` is the minor version. |
220| `version` | integer | yes | | no | The version of the description document. Whenever the document changes, a new version must be assigned. This does not need to be sequential, eg. a timestamp or a random number could be used. |
221| `nodes` |object | no | `{}` | no | The [Nodes](#nodes) the device exposes. An object containing the [Nodes](#nodes), indexed by their [ID](#topic-ids). Defaults to an empty object.|
222| `name` |string | no | [device-id] | no | Friendly name of the device. Defaults to the [ID](#topic-ids) of the device. |
223| `type` |string | no | | no | Type of Device. Please ensure proper namespacing to prevent naming collisions. |
224| `children` |array-strings | no | `[]` | no | Array of [ID](#topic-ids)'s of child devices. Defaults to an empty array.|
225| `root` |string | yes/no | | no | [ID](#topic-ids) of the root parent device. **Required** if the device is NOT the root device, MUST be omitted otherwise. |
226| `parent` |string | yes/no | same as `root`| no | [ID](#topic-ids) of the parent device. **Required** if the parent is NOT the root device. Defaults to the value of the `root` property. |
227| `extensions`|array-strings | no | `[]` | no | Array of supported extensions. Defaults to an empty array.|
n144For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine` and a `lights` node would send:n229For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine`, and a `lights` node would send:
145
n147homie/super-car/$homie → "2.1.0"n
148homie/super-car/$name → "Super car"
149homie/super-car/$nodes → "wheels,engine,lights"
150homie/super-car/$implementation → "esp8266"
151homie/super-car/$state → "ready"231homie/5/super-car/$state → "init"
232homie/5/super-car/$description → following JSON document;
nn234```json
235 {
236 "homie": "5.0",
237 "name": "Supercar",
238 "version": 7,
239 "nodes": {
240 "wheels": { ... },
241 "engine": { ... },
242 "lights": { ... }
243 }
244 }
245```
246
247#### Device hierarchy
248
249Devices can be organized in parent-child relationships. These are expressed via the device
250attributes `root`, `parent`, and `children`. In any parent-child tree, there is only one
251"root" device, which is the top-level device that has no parent, but only children.
252
253Example: a ZWave bridge (`id = "bridge"`), which exposes a ZWave device with a dual-relay (`id = "dualrelay"`),
254which respectively control Light1 (`id = "light1"`) and Light2 (`id = "light2"`). So there are 4 devices in total.
255Then these are the attribute values:
256
257| | id | children | root | parent |
258|--------------|-------------|----------------------|----------|-------------|
259| Zwave bridge | "bridge" | ["dualrelay"] | | |
260| Zwave relay | "dualrelay" | ["light1", "light2"] | "bridge" | |
261| First light | "light1" | | "bridge" | "dualrelay" |
262| Second light | "light2" | | "bridge" | "dualrelay" |
263
264To monitor the state of child devices in this tree 2 topic subscriptions are needed. The `$state` attribute of the device itself, as well as the `$state` attribute of its root device.
265Because if the root device loses its connection to the MQTT server, the last will (LWT), will set its `$state` attribute to `"lost"`, but it will not update the child-device states. Hence the need for 2 topic subscriptions.
266
267The `state` of any device should be determined as follows:
268| has a `root` set | `root` state | device state |
269|------------------|--------------|--------------|
270| no | n.a. | device state is the `$state` attribute of the device itself
271| yes | not `"lost"` | device state is the `$state` attribute of the device itself
272| yes | `"lost"` | device state is `"lost"` (`$state` attribute of the root device)
273
n156The `$state` device attribute represents the current state of the device.n277The `$state` device attribute represents the current state of the device. A device exists once a valid value is set in the `$state` attribute. It doesn't mean the device is complete and valid (yet), but it does mean it exists.
157There are 6 different states:278
279There are 5 possible state values:
n160This state is optional, and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online. n282This state is optional and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online.
161* **`ready`**: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. A Homie Controller can assume default values for all optional topics.283A device may fall back into this state to do some reconfiguration.
284* **`ready`**: this is the state the device is in when it is connected to the MQTT broker and has sent all Homie messages describing the device attributes, nodes, properties, and their values. The device has subscribed to all appropriate `/set` topics and is ready to receive messages.
n166* **`lost`**: this is the state the device is in when the device has been "badly" disconnected.n289* **`lost`**: this is the state the device is in when the device has been "badly" disconnected. **Important**: If a root-device `$state` is `"lost"` then the state of **every child device in its tree** is also `"lost"`.
167You must define this message as LWT.290You must define this message as the last will (LWT) for root devices.
168* **`alert`**: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention.291
169You have to send this message when something is wrong.292In order to permanently remove a device the following steps should be performed in order:
2931. remove the retained `$state` attribute from the broker by publishing a zero length payload message to its topic. The device will cease to exist.
2942. any other retained attributes or property values should be cleared via the same method afterwards.
n173* `homie` / `device ID` / **`node ID`**: this is the base topic of a node.n298* `homie` / `5` / `[device ID]` / **`[node ID]`**: this is the base topic of a node.
174Each node must have a unique node ID on a per-device basis which adhere to the [ID format](#topic-ids).299Each node must have a unique node ID on a per-device basis which adheres to the [ID format](#topic-ids).
n178* `homie` / `device ID` / `node ID` / **`$node-attribute`**:n303There are no node attributes in MQTT topics for this level.
n180All listed attributes are **required**. A node attribute MUST be one of these:n305The Node object itself is described in the `homie` / `5` / `[device ID]` / `$description` JSON document. The Node object has the following fields:
n182| Topic | Description |n307| Field | Type | Required | Default | Nullable | Description |
183|-------------|-------------------------------------------------------------------------------------------|308|-------------|--------------|----------|---------|----------|-------------|
184| $name | Friendly name of the Node |309| `name` |string | no | [node-id] | no | Friendly name of the Node. Defaults to the [ID](#topic-ids) of the node. |
185| $type | Type of the node |310| `type` |string | no | | no | Type of Node. Please ensure proper namespacing to prevent naming collisions. |
186| $properties | Exposed properties, separated by `,` for multiple ones. |311| `properties`|object | no | `{}` | no | The [Properties](#properties) the Node exposes. An object containing the [Properties](#properties), indexed by their [ID](#topic-ids). Defaults to an empty object.|
n188For example, our `engine` node would send:n313For example, our `engine` node would look like this:
nn315```json
316 ...
317 "engine": {
318 "name": "Car engine",
319 "properties": {
320 "speed": { ... },
321 "direction": { ... },
322 "temperature": { ... }
323 }
324 }
325 ...
326```
327
328### Properties
329
330* `homie` / `5` / `[device ID]` / `[node ID]` / **`[property ID]`**: this is the base topic of a property.
331Each property must have a unique property ID on a per-node basis which adheres to the [ID format](#topic-ids).
332
333#### Property Attributes
334
335| Attribute | Required | Description |
336|-----------|----------|----------------------------------------------------------------|
337| | yes | A property value (e.g. a sensor reading) is directly published to the property topic, e.g.: `homie/5/super-car/engine/temperature → "21.5"` |
338| `$target` | no | Describes an intended state change. The `$target` attribute must either be used for every value update (including the initial one), or it must never be used. |
339
340The Property object itself is described in the `homie` / `5` / `device ID` / `$description` JSON document. The Property object has the following fields:
341
342| Field | Type | Required | Default | Nullable | Description |
343|-----------|--------------|----------|----------|----|---------|
344| `name` | string | no | [property-id] | no | Friendly name of the Property. Defaults to the [ID](#topic-ids) of the property. |
345| `datatype`| string | yes | | no | The data type. See [Payloads](#payload). Any of the following values: `"integer", "float", "boolean", "string", "enum", "color", "datetime", "duration", "json"`. |
346| `format` | string | see [formats](#formats) | see [formats](#formats) | no | Specifies restrictions or options for the given data type. |
347| `settable`| boolean | no | `false` | no | Whether the Property is settable. |
348| `retained`| boolean | no | `true` | no | Whether the Property is retained. |
349| `unit` | string | no | | no | Unit of this property. See [units](#units). |
350
351
352For example, our `temperature` property would look like this in the device/node description document:
353
354```json
355 ...
356 "temperature": {
357 "name": "Engine temperature",
358 "unit": "°C",
359 "datatype": "float",
360 "format": "-20:120"
361 }
362 ...
363```
364And the following MQTT topic with the reported property value:
n191homie/super-car/engine/$name → "Car engine"n
192homie/super-car/engine/$type → "V8"
193homie/super-car/engine/$properties → "speed,direction,temperature"
194```
195
196### Properties
197
198* `homie` / `device ID` / `node ID` / **`property ID`**: this is the base topic of a property.
199Each property must have a unique property ID on a per-node basis which adhere to the [ID format](#topic-ids).
200
201* A property payload (e.g. a sensor reading) is directly published to the property topic, e.g.:
202 ```java
203 homie/super-car/engine/temperature → "21.5"
204 ```
205
206* Properties can be **settable**.
207 For example, you don't want your `temperature` property to be settable in case of a temperature sensor
208 (like the car example), but to be settable in case of a thermostat.
209
210* Properties can be **retained**.
211 A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).
212
213A combination of those flags compiles into this list:
214
215* **retained + non-settable**: The node publishes a property state (temperature sensor)
216* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
217* **non-retained + non-settable**: The node publishes momentary events (door bell pressed)
218* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
219
220
221#### Property Attributes
222
223* `homie` / `device ID` / `node ID` / `property ID` / **`$property-attribute`**:
224
225The following attributes are required:
226
227| Topic | Description | Payload type |
228|-----------|------------------------------------------------------|---------------------------------------------|
229| $name | Friendly name of the property. | String |
230| $datatype | The data type. See [Payloads](#payloads). | Enum: \[integer, float, boolean,string, enum, color\] |
231
232The following attributes are optional:
233
234| Topic | Description | Payload type |
235|-----------|------------------------------------------------------|---------------------------------------------|
236| $format | Specifies restrictions or options for the given data type | See below |
237| $settable | Settable (<code>true</code>). Default is read-only (<code>false</code>) | Boolean |
238| $retained | Non-retained (<code>false</code>). Default is Retained (<code>true</code>). | Boolean |
239| $unit | Optional unit of this property. See list below. | String |
240
241For example, our `temperature` property would send:
242
243```java
244homie/super-car/engine/temperature/$name → "Engine temperature"
245homie/super-car/engine/temperature/$settable → "false"
246homie/super-car/engine/temperature/$unit → "°C"
247homie/super-car/engine/temperature/$datatype → "float"
248homie/super-car/engine/temperature/$format → "-20:120"
249homie/super-car/engine/temperature → "21.5"366homie/5/super-car/engine/temperature → "21.5"
n252Format:n369#### Settable and retained properties
n254* For `integer` and `float`: Describes a range of payloads e.g. `10:15`n371Properties can be **settable** and/or **retained**. For example, you don't want your `temperature`
255* For `enum`: `payload,payload,payload` for enumerating all valid payloads.372property to be settable in case of a temperature sensor (like the car example), but it should be
256* For `color`:373settable in the case of a thermostat setpoint.
257 - `rgb` to provide colors in RGB format e.g. `255,255,0` for yellow.374
258 - `hsv` to provide colors in HSV format e.g. `60,100,100` for yellow.375A property is retained by default. A non-retained property would be useful for momentary events
376(e.g. doorbell pressed). See also [QoS settings](#qos-and-retained-messages).
377
378A combination of the **settable** and **retained** flags compiles into this list:
379
380| retained | settable | description |
381|----------|----------|-------------|
382| yes | yes | The node publishes a property state and can receive commands for the property (by a controller or other party) (lamp power)
383| yes | no | (**default**) The node publishes a property state (temperature sensor)
384| no | yes | The node publishes momentary events and can receive commands for the property from a controller (brew coffee)
385| no | no | The node publishes momentary events (doorbell pressed)
386
387
388#### Formats
389
390The format attribute specifies restrictions or options for the given data type. User interfaces can derive hints from
391the formats for displaying values.
392
393| Type | Required | Default | Description |
394|--------------|----------|----------|-------------|
395| float | no | `:` | `[min]:[max][:step]` where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for [float types](#float). Eg. `10.123:15.123`. If the minimum and/or maximum are missing from the format, then they are open-ended, so `0:` allows a value >= 0.<br/>The optional `step` determines the step size, eg. `2:6:2` will allow values `2`, `4`, and `6`. It must be greater than 0. See notes below this table on calculations. |
396| integer | no | `:` | `[min]:[max][:step]` where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for [integer types](#integer). Eg. `5:35`. If the minimum and/or maximum are missing from the format, then they are open-ended, so `:10` allows a value <= 10. <br/>The optional `step` determines the step size, eg. `2:6:2` will allow values `2`, `4`, and `6`. It must be greater than 0. See notes below this table on calculations. |
397| enum | yes | | A comma-separated list of non-quoted values. Eg. `value1,value2,value3`. Leading- and trailing whitespace is significant. Individual values can not be an empty string, hence at least 1 value must be specified in the format. Duplicates are not allowed. |
398| color | yes | | A comma-separated list of color formats supported; `rgb`, `hsv`, and/or `xyz`. The formats should be listed in order of preference (most preferred first, least preferred last). See the [color type](#color) for the resulting value formats. E.g. a device supporting RGB and HSV, where RGB is preferred, would have its format set to `"rgb,hsv"`. |
399| boolean | no | `false,true` | Identical to an enum with 2 entries. The first represents the `false` value and the second is the `true` value. Eg. `close,open` or `off,on`. If provided, then both entries must be specified. **Important**: the format does NOT specify valid payloads, they are descriptions of the valid payloads `false` and `true`. |
400| json | no | `{\"anyOf\": [{\"type\": \"array\"},{\"type\": \"object\"}]}` | A [JSONschema](https://json-schema.org/) definition, which is added as a string (escaped), NOT as a nested json-object. See [JSON considerations](#json-considerations), for some ideas wrt compatibility. If a client fails to parse/compile the JSONschema, then it should ignore the given schema and fall back to the default schema.
401
402**Note on numeric formats and step-sizes**:
403
404The base for calculating a proper value based on `step` should be `min`, `max`, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding. In case of integers, the input MUST already be a valid integer, before rounding to a step.
405
406There is a caveat when rounding towards a step: if the value to be rounded, is less than the base then the intermediate values might round differently since proper mathematical rounding is done "away from 0". A positive 1.5 rounds to 2 (so up), and negative -1.5 rounds to -2 (so down), but both round "away from 0". Example showing the unexpected effect:
407
408input | format | result | explanation
409-|-|-|-
410`5` | `"0:10:2"` | `6` | base = 0; result = round((5-base)/stepsize) * stepsize + base = 6
411`5` | `":10:2"` | `4` | base = 10; result = round((5-base)/stepsize) * stepsize + base = 4
412
413To mitigate this do not use `round(x)`, but use `floor(x + 0.5)` instead. Such that rounding always goes up:
414
415input | format | result | explanation
416-|-|-|-
417`5` | `"0:10:2"` | `6` | base = 0; result = floor((5-base)/stepsize + 0.5) * stepsize + base = 6
418`5` | `":10:2"` | `6` | base = 10; result = floor((5-base)/stepsize + 0.5) * stepsize + base = 6
419
420
421#### Units
n262* `°C`: Degree Celsiusn425* `°C`: Degree Celsius (see 'Degree' for encoding)
263* `°F`: Degree Fahrenheit426* `°F`: Degree Fahrenheit (see 'Degree' for encoding)
nn428 * Character '°' is [Unicode: `U+00B0`](https://www.compart.com/en/unicode/U+00B0), Hex: `0xc2 0xb0`, Dec: `194 176`
n266* `gal`: Galonn430* `gal`: Gallon
nn433* `kW`: Kilowatt
434* `kWh`: Kilowatt-hour
nn436* `Hz`: Hertz
437* `rpm`: Revolutions per minute
nn440* `m³`: Cubic meter
441 * Character '³' is [Unicode: `U+00B3`](https://www.compart.com/en/unicode/U+00B3), Hex: `0xc2 0xb3`, Dec: `194 179`
nn443* `m/s`: Meters per Second
444* `kn`: Knots
nn447* `ppm`: Parts Per Million
448* `s`: Seconds
449* `min`: Minutes
450* `h`: Hours
451* `lx`: Lux
452* `K`: Kelvin
453* `MK⁻¹`: Mired
454 * Character '⁻' is [Unicode: `U+207B`](https://www.compart.com/en/unicode/U+207B), Hex: `0xe2 0x81 0xbb`, Dec: `226 129 187`
455 * Character '¹' is [Unicode: `U+00B9`](https://www.compart.com/en/unicode/U+00B9), Hex: `0xc2 0xb9`, Dec: `194 185`
nn458The non-ASCII characters are specified as Unicode codepoints and the UTF-8 byte sequence that represents them. Since the same characters can be created in many visually similar ways it is important to stick to the exact byte sequences to enable proper interoperability.
459
nn462#### Target attribute
463
464The `$target` attribute for properties allows a device to communicate an intended state change of a property. This serves 2 main
465purposes;
466
4671. closing the control loop for a controller setting a value (if the property is settable).
4682. feedback in case a change is not instantaneous (e.g. a light that slowly dimms over a longer period, or a
469 motorized valve that takes several minutes to fully open)
470
471If implemented, then a device must first update the `$target` attribute, then start the transition (with
472optional state-value updates during the transition), and when done update the property value to match the
473`$target` value (functional equivalent, not necessarily a byte-by-byte equality).
474
475If a new target is received (and accepted) from a controller by publishing to the property's `set` topic, then the exact value received must be published to the `$target` topic (byte-by-byte equality). To allow for closing the control loop.
476
477**Notes:**
478
479- a controller can only assume that the command it send to the `set` topic was received and accepted. Not necessarily that it will ever reach the target state, since if another controller updates the property again, it might never reach the target state.
480- The same goes for possible conversions (colors), rounding (number formats), etc. it will be very hard to check functional equivalence, since the value published may have a different format. So a controller should NOT implement a retry loop checking the final value. At best they should implement retries until the value set is being accepted.
481- Homie devices representing remote hardware (typically when bridging) should NOT set the `$target` attribute upon receiving a change from the hardware device. This is only allowed if the hardware explicitly distinguishes between current value and target value. This is to prevent a loop; e.g. a homie controller sets 100% as target, software instructs hardware to change, intermediate updates received from hardware; 20%, 40%, etc, should NOT overwrite the `$target` value, since that still is 100.
482
483
n281* `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).n486* `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / **`set`**: The device must subscribe to this topic if the property is **settable** (in the case of actuators for example).
n283A Homie controller publishes to the `set` command topic with non-retained messages only.n488A Homie controller publishes to the `set` command topic with non-retained messages only. See [retained messages](#qos-and-retained-messages).
n285The assigned and processed payload must be reflected by the Homie device in the property topic `homie` / `device ID` / `node ID` / `property ID` as soon as possible.n490The assigned and processed payload must be reflected by the Homie device in the property topic `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` or target attribute `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `$target` as soon as possible.
n288To give an example: A `kitchen-light` device exposing the `light` node with a settable `power` property subscribes to the topic `homie/kitchen-light/light/power/set` for commands:n493To give an example: A `kitchen-light` device exposing the `light` node with a settable `power` property subscribes to the topic `homie/5/kitchen-light/light/power/set` for commands:
n291homie/kitchen-light/light/power/set ← "true"n496homie/5/kitchen-light/light/power/set ← "true"
n294In response the device will turn on the light and upon success update its `power` property state accordingly:n499In response, the device will turn on the light and upon success update its `power` property state accordingly:
n297homie/kitchen-light/light/power → "true"n502homie/5/kitchen-light/light/power → "true"
n300## Broadcast Channeln505If the `light` were a dimmable light with a `brightness` property (0-100%), and it would be set to slowly dim over 5 seconds, then the `$target` attribute can be used (assuming once per second updates);
nn507```java
508homie/5/kitchen-light/light/brightness/set ← 100
509homie/5/kitchen-light/light/brightness/$target → 100
510homie/5/kitchen-light/light/brightness → 20 (after 1 second)
511homie/5/kitchen-light/light/brightness → 40 (after 2 seconds)
512homie/5/kitchen-light/light/brightness → 60 (after 3 seconds)
513homie/5/kitchen-light/light/brightness → 80 (after 4 seconds)
514homie/5/kitchen-light/light/brightness → 100 (after 5 seconds)
515```
516
517## Alert topic
518
519Devices can raise alerts. Alerts are user facing messages that have an ID, they can be set and removed.
520The alert topic is defined as;
521
522* `homie` / `5` / `[device ID]` / `$alert` / `[alert ID]` → "alert message"
523
524A device can raise a message on a specific ID. Once the alert is no longer usefull or has been resolved, it can be removed by deleting the topic. Alerts must be send as retained messages. The alert ID must have a valid [ID format](#topic-ids), where topic ID's starting with `$` are reserved for Homie usage.
525
526Examples;
527```java
528/homie/5/mydevid/$alert/childlost = "Sensor xyz in livingroom hasn't reported updates for 3 hours"
529/homie/5/mydevid/$alert/battery = "Battery is low, at 8%"
530```
531
532In the examples above, once the situation is resolved (the sensor comes back to live, or the batteries are replaced), the device will delete the topics again, indicating the alerts have been handled.
533
534## Broadcast Topic
535
302Homie defines a broadcast channel, so a controller is able to broadcast a message to all Homie devices:536Homie defines a broadcast topic, so a controller can broadcast a message to all Homie devices:
n304* `homie` / `$broadcast` / **`level`**: `level` is an arbitrary broadcast identifier.n538* `homie` / `5` / `$broadcast` / **`[subtopic]`**: where `subtopic` can be any topic with single or multiple levels. Each segement must adhere to the [ID format](#topic-ids).
305It must adhere to the [ID format](#topic-ids).539
540The messages SHOULD be non-retained.
n312homie/$broadcast/alert ← "Intruder detected"n547homie/5/$broadcast/alert ← "Intruder detected"
548homie/5/$broadcast/security/alert ← "Intruder detected"
n315Any other topic is not part of the Homie convention.n551## Logging
552
553Since devices may be resource constraint they might not have logging capabilities. Homie provides a specific
554topic where devices can send log messages. The topic is defined as;
555
556* `homie` / `5` / `[device ID]` / `$log` / `[level]`
557
558The topic-value is the logged message, no sub-topics are allowed.
559All log messages send should be non-retained.
560The `level` is set according to the following table:
561
562level | description
563--------|------------
564`debug` | detailed information for troubleshooting purposes
565`info` | informational message, device is working as expected
566`warn` | something potentially harmful happened
567`error` | an error happened, the device will continue to operate but functionality might be impaired
568`fatal` | a non-recoverable error occured, operation of the device is likely suspended/stopped
569
570```java
571homie/5/my-device/$log/warn → "battery low"
572homie/5/my-device/$log/error → "sensor value is out of range"
573```
574
575Note that MQTT is not meant to be a logging solution, and hence it should be used with care. The implementation should
576try and limit the traffic on the MQTT bus. If devices implement messages and levels that can be "noisy", then the
577device should provide a configuration option to turn them off, to limit the bandwidth consumed.
n319This convention only covers discoverability of devices and its capabilities.n581This convention only covers the discoverability of devices and their capabilities.
320The aim is to have standardized MQTT topics for all kind of complex scenarios.582The aim is to have standardized MQTT topics for all kinds of complex scenarios.
n325The proper term `homie` is reserved and must not be used as the suffix or as part of the domain name.n587The proper term `homie` is reserved and must not be used as a suffix or as part of the domain name.
n331The recommended license is the [CCA 4.0](https://homieiot.github.io/license), since this is the license Homie itself uses.n593The recommended license is the [CCA 4.0](https://homieiot.github.io/license) since this is the license Homie itself uses.
tt596## Implementation notes
597
598### Device initialization
599
600Some devices require knowledge of their settable retained properties to function properly.
601The homie convention does not specify how to initialize/recover them e.g. after a power cycle.
602A few common approaches are:
603
604* A device can simply load default values from some configuration file.
605* A device can restore its previous state from some local storage. This is the recommended way.
606* A device may try to restore its state using MQTT. This can be done by subscribing to the respective channels.
607 The controller could set all properties of a device once it becomes `ready`.
608 An alternative way is to recover the state from other MQTT channels that are external to the Homie specification.
609* If a property is not critical for correctly functioning, there is no need to recover it.
610
611### Device reconfiguration
612
613If a device wishes to modify any of its nodes or properties, it can
614
615* disconnect and reconnect with other values, or
616* set `$state=init` and then modify any of the attributes.
617
618Devices can remove old properties and nodes by deleting the respective MQTT topics by publishing an empty message
619to those topics (an actual empty string on MQTT level, so NOT the escaped `0x00` byte, see also [empty string values](#empty-string-values)).
620
621When adding many child devices, implementations should take care of not publishing too many parent-updates, since every controller would have to parse the description again and again.
622
623#### Adding children
624
625The recommended way to add child device is as follows:
626
6271. first publish any child-devices, as any other device
628 1. set child-device state to `"init"`
629 1. publish child-device details (including parent details in `root` and `parent` fields)
630 1. set child-device state to `"ready"`
6311. update the parent device, as any other change
632 1. set parent state to `"init"`
633 1. update parent description (add any child IDs to its `children` array)
634 1. set parent state to `"ready"`
635
636Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.
637
638#### Removing children
639
640The recommended way to remove child device is as follows:
641
6421. update the parent device
643 1. set parent state to `"init"`
644 1. update parent description (remove any child IDs from its `children` array)
645 1. set parent state to `"ready"`
6461. clear any child-device(s) topics, starting with the `$state` topic
647
648Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.
649
650### Versioning
651
652Some considerations related to versioning in this specification;
653
654* compatibility is assumed to be major version only, so version 5 for this spec.
655* the base topic includes the major version. This allows controllers to only subscribe to devices they are
656compatible with.
657
658#### Backward compatibility
659
660* backward compatibility: a v5 controller controlling a v5 device with a smaller minor version. Eg. a v5.3
661controller sending commands to a v5.0 device.
662* Controllers should be aware of unsupported features in older major or minor versions they subscribe to because the spec for that version is known.
663
664#### Forward compatibility
665
666* forward compatibility: a v5 controller controlling a v5 device with a higher minor version. Eg. a v5.0
667controller sending commands to a v5.2 device.
668* Controllers should ignore unknown fields, properties, attributes, etc. within an object (device, node, or property), but keep the object itself.
669* Controllers should ignore the entire object (device, node, or property) if in a known field, property, or attribute an illegal value is encountered. For example;
670 * illegal characters in a topic or name
671 * unknown data type
672 * unknown/illegal format
673 * required element missing
674
675### JSON considerations
676
677Validation of JSON payloads is hard. The most common approach to validate JSON data is to use [JSONschema](https://json-schema.org/).
678Unfortunately JSONschema is not a standard, it is a long list of mostly incompatible drafts of a potential standard. And as such one
679has to take into account the potential differences in implementations. This is about the JSONschema specifics itself as well as its reliance on RegEx engines for string validations, which are also known to be riddled with incompatibilities (typically language/platform specific).
680
681The most popular JSONschema versions over time tend to be [`draft 4`](https://json-schema.org/specification-links.html#draft-4), [`draft 7`](https://json-schema.org/specification-links.html#draft-7) and the latest (at the time of writing) [`2020-12`](https://json-schema.org/specification-links.html#2020-12).
682
683General recommendations;
684- If possible use a library that implements the latest JSONschema version available
685- When writing schema's make sure they are compatible with the popular versions mentioned above
686- Try to avoid RegEx'es, if you have to use them, then;
687 - restrict them to character classes and modifiers (`"+", "-", "*", "?"`)
688 - do not use back-tracking and OR (`"|"`) constructs (the OR construct can typically be handled on the JSONschema level using an `anyOf` construct)
689- If a device fails to parse the JSONschema, or a RegEx, then by default it should skip validation and assume the payload is valid.
690
691### QoS choices explained
692
693The nature of the Homie convention makes it safe about duplicate messages, so QoS levels for reliability **At least once (QoS 1)**
694and **Exactly once (QoS 2)** should both be fine. The recommended level is **Exactly once (QoS 2)**, since a resend on QoS 1 might have a different order, and hence is slightly less reliable, in case another device sends a new message that lands in between the 'send' and 'resend' of the first message. However, the probability of that happening is most likely negligible.
695
696Keep in mind that if you change the QoS level to **At least once (QoS 1)**, then it should be done so for the entire Homie network.
697Because the MQTT order will not hold if the QoS levels of messages are different. That said; anyone who accepts the lesser reliability of
698**At least once (QoS 1)**, will most likely also not care about the potential ordering issue of mixed QoS levels.
699
700For **non-retained** properties the QoS level is **At most once (QoS 0)** to ensure that events don't arrive late or multiple times. Because the events and commands are time-sensitive. With **At most once (QoS 0)** messages will not be queued by the broker for delivery if the subscriber (a device or controller) is currently disconnected. Which effectively translates to "either you get it now, or you don't get it at all".
701
702