summaryrefslogtreecommitdiff
path: root/src/catbus-design.thrust
diff options
context:
space:
mode:
authorEthel Morgan <eth@ethulhu.co.uk>2020-07-06 18:23:10 +0100
committerEthel Morgan <eth@ethulhu.co.uk>2020-07-06 18:23:10 +0100
commitb1e6491f77421ae4623391a7f53af7f3e6c13f34 (patch)
treeacc4ce7ae214b92dbf2c269c70e94b68dac1d640 /src/catbus-design.thrust
parent04be5845dbaa6f8dec45a80dbe199861608b96f2 (diff)
import website from previous repo
Diffstat (limited to '')
-rw-r--r--src/catbus-design.thrust183
1 files changed, 183 insertions, 0 deletions
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 %}
+ <nav>
+ <a href='/catbus'>&gt; catbus home</a>
+ </nav>
+ <header>
+ <h1>{{ title }}</h1>
+ <p>{{ subtitle }}</p>
+ </header>
+ <article>
+{% 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/).
+
+<details markdown='1'>
+<summary>contents…</summary>
+<div markdown='block'>
+
+[TOC]
+
+</div>
+</details>
+
+## 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 %}
+ </article>
+{% endblock %}