MQTT is a lightweight message protocol often used in IoT and home automation applications. It is very easy to get up and running. However once you have a larger number of devices communicating, it's easy to get in a pickle if you don't have a plan for how they will all communicate.

Here's the strategy I'm using at the moment.

Basic principles

Topics should be all lower case and contain only alphanumerics and hyphens. Whilst MQTT does support a wider set of characters in topic names than this, keeping it simple helps avoiding confusing errors in spacing and capitalisation.

Publishing status changes - Status topics

When a device changes state, it posts to a specific topic for that device and sensor. These are called status topics.

Other devices with interest in their status can monitor these topics.

Messages posted to these topics should be retained. This means that when a client connects to the broker it can receive information on the last known status of each device.

These topics have the following format.

Values posted to these topics should be simple text, boolean or integer values.

value -> home/s/<room-name>/<device-name>/<sensor-name>

Examples

Light is turned in the living room.

true -> home/s/living-room/light/on

Garage door is closed

false -> home/s/garage/door/open

Temperature changes to 19° in the study

19 -> home/s/study/temperature/value

Motion detected in the kitchen

true -> home/s/kitchen/motion/occupied

Issuing commands to devices - Command topics

We use the same protocol but substitute the s for a c.

Examples

Turn on the living room light

true -> home/c/living-room/light/on

Examples using Node-RED, HomeBridge and a custom web app.

In my current setup I use Node-RED integrated with HomeBridge. I also have a web app that uses React and Redux that I use to monitor the house and turn devices on and off.

This describes the flow of messages and updates that make this work.

Example via Node-Red and HomeBridge

# Bedroom button pressed
true -> home/c/bedroom/button/pressed

# Node-Red receives command and issues update to HomeBridge
# Node-Red receives state change update from HomeBridge and forwards to MQTT
true -> home/s/bedroom/light/on

# Web App monitors the channel and updates redux state
# Light appears as on in the web app

Example via Web App and Node-Red

# Web App posts directly on command topic
true -> home/c/living-room/lamp/on

# Node-Red receives command and issues update to HomeBridge
# Node-Red receives state change update from HomeBridge and forwards to MQTT
true -> home/s/living-room/light/on

# Web App monitors the channel and updates redux state
# Light appears as on in the web app

Summary

I find this separation of command and status messages works well in managing the flow of messages and avoiding confusing deadlocks or infinte loops. The retained message feature of MQTT means that whenever a web client initialises the web app, they naturally get an up to date representation of the state of the house.