Qth Conventions for MQTT

The following outlines the conventions defined by Qth for MQTT.

Topics, Payloads and QoS

Topics should be organised in a directory-tree-like fashion. As a recommendation, physical location should be at the base of the hierarchy. Leaves of the hierarchy should (in general) represent simple entities, for example the state of a light or the temperature of a room.

All messages must have valid UTF-8 encoded JSON payloads, even if that means setting an unused payload to ‘null’.

Unless you have good reason to do otherwise, QoS 2 should be used.

Events and Properties

The style of use of a particular topic in Qth systems should be consistent and well defined over time. Topic uses are divided into the following behaviour classes:

  • Events always have the ‘retain’ flag cleared and represent an ephemeral report or command.
    • Many-to-One Events have one subscriber and many publishers, for example a bell which can be rung.
    • One-to-Many Events have one publisher and many subscribers, for example a motion sensor.
  • Properties always have the ‘retain’ flag set and represent a value which changes or may be changed over time.
    • Many-to-One Properties have one subscriber and many publishers, for example the state of a light.
    • One-to-Many Properties have one publisher and many subscribers, for example the humidity of a room.

Registration

Since MQTT doesn’t provide a way to list all of the topics in use at a given time, Qth requires that the topics used by the system must be registered.

Qth clients are responsible for registering the topics of all events and properties for which they are the ‘one’ side of. For example, a Many-to-One Event should be registered by the subscriber while a One-to-Many event should be registered by the publisher.

A Qth Reigstrar collates the information published by each client to produce a hierarchy of One-to-Many Properties which enumerate the complete hierarchy of MQTT topics.

Client Registration

Clients should pick, or be assigned, a globally unique ID.

Registration is performed by publishing a QoS 2, retained message to meta/clients/<CLIENT-ID>. Whenever the set of topics to be registered by the client changes, a new retained message should be published to this same topic.

The registration message should take the following form:

{
    "description": "<HUMAN-READABLE DESCRIPTION OF CLIENT'S PURPOSE>",
    "topics": {
        "<TOPIC>": <TOPIC DESCRIPTION>,
        ...
    }
}

The ‘topics’ object should be a mapping from topics registered by the client to a description of that topic. A topic description is an object of the following form:

{
    "behaviour": <TOPIC BEHAVIOUR>,
    "description": "<HUMAN-READABLE DESCRIPTION OF TOPIC>",
    "on_unregister": <ACTION>,
    "delete_on_unregister": <BOOLEAN>
}

Here, the ‘behaviour’ value must be one of:

  • "EVENT-1:N" For One-to-Many Events.
  • "EVENT-N:1" For Many-to-One Events.
  • "PROPERTY-1:N" For One-to-Many Properties.
  • "PROPERTY-N:1" For Many-to-One Properties.

The on_unregister and delete_on_unregister entries are optional and may be omitted. Their behaviour is described in the next section.

Client Unregistration

Upon client disconnection, the client must publish a QoS 2, retained empty message (not just JSON null) to meta/clients/<CLIENT-ID> to clear their registration. This message should be set as the client’s MQTT will.

The Qth registrar can use the on_unregister and delete_on_unregister values associated with a topic to help clean up the system or help inform other clients about the unavailability of topics.

Topics with an on_unregister value specified will be set to this value (if a property) or sent as a final event (if an event). This will be sent only when the client disconnects but not if the topic is removed from the set of registered topics while the client remains connected.

The delete_on_unregister is boolean whose default value is false. If set to true, the associated property will be deleted when the client unregisters. If the topic is an event, the behaviour is undefined. As with on_unregister, if the topic is removed from the topic list this action will not take place.

If both on_unregister and delete_on_unregister are specified the result is unspecified.

The Qth Registrar

The Qth registrar aggregates messages published into meta/clients/+ to produce a hierarchy of One-to-Many properties in meta/ls/# whose topics end in trailing slashes.

The topic meta/ls/ (note the trailing slash) describes the topics at the root of the address space. Likewise meta/ls/foo/ describes the topics in foo/, e.g. foo/bar and foo/baz, but not topics in subdirectories such as foo/quz/quo.

Each directory’s property contains a JSON object of the following format:

{
    "<TOPIC>": [
        {"behavior": "...", "description": "...", "client_id": "...", ...},
        ...
    ],
    ...
}

For every topic (and subdirectory) in a path a corresponding entry in this object with <TOPIC> set to just the basename will be created. For example, if we have a topic foo/bar`, the property ``meta/ls/foo/ will have an entry bar (not foo/bar).

Each item contains a list of JSON objects describing the uses of the topic. Typically a topic will only have one use (e.g. it might be an event) but there are occasions where it may have several. For example a topic may be both an event and a directory simultaneously. Also, if two clients mistakenly register the same topic, both of these registrations may be listed to aid debugging.

In addition to the ‘behaviour’ and ‘description’ fields provided in the client’s subscription, the ID of the client which initiated the registration is also included.

Note that ‘subdirectories’ are also included and their ‘behaviour’ is defined as “DIRECTORY”.

For example, say we had the following set of topics:

  • lounge/light (Many-to-One Property)
  • lounge/light/power_usage (One-to-Many Property)
  • lounge/motion (One-to-Many Event)
  • lounge/tmperature (One-to-Many Property)
  • lounge/tv/power (Many-to-One Property)
  • lounge/tv/channel (Many-to-One Property)
  • bedroom/light (Many-to-One Property)
  • bedroom/motion (One-to-Many Event)
  • …and so on…

The property meta/ls/lounge/ would be as follows:

{
    "light": [
        {"behaviour": "PROPERTY-N:1", "description": "...", "client": "..."},
        {"behaviour": "DIRECTORY", "description": "...", "client": "..."}
    ],
    "motion": [
        {"behaviour": "EVENT-1:N", "description": "...", "client": "..."},
    ],
    "temperature": [
        {"behaviour": "PROPERTY-1:N", "description": "...", "client": "..."},
    ],
    "tv": [
        {"behaviour": "DIRECTORY", "description": "...", "client": "..."}
    ],
}