AutoBuddy is a home automation system build almost entirely with Python. It uses asyncio and thus required that I created/adapted a number of libraries to use asyncio throughout. AutoBuddy grew organically through hit and misses. I plan to present it, explain how it grew and the mistakes I made.
IoT, Home automation is a very hot topic these days. When I bought a bunch of WiFi LED lights in the summer of 2015, I wanted to automated things a bit. The lights came with a mobile app, but I could not find an easy way to control the lights from my computer. That was disappointing! Then the Internet connection went down, and I could not control the lights anymore.
Why do I need to access a server on the other side of the world when me and my lights are right here?
Don’t get me wrong, I think the Cloud is great. I am no Luddite. In fact, I am the opposite of a Luddite, and by that I don’t mean an Etiddul. I love technology, I love new gadgets, new technologies. Yet, I do not like waste. If me and my lights are in a room, the fact that I turn it off or on should stay in the family …. home. If only to be “Green”.
Think about it. The small packet that passes though the network to tell that far-away server that you want to turn off the light doesn’t use much energy. Neither does the small packet that tells the light to actually turn off. But when you multiply that by billions of devices, that turn out to represent a LOT of energy.
So I decided to write my own application, and soon things got out of hand.
So I decided to write my own application. The first things that came to mind were:
- It must use the LAN as much as possible
- It should be Web-based, so that one app works both on computers and mobile
The next question was: What kind of architecture? It was self-evident, for me at least, that AutoBuddy should be a messaging bus. Different entities on the bus would send messages on the bus and react to the messages received.
Having decided that, it was clear that before I could even start talking about turning on the lights, AutoBuddy needed 4 components:
- ControlBuddy: To control the distribution of messages on the bus.
- WebBuddy: A web application.
- SocketBuddy: A way to link WebBuddy to the bus using Web socket.
- ActionBuddy: A rule engine to react to messages.
And then, of course, the lighting thing:
- LifxBuddy: A bridge to control Lifx lights.
To manage the bus, I decided to use AMQP. Worse… I decided to use Qpid. This was a bad idea because:
- AMQP was, IMO, a big mess at the time.
- Needed an extra daemon
This mistake has now been solved, and ControlBuddy performs all the the message multiplexing by itself. Using MQTT might have been a better option.
I decided early to use SQLAlchemy to make the database access more flexible. At the time I was not aware of an asyncio implementation of the library, but I thought it was OK, and so far it has been. The mistake I made was to think that all participants to the AutoBuddy ecosystem would need access to the database, even if only read-only. This was not really needed. It has not been corrected yet.
Early on, the structure of the messages had to be fleshed out. It was definitely influenced by the early choice of AMQP, but is is simple and powerful enough. The messages are essentially json’ed dictionaries, with the following keys:
The subject specifies the entity the message is meant for, or originates from. The general format is:
With the type, the type of devices, e.g. light, sensor, mediaplayer. With the subtype, a more specific entity, e.g. light.lifx, light.yeelight, sensor.ruuvi, mediaplayer.kodi With the id identifying a specific device.
The content_type specifies what is in the message. The types are:
- command: a command sent to a device
- event: something happened somewhere
- request: Request a bus participant to do something.
- response: Result of a request
- restricted event: an event containing sensitive information.
The value is a dictionary containing the needed information. It will often contain a “target” that will have the format of the “subject” and will specify a device.
There are 3 different types of modules:
- Bridge modules: Interface multiple devices with the AutoBuddy bus. Only one instance should be running.
- Device module: Interface one device with the AutoBuddy bus. Multiple instance with different subtypes can run.
- Service Module: Modules generating events to be consumed by other modules.
A bridge module to control Lifx WIFI light bulbs. The library aiolifx was developed to support these devices
A bridge module to control Xiaomi Yeelight LED lights. This led to the development of the aioxiaomi library.
Bridge module to control Flic BLE smart buttons.
Bridge module to control Sonoff and other WiFi switches running the Tasmota firmware. Use MQTT to communicate with the switches.
A bridge module to get sensor data from BLE devices. Uses a plugin infrastructure to support various devices. At this time only RuuviTag is supported. Led to the development of aioblescan, a library that uses HCI commands to receive or broadcast BLE data, including EddyStone beacons.
A device module for I2C sensors. Uses a plugin infrastructure to support various sensors. At this time only BH1750, BMP180 and HTU21D sensors are supported.
A service module that can monitor the presence of IP and/or Bluetooth MAC addresses. This is done by pinging the IP address of the device or requesting the name of the Bluetooth devices. The libraries aioarping and aiobtname were developed for this purpose.
A bridge module to control Kodi media player instances.
A bridge module for WeMo motion detector. Led to the development of the library aioouimeaux.
A bridge module for WeMo switches. Also uses aioouimeaux.
A device module for voice recognition. This module will send specific events upon recognition of utterances. Can be configured to use PocketSphinx, Google Speech, Wit.ai, Houndify, Bing or IBM Watson.
A service module to log various events in an sqlite database. The data stored can be visualised in WebBuddy.
A service module to log events on the cloud with ThingsBoard. Dashboards on ThingsBoard can be used to visualised the data.
A service module monitoring zeroconf services on behalf of the AutoBuddy bus. Led to the development of the library aiozeroconf. Used by KodiBuddy for detecting presence of Kodi media player instances.
A service module running an MQTT broker under AutoBuddy’s control. The MQTT broker is hbmqtt. hbmqtt can use plugins defined using namespace packages. MQTTBuddy uses some deep voodoo to include a local authentication plugin into the namespace without requiring a package.
The service module that will serve WebBuddy, the web application, and communicate with it over web socket. This is the only module that will receive restricted events, and it will only dispatch them to instance of WebBuddy logged with admin privileges. This uses aiohttp for all its web-based communications.
The ActionBuddy service module controls all the action in response to events. It accepts complex configuration and is aware of time. It knows about sunrise and sunset based on your configured location. Allows users to created State Variables and Rules to interact with the verious object on the AutoBuddy bus.
This service module multiplexes all the messages on the AutoBuddy bus . It also can persist data on behalf of other modules.