From b1e6491f77421ae4623391a7f53af7f3e6c13f34 Mon Sep 17 00:00:00 2001 From: Ethel Morgan Date: Mon, 6 Jul 2020 18:23:10 +0100 Subject: import website from previous repo --- src/catbus-design.thrust | 183 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/catbus-design.thrust (limited to 'src/catbus-design.thrust') diff --git a/src/catbus-design.thrust b/src/catbus-design.thrust new file mode 100644 index 0000000..843d3b6 --- /dev/null +++ b/src/catbus-design.thrust @@ -0,0 +1,183 @@ +--- +title: 'CatBus: design' +subtitle: a home automation platform built on MQTT +date: 2020-02-20 +--- +{% extends 'templates/base.html' %} +{% block body %} + +
+

{{ title }}

+

{{ subtitle }}

+
+
+{% markdown %} +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](https://xkcd.com/927/). + +
+contents… +
+ +[TOC] + +
+
+ +## Why not `$platform` ? + +I initially started by using Apple HomeKit via [HomeBridge](https://homebridge.io), 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](https://www.home-assistant.io), 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: + +- Automate things I forget to do. +- Turn down the lights as it gets late for better sleep. +- Turn up the lights before the alarm goes off for an easier wake. +- Detect when I'm at home, and remind me to eat. + +To lead a comfier life: + +- Convenience! +- Actionable metrics, e.g. to predict whether or not I will feel cold in a given outfit given room temperatures. + +## Practical considerations + +### Low maintenance + +This is a hobby project, so it must be low-effort to maintain: + +- The system must be easy to add new parts to. +- The system must allow existing parts to run without ongoing intervention. +- The system should support multiple parts acting in concert without reconfiguration. + +### The real world comes first + +The system will not "own" the devices it controls: the TV's own remote must continue to work. + +- The system must not interfere with external control systems. +- The system should reflect changes caused by external control systems. + +### 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: + +- The system must have a command-line interface. +- The system must have a web interface. +- The system should have a HomeKit interface. +- The system should have physical controllers. + +## 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: + +- A **user** is a human interacting with the system, either directly via the software or indirectly by being inside the house. +- An **observer** is a part of the system that observes the state of something external to the system, such as the temperature of a room or the volume of a speaker. +- An **actuator** is a part of the system that affects the state of something external to the system, such as the channel of a TV or whether a light is on. + In practice, many actuators are also observers. +- An actuator has one or more **controls**, usually one for each thing that it affects, such as one for a TV's volume level and another for its channel. +- A **controller** is a part of the system that tells the actuators what to do using their controls, such as a web UI or an automation daemon. + +### 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](#low-maintenance). + +### 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](#low-maintenance). +However a naïve implementation risks violating the [real world requirement](#the-real-world-comes-first), 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](https://mqtt.org/) 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](#low-maintenance): + +- Controllers & actuators only need to directly communicate with the MQTT Broker. +- Controllers & actuators only need to know what topics they will subscribe or publish to. +- Multiple actuators can listen on the same topic, which allows for "complex" devices: + - For example, "turn the speakers on" can trigger a relay device to physically turn the speakers on, and trigger another device to redirect its audio output. + +To meet the [real world requirement](#the-real-world-comes-first), actuators only act when a topic's value changes. +The two rules for actuators are: + +- When a topic is updated, the actuator tries to make it true. +- When an actuator notices an external change in the real-world state, it puts the new value on the bus. + +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](https://prometheus.io/docs/practices/naming/), 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. + +topic | data +------------------------------------------------- | ---- +`home/living-room/speakers/power` | `on` +`home/living-room/speakers/input_enum` | `UPnP` +`home/living-room/speakers/input_enum/values` | `UPnP\nAirPlay` +`home/outside/weather-sensor/temperature_celcius` | `15` + +The hierarchy also allows for controllers presenting a unified device to a user, even if it is actually multiple devices: + +- `speakers/power` is controlled by an Energenie RF relay socket. +- `speakers/input_enum` is controlled by Snapcast. +- `speakers/volume_percent` is controlled by ALSA. +{% endmarkdown %} +
+{% endblock %} -- cgit v1.2.3