CatBus: design

a home automation platform built on MQTT.

last modified on

Home automation and the “Internet of Things” (IoT) is so hot right now. Between the phone vendors, washing machine makers, and light-bulb pushers, there are many home automation platforms. In this document, I will try to explain why I have made my own 15th standard.

contents…

Why not $platform ?

I initially started by using Apple HomeKit via HomeBridge, which has a large collection of plugins for many of my devices. However, it was always limited to being controlled from Apple devices, and the automation abilities were limited.

I briefly looked at Home Assistant, which is open-source and can be controlled from Linux, but seemed brittle and inflexible.

When I was gifted some hardware that lacked plugins for either HomeBridge or Home Assistant, requiring me to write software to use them, I decided I would invest that effort into building my own platform.

Wants & needs

Why do I want home automation?

To lead a “better” life.

What do I mean by “better”?

To lead a less stressful life:

To lead a comfier life:

Practical considerations

Low maintenance

This is a hobby project, so it must be low-effort to maintain:

The real world comes first

The system will not “own” the devices it controls: the TV’s own remote must continue to work.

Frictionless interaction

When I used HomeKit, a frequent problem was that I had to use an Apple product to use it. If I had left my phone charging instead of in my pocket, I had to go and get it, which grew to be an annoyance. It should thus be controllable without needing a specific item:

Models of operation

Now that I have my requirements, there are a few high-level models of operation for managing the distributed state.

First, a quick glossary:

Remote objects

Remote objects (RPC, REST, SOAP, …) is a common idiom in distributed computing. A sender sends a message to recipient, which actions the message, possibly returning a response. The sender must know where to send the message, or talk to a central router that itself must know where to send the message.

Under this system, to set up a new actuator for an existing control, I would need to add the actuator to the control’s configuration. This is more wiring and configuration than I can be bothered with, and contravenes the low maintenance requirement.

Intent-based

In an intent-based control system there is a central intended system state, and one or more actuators that try to make it reality. For example, if the intent says that the lights should be on, an actuator daemon continually checks the actual state of the lights, turning them on if they are off.

This allows for low coupling, as the controller that sets the intent doesn’t care which actuator turns on the light and how, which meets the low maintenance requirement. However a naïve implementation risks violating the real world requirement, e.g. if a light is turned off at the device itself, the actuator will repeatedly try to turn it back on.

The compromise: semi-intent-based

The current implementation uses MQTT as a sort of real-time database. MQTT has “topics”, and clients can publish events to topics and subscribe to events from topics.

It meets the low maintenance requirement:

To meet the real world requirement, actuators only act when a topic’s value changes. The two rules for actuators are:

For example, with one actuator, one observer, and an external control:

  1. The TV is off.
  2. The topic home/living-room/tv/power is set to on.
  3. The actuator receives this change of state, and turns the TV on.
  4. After some time, the TV is turned off with the remote.
  5. The observer notices the TV is off, and sets the topic home/living-room/tv/power to off.
  6. The actuator receives this change of state, and tries to turn the TV off, which it already is.

An example with two actuators:

  1. The speakers are off.
  2. The topic home/living-room/speakers/power is set to on.
  3. The first actuator receives this change of state, and turns the amp on.
  4. The second actuator receives this change of state, tries to turn the receiver on, and fails.
  5. The second actuator sets the topic home/living-room/speakers/power to off.
  6. The first actuator receives this change of state, and turns the amp back off.

Scheme

MQTT is similar to a simple key-value store, with strings as keys and unstructured bytes as values. The keys can have hierarchy, like a filesystem path, but this isn’t required.

As it is stringly typed, I have opted for a key naming scheme:

home/{zone}/{device}/{control}(/{metadata})?

Similar to the Prometheus best practices, topics have _suffixes that state a topic’s units and valid values. For example, a topic home/kitchen/speakers/volume_percent can only have number values in the range [0,100].

Since not all types of control are so convenient, topics can have optional metadata topics, e.g. a topic …/foo_enum can have a metadata topic …/foo_enum/values to list valid values for controllers to choose from.

topicdata
home/living-room/speakers/poweron
home/living-room/speakers/input_enumUPnP
home/living-room/speakers/input_enum/valuesUPnP\nAirPlay
home/outside/weather-sensor/temperature_celcius15

The hierarchy also allows for controllers presenting a unified device to a user, even if it is actually multiple devices: