A well thought out architecture and API is critical to the performance, flexibility, maintenance and extensibility of any software solution.
The general design breaks the solution into 3 main subsystems:
- Front end user interface. HTML5 / JavaScript leveraging Twitter's Bootstrap front end framework, using HTML objects to embed widgets as active and passive elements into HTML5 screens, and uses websockets to communicate to the automation engine using a topic based publish / subscribe model.
- Automation engine. This is the brains of the system using Visual Basic .NET and uses event driven messages that pass through several modules including a state store, database, rules processing and console.
- Plugin Manager. This is the integration layer that connects to the external world using Node.JS or .NET and uses a plugin design so that connection semantics to each device / service are managed in discrete JavaScript modules.
Events & Message Queue
Events are the heart of the automation engine where all events are represented as messages managed by a message queue. Being event driven enhances performance and allows use of modern design patterns like publish / subscribe and modern tools like Node.JS / JavaScript (which are inherently asynchronous) and .NET async.
The message format is critical to ensure it is rich enough to cater for all types of messages and not too big to be difficult to work with and consume space. The format chosen is topic based similar to MQTT messages, and has proven to be very durable and flexible, as follows:
<NETWORK> / <CATEGORY> / <CLASS> / <INSTANCE> / <SCOPE> / <DATA>
- Network allows partitioning of messages and segmenting of networks if implemented in a large environment with several hubs (I only use one hub so the network name is common for all messages).
- Category is the general type of message, eg. SYSTEM for system messages like timers, LIGHTING for all messages related to lights. There is an editable category list that covers all general automation and other categories.
- Class is the entity that is interacting with the automation system and generally is matched to the plugin which represents the device or service being integrated with. For example, I use CBUS as my lighting automation system so there is a CBUS message class instantiated from the CBUS plug-in that deals with the semantics of connecting with the CBUS interface (serial with an ASCII protocol).
- Instance is the interface on the class entity, and there may be many of these. In the CBUS example all the lights have a unique instance name (eg. Master-Bedroom). The interface definition is managed by the plug-in.
- Scope defines the type of message sent to/from the instance. With the CBUS example, when a light switch is toggled on the wall, the CBUS plug-in receives this message from the CBUS interface as a update and fills the scope field out as STATE_CHANGE. It also allows the instance to have several functions (eg. can be used to send commands as well as status).
- Data is the value of the message and for the CBUS example of receiving an update that someone switched the light on will have the value of 100 (100% dim level). All values are managed and stored as strings but the rules and transformation modules will recognise numbers within strings.
Scheduling is performed via the rules engine using time based triggers.
HTML5 widgets and plugins subscribe or publish to channels. Channels are bi-directional names for message class instances used with widgets and plugins. For example, a light widget would subscribe to the LIGHTING/CBUS/Master-Bedroom channel and all messages on that channel will be sent to the light widget and it can also publish messages on that channel (eg. widget can turn on the master bedroom light).
Why Microsoft .Net as the platform?
The automation engine is written in Visual Basic .NET for several reasons but mostly legacy.
The code started as VB 6 in its first version back in 2003, when it was small system not unlike the average DIY home automation software (eg. hard coded, monolithic design, single interface). Although re-written and re-architected several times, there is still some of the original code present, and there was never a compelling enough reason to switch to another language or development platform. The next version will be ported to C# only because Visual Basic isn't a dominant language like it was (although I do like the expressiveness of VB) and it is easy to convert as the .NET runtime is consistent.
JavaScript / Node.JS might be an obvious language given the plug-ins and front end are written in JavaScript. However .Net is a more robust runtime, with better error handling, multi-threading and better language semantics. The message management is multi-threaded and an important part of the architecture that couldn't be implemented as well with Node.JS (Node has other advantages which I'll discuss in the project log about the plug-ins).
Why not Java so it can run on Linux? No reason except that my background is with .Net not Java and I don't see any advantages of Java as a development platform to better solve home automation use-cases. The next version of this system will be ported to .Net Core which is the Open Source implementation of DotNet just released and will run on Linux if needed. I live in the Microsoft ecosystem for my day job and prefer tools like Visual Studio and Windows, but also recognise the power of Open Source so ensured there is nothing in the design or implementation choices that would hinder running on a Linux server.
Also, popular fully featured home automation software are written using different platforms and although the high level architecture of this system isn't much different to some of the other fully featured HA software hubs, there are significant differences in the implementation. For example, widget based HTML5 front end, .NET automation engine and Node.JS for integration seems to be unique and bring advantages compared to other home automation software.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.