<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Home Automation on mhemeryck</title><link>https://mhemeryck.xyz/tags/home-automation/</link><description>Recent content in Home Automation on mhemeryck</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 16 Nov 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://mhemeryck.xyz/tags/home-automation/index.xml" rel="self" type="application/rss+xml"/><item><title>modbusbackup</title><link>https://mhemeryck.xyz/posts/2021-11-16-modbusbackup/</link><pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-11-16-modbusbackup/</guid><description>&lt;p&gt;Another home automation post!
Please check out my earlier &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation series&lt;/a&gt; first to get an idea what I am talking about.&lt;/p&gt;
&lt;p&gt;Recently, I figured to rework some part of it to be able to deal better with certain kind of failures, while at the same time still being able to offer all the automation abilities as before.&lt;/p&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s &lt;strong&gt;recap the architecture&lt;/strong&gt; that was already in place:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/architecture.png" alt="current architecture"&gt;&lt;/p&gt;</description><content:encoded>&lt;p&gt;Another home automation post!
Please check out my earlier &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation series&lt;/a&gt; first to get an idea what I am talking about.&lt;/p&gt;
&lt;p&gt;Recently, I figured to rework some part of it to be able to deal better with certain kind of failures, while at the same time still being able to offer all the automation abilities as before.&lt;/p&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s &lt;strong&gt;recap the architecture&lt;/strong&gt; that was already in place:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/architecture.png" alt="current architecture"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inputs: a unipi unit is used to read the digital inputs (e.g. push buttons); updates are pushed as MQTT events&lt;/li&gt;
&lt;li&gt;MQTT broker: a centralized broker takes in the updates and pushes them to all subscribers&lt;/li&gt;
&lt;li&gt;home assistant: sees updates on the MQTT inputs and performs specific actions as automations on outputs push out another MQTT events&lt;/li&gt;
&lt;li&gt;outputs: a unipi unit takes in MQTT updates and triggers a corresponding relay change (e.g. a light).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, refer to the &lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;architecture post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although this approach has worked fairly well for me, it inherently holds some potential issues.&lt;/p&gt;
&lt;h2 id="problem-centralized-points-of-failure"&gt;Problem: centralized points of failure&lt;/h2&gt;
&lt;p&gt;Firstly, on the &lt;strong&gt;software level&lt;/strong&gt;, all connections need to run through home assistant and the MQTT broker.
An advantage of this is that everything is configurable from a centralized place, in software.
However, this also requires a higher level of reliability on those software instances.
As an example, initially, doing any updates to this software component was a bit more involved since any downtime here, effectively translated into non-functioning light push buttons.
Downtime is fortunately a lot less common since I did start using &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt; for orchestrating the deployment.
Nevertheless, it is (currently) not possible to run home assistant in an &amp;ldquo;high availability&amp;rdquo; (HA) mode.
HA mode means that multiple home assistant instances can run at the same time, where one instance sits idle as a fail-over for the main running instance.&lt;/p&gt;
&lt;p&gt;In addition, on the &lt;strong&gt;network level&lt;/strong&gt;, the links between the components all depend on the local network.
Any downtime at this level of the stack again translates into the non-availability of my light push buttons.&lt;/p&gt;
&lt;p&gt;This solution would need to be &lt;strong&gt;more robust&lt;/strong&gt; against issues on both &lt;strong&gt;software&lt;/strong&gt; and &lt;strong&gt;network&lt;/strong&gt; layers.
At the same time, I would still like to keep &lt;strong&gt;centralized control from home assistant&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="solution-direct-link"&gt;Solution: direct link&lt;/h2&gt;
&lt;p&gt;I did start digging around and got some helpful feedback on the unipi forum from this &lt;a href="https://forum.unipi.technology/topic/1434/connecting-2-unipi-controllers-over-rs485/7"&gt;post on connecting 2 unipi units together&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first issue of the &lt;strong&gt;dependency on the home assistant instance&lt;/strong&gt; could be mitigated by moving the automation logic out of home assistant.
This would mean that some other component could listen to the input events and do the required actions on the outputs.
The unipi units could easily run such extra software themselves.&lt;/p&gt;
&lt;p&gt;The second issue related to the &lt;strong&gt;network layer&lt;/strong&gt; could be solved by finding a way to connect to the involved unipi units directly together.
This post on &lt;a href="https://www.unipi.technology/news/the-four-ways-to-set-up-your-automation-project-257"&gt;unipi connections&lt;/a&gt; provided some inspiration in that respect: the third option details using the RS-485 bus which is available on most unipi units to connect devices together.&lt;/p&gt;
&lt;p&gt;Control from home assistant will still be possible: either the update should come from the toggle of an input button (via the direct connection) or from an MQTT event.
There should be no issue in having both options at the same time.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The new architecture now changes into the following:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-11-16/arch.png" alt="proposal architecture"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inputs / outputs: the edges of the diagram just again show the inputs and output&lt;/li&gt;
&lt;li&gt;unipi units: 1 unipi unit is shown for inputs, 1 for outputs&lt;/li&gt;
&lt;li&gt;home assistant: state updates on inputs and outputs still come in over MQTT. State changes on the outputs can still be pushed using MQTT.&lt;/li&gt;
&lt;li&gt;MQTT broker: sits between the unipi units and home assistant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;key difference&lt;/strong&gt; over the previous architecture is that the link between the input push buttons and the output relays is no longer established using an automation in home assistant.
Instead, there is a direct link over RS-485 using the &lt;a href="https://www.simplymodbus.ca/FAQ.htm"&gt;Modbus&lt;/a&gt; protocol.&lt;/p&gt;
&lt;h2 id="hardware"&gt;Hardware&lt;/h2&gt;
&lt;p&gt;The following image (taken from the post on &lt;a href="https://www.unipi.technology/news/the-four-ways-to-set-up-your-automation-project-257"&gt;unipi connections&lt;/a&gt;) shows a conceptual wiring diagram to connect one or more unipi units together.
Essentially, it is a 2-wire connection, &lt;a href="https://en.wikipedia.org/wiki/Daisy_chain_%28electrical_engineering%29"&gt;daisy-chaining&lt;/a&gt; all units.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-11-16/wiring.png" alt="wiring"&gt;&lt;/p&gt;
&lt;p&gt;As the photo shows, the wiring I did use was a simple piece of leftover &lt;strong&gt;CAT6 UTP&lt;/strong&gt; (the grey piece of cable behind the black ethernet connector in the front).
In fact, I only did use 2 of the 8 wires.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-11-16/hardware.jpg" alt="hardware"&gt;&lt;/p&gt;
&lt;p&gt;For my purposes I only did connect 2 units together, but you could in principle extend this over to multiple clients on the same bus.
This would practically mean extending the 2-wire connection from the last connection on.&lt;/p&gt;
&lt;p&gt;I did not need to introduce any additional resistors nor change any of the DIP switches, this all worked fine out of the box.&lt;/p&gt;
&lt;p&gt;As the unipi units readily expose the RS-485 interface, this step turned out to be really simple.&lt;/p&gt;
&lt;h2 id="software"&gt;Software&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;protocol&lt;/strong&gt; you would typically run on this wiring setup is &lt;strong&gt;&lt;a href="https://www.simplymodbus.ca/FAQ.htm"&gt;Modbus&lt;/a&gt;&lt;/strong&gt;.
Within Modbus, there is one server and all the other clients are daisy-chained on the same bus.
All clients provide their own set of inputs and outputs (&amp;ldquo;coils&amp;rdquo;, &amp;ldquo;discrete inputs&amp;rdquo;, &amp;ldquo;input registers&amp;rdquo; and &amp;ldquo;holding registers&amp;rdquo;) in a predefined range.
Read and write control is fully managed by the server.
To read specific values, the server will just poll those values from the client.
To write updates, the server can similarly just put the values where they need to be on the clients.
The speed at which updates from a client are propagated thus also depend on the polling speed the server uses.
In case any communications needs to occur between clients, this is only possible from the server.
The server would then first read those values from one client (by polling) and later on write them to the other client.&lt;/p&gt;
&lt;p&gt;The unipi OS exposes the RS-485 connection via a specific linux device; see the &lt;a href="https://kb.unipi.technology/en:hw:02-neuron:portmap"&gt;unipi serial port map&lt;/a&gt;.
For my neuron L403 and L303 I would need to use the &lt;code&gt;/dev/ttyNS0&lt;/code&gt; devices.&lt;/p&gt;
&lt;p&gt;Running a Modbus protocol stack on both the client and server devices was however &lt;em&gt;not&lt;/em&gt; provided by default by unipi.
Here I did write my own small script &lt;a href="https://github.com/mhemeryck/modbusbackup"&gt;&lt;code&gt;modbusbackup.py&lt;/code&gt;&lt;/a&gt;, which can run both in client and server mode.&lt;/p&gt;
&lt;p&gt;The idea is to run a Modbus client (server mode) on the device that will be expecting incoming Modbus requests.
A Modbus server is ran (client mode) on the device that sends out Modbus messages.&lt;/p&gt;
&lt;p&gt;The implementation itself is based on &lt;a href="https://pymodbus.readthedocs.io/en/latest/index.html"&gt;PyModbus&lt;/a&gt;, which makes it possible to easily write both client and server code.&lt;/p&gt;
&lt;p&gt;For now, I only implemented this connection between my unipi unit that reads my push buttons and the unipi unit that controls relays connected to lights.
To illustrate, let&amp;rsquo;s again go through toggling a light:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;push button is pushed&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/UniPiTechnology/evok"&gt;evok&lt;/a&gt; detects the push button change and exposes the event through a websocket event (among others).&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;modbusbackup.py&lt;/code&gt; client (on the inputs unit) sees the websocket event, maps it our own configured Modbus address and sends it out as a Modbus message&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;modbusbackup.py&lt;/code&gt; server (on the relays unit) sees the incoming Modbus message, and translates it to the correct relay update; the current relay state is first retrieved (evok REST API) and then toggled.&lt;/li&gt;
&lt;li&gt;evok handles the relay update&lt;/li&gt;
&lt;li&gt;the relay is toggled; the light is turned on&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="client-code"&gt;Client code&lt;/h3&gt;
&lt;p&gt;The key function is &lt;code&gt;ws_process&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_ws_process&lt;/span&gt;(payload) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Process incoming websocket payload, push to modbus RTU&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loads(payload)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Ignore analog I?O&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#34;ai&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;ao&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Incoming message for websocket &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;obj&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Don&amp;#39;t send any events related to trailing edges&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; address &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _circuit_map()[obj[&lt;span style="color:#e6db74"&gt;&amp;#34;circuit&amp;#34;&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KeyError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Could not find mapping address for &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;obj[&lt;span style="color:#e6db74"&gt;&amp;#39;circuit&amp;#39;&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Blocking sync call&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Writing &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;obj[&lt;span style="color:#e6db74"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; to address &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;address&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; for circuit &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;obj[&lt;span style="color:#e6db74"&gt;&amp;#39;circuit&amp;#39;&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _modbus_client()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write_coil(address, obj[&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;], unit&lt;span style="color:#f92672"&gt;=&lt;/span&gt;_UNIT)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The function takes a payload from a websocket event, made available through evok.
The reason we use a websocket event here is that it is easier to check any updates from the inputs as opposed to polling ourselves.
Additionally, evok already exposes the events using websockets.
This is part is quite close to the earlier solution presented in the &lt;a href="https://mhemeryck.xyz/posts/2021-07-27-home_automation_software/"&gt;software post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;circuit_map&lt;/code&gt; function is singleton wrapper around a dictionary mapping the unipi digital inputs to Modbus addresses.
Similarly, the &lt;code&gt;_modbus_client&lt;/code&gt; function is also a singleton wrapper to avoid having to constantly recreate the Modbus connection to the other unit.&lt;/p&gt;
&lt;p&gt;See this link for the full &lt;a href="https://github.com/mhemeryck/modbusbackup/blob/90a74da444bf514f59d68bc32d61c83ac9667e87/modbusbackup.py#L31-L112"&gt;client code&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="server-code"&gt;Server code&lt;/h3&gt;
&lt;p&gt;The key to get this to work for me was having a Modbus instance being able to accept incoming messages and handling those directly, i.e. in an event-based way.
After digging through some PyModbus examples, I did find this &lt;a href="https://github.com/pymodbus-dev/pymodbus/blob/7c2aa3953951fe2caf10500210dbf424c70a67cd/examples/server_callback.py"&gt;callback server example&lt;/a&gt;.
I did end up with the following set of functions, definitions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CallbackDataBlock&lt;/span&gt;(pymodbus&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datastore&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ModbusSparseDataBlock):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;callbacks on operation&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; super()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;({index: &lt;span style="color:#ae81ff"&gt;0x0&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; index &lt;span style="color:#f92672"&gt;in&lt;/span&gt; _relay_map()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;keys()})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setValues&lt;/span&gt;(self, address: int, values: typing&lt;span style="color:#f92672"&gt;.&lt;/span&gt;List) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Got &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;values&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; for &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;address&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _trigger(address, values[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; super()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;setValues(address, values)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_run_server&lt;/span&gt;(port: str, timeout: float, baudrate: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CallbackDataBlock()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; store &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pymodbus&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datastore&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ModbusSlaveContext(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; di&lt;span style="color:#f92672"&gt;=&lt;/span&gt;block,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; co&lt;span style="color:#f92672"&gt;=&lt;/span&gt;block,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hr&lt;span style="color:#f92672"&gt;=&lt;/span&gt;block,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ir&lt;span style="color:#f92672"&gt;=&lt;/span&gt;block,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zero_mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pymodbus&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datastore&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ModbusServerContext(slaves&lt;span style="color:#f92672"&gt;=&lt;/span&gt;store, single&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pymodbus&lt;span style="color:#f92672"&gt;.&lt;/span&gt;server&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sync&lt;span style="color:#f92672"&gt;.&lt;/span&gt;StartSerialServer(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; framer&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pymodbus&lt;span style="color:#f92672"&gt;.&lt;/span&gt;transaction&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ModbusRtuFramer,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;port,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timeout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;timeout,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; baudrate&lt;span style="color:#f92672"&gt;=&lt;/span&gt;baudrate,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;_run_server&lt;/code&gt; code wraps starting up a serial, RTU, Modbus server.
The port to be provided to the server is simple the Linux device mentioned above, &lt;code&gt;/dev/ttyNS0&lt;/code&gt;.
As part of a Modbus server configuration, you need to provide the outline of the various Modbus addresses it exposes for the main Modbus instance to poll.
In this, case I did pass in a custom block, the &lt;code&gt;CallbackDataBlock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;CallbackDataBlock&lt;/code&gt; constructor sets up all required addresses based on the configuration (through the &lt;code&gt;_relay_map&lt;/code&gt; function).
The &lt;code&gt;_relay_map&lt;/code&gt; function keeps a (singleton) mapping of the relays for the given Modbus address.
In order to have callback-like handlers, you can override the &lt;code&gt;setValues&lt;/code&gt; method.
This method is called for any Modbus message for the address that is part of the function arguments.
At this point, I do have the message handled by another &lt;code&gt;_trigger&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_trigger&lt;/span&gt;(address: int, value: bool, host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://localhost&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Process incoming event&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Only check for rising edges since we&amp;#39;re dealing with lights&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; value:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Convert address to zero-based address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; relay &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _relay_map()[address]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KeyError&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Could not find relay for address &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;address&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _session()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;host&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/json/relay/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;relay&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;raise_for_status()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; requests&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exceptions&lt;span style="color:#f92672"&gt;.&lt;/span&gt;HTTPError &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; error:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Issue with API call: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;error&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current &lt;span style="color:#f92672"&gt;=&lt;/span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;json()[&lt;span style="color:#e6db74"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;][&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;KeyError&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;&amp;#34;Error reading current state&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Flip the bit from current by XOR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toggled &lt;span style="color:#f92672"&gt;=&lt;/span&gt; current &lt;span style="color:#f92672"&gt;^&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _session()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;host&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/json/relay/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;relay&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, json&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: str(toggled)})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;raise_for_status()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; requests&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exceptions&lt;span style="color:#f92672"&gt;.&lt;/span&gt;HTTPError &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; error:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Issue with API call: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;error&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Toggled relay &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;relay&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; to &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;toggled&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;_trigger&lt;/code&gt; function simply takes in the Modbus message and maps it again to a relay on the unipi output device.
For the purposes of light control, a simple toggle is enough, so the value is just used to see if the incoming event is actually for a rising edge, i.e. a &lt;code&gt;True&lt;/code&gt; value.
The current value is read first using evok; the new value is the toggled value (by XOR).&lt;/p&gt;
&lt;p&gt;Refer to this section for the full &lt;a href="https://github.com/mhemeryck/modbusbackup/blob/90a74da444bf514f59d68bc32d61c83ac9667e87/modbusbackup.py#L113-L216"&gt;server code&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="configuration-format"&gt;Configuration format&lt;/h3&gt;
&lt;p&gt;As configuration format, I did use YAML, similar to what I had before on home assistant.
The source code repository also holds a &lt;a href="https://github.com/mhemeryck/modbusbackup/blob/master/config.yaml"&gt;sample configuration file&lt;/a&gt; &amp;ndash; actually, my configuration file.
The format looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;index&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;input&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2_03&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kelder inkom inbouw&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;output&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2_01&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;index&lt;/code&gt; can be an arbitrary number &amp;ndash; yet it needs to be unique.
The &lt;code&gt;input&lt;/code&gt; relates to the digital input on the unipi unit connected to the inputs.
Similarly, the &lt;code&gt;output&lt;/code&gt; relates to the relay output on the other unipi unit.
The &lt;code&gt;name&lt;/code&gt; field is just there for documentation purposes.&lt;/p&gt;
&lt;p&gt;The automation part previously handled by home assistant now effectively becomes part of this configuration file.
The link between input and output is done by the unique &lt;code&gt;index&lt;/code&gt; field.
The input-side will map its digital input change to the index; this index is used as Modbus address.
On the other end, the output-side will map the incoming Modbus address to the its relay it needs to change.&lt;/p&gt;
&lt;p&gt;This also means that this configuration file needs to be shared between both the input and output side.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;While my home automation setup described in the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation series&lt;/a&gt; has been running OK for me for quite some time, I did want to be less dependent on the correct functioning of the home assistant instance and the network.&lt;/p&gt;
&lt;p&gt;To get there, I made changes on 2 levels.
On the &lt;strong&gt;hardware-side&lt;/strong&gt;, I did make use of the already existing RS-485 connection, removing any network-related issues.
On the &lt;strong&gt;software-side&lt;/strong&gt;, I did add a custom script to run on both the input and output side that essentially notifies the output-side of any input-related events.
The actual link between the input and output is through a simple configuration file, shared by both sides.&lt;/p&gt;
&lt;p&gt;I only did implement this connection for my &lt;strong&gt;lighting setup&lt;/strong&gt; since I did feel this is the most critical part for me to continue to keep on functioning.
For my alarm setup and my shades, all input and outputs are actually each on their own, same unipi unit.
This means that in order to decouple those from home assistant I could also write some custom code, but they would not need the whole RS-485 connection, nor any Modbus setup.&lt;/p&gt;
&lt;p&gt;Note that some push button-to-light connections still go through home assistant: I have e.g. some Philips Hue lights that get triggered via a push button change.
For this case, I think it still makes sense to keep this connection in home assistant.
However, once I would start to expand my DALI-bus based setup, I&amp;rsquo;m not yet sure how to tackle this.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;transition&lt;/strong&gt; between the fully home assistant-based outline and my current &amp;ldquo;modbusbackup&amp;rdquo; solution has gone pretty well though, to that extent that it&amp;rsquo;s not really clear to others in my home what I have actually been working on.
The main advantage I now see is being able to more easily make any changes on the home assistant / network side.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;the automation that was already in place in home assistant to link input to output would need to be removed though.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>evok2mqtt ansible</title><link>https://mhemeryck.xyz/posts/2021-08-09-evok2mqtt_ansible/</link><pubDate>Mon, 09 Aug 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-08-09-evok2mqtt_ansible/</guid><description>&lt;p&gt;As part of my &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation series&lt;/a&gt;, I did write about a small interfacing library that I did write, called &lt;a href="https://github.com/mhemeryck/evok2mqtt"&gt;evok2mqtt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The purpose of this service is to translate between two different protocols: websockets, as exposed by &lt;a href="https://github.com/UniPiTechnology/evok"&gt;evok&lt;/a&gt; and &lt;a href="https://mqtt.org/"&gt;mqtt&lt;/a&gt;, which is the primary protocol I did use in communicating with &lt;a href="https://www.home-assistant.io/integrations/mqtt/"&gt;home assistant&lt;/a&gt;.
If you&amp;rsquo;d like to get more details about the setup itself, check the blog post series first.&lt;/p&gt;
&lt;p&gt;The focus of this post is the &lt;strong&gt;automated provisioning of a unipi unit using &lt;a href="https://www.ansible.com/"&gt;ansible&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded>&lt;p&gt;As part of my &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation series&lt;/a&gt;, I did write about a small interfacing library that I did write, called &lt;a href="https://github.com/mhemeryck/evok2mqtt"&gt;evok2mqtt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The purpose of this service is to translate between two different protocols: websockets, as exposed by &lt;a href="https://github.com/UniPiTechnology/evok"&gt;evok&lt;/a&gt; and &lt;a href="https://mqtt.org/"&gt;mqtt&lt;/a&gt;, which is the primary protocol I did use in communicating with &lt;a href="https://www.home-assistant.io/integrations/mqtt/"&gt;home assistant&lt;/a&gt;.
If you&amp;rsquo;d like to get more details about the setup itself, check the blog post series first.&lt;/p&gt;
&lt;p&gt;The focus of this post is the &lt;strong&gt;automated provisioning of a unipi unit using &lt;a href="https://www.ansible.com/"&gt;ansible&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="rationale"&gt;Rationale&lt;/h2&gt;
&lt;p&gt;Earlier, I did discuss all of the software running on each of my &lt;a href="https://www.unipi.technology/"&gt;unipi&lt;/a&gt; units, where each part is responsible for a layer in the overall process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the linux-based OS: for capturing and sending I/O events&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evok&lt;/code&gt;: for exposing this I/O information in various formats and protocols (of which I did use websockets)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evok2mqtt&lt;/code&gt;: for translating between websockets and MQTT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Arguably, it would be better to have fewer of these layers, but the current situation is one of &lt;strong&gt;many different moving parts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Next to that, I do have &lt;strong&gt;multiple of these unipi nodes&lt;/strong&gt;, each interfacing different types of I/O, yet fundamentally they share a common setup.&lt;/p&gt;
&lt;p&gt;Occasionally, there is a need to &lt;strong&gt;update or reprovision&lt;/strong&gt; the unipi nodes, e.g. in the case of regular software or in the unlucky event that and SD-card crashed.&lt;/p&gt;
&lt;p&gt;Considering all of these factors, having a way to automatically provision the unipi units in a predefined state is very helpful to have.&lt;/p&gt;
&lt;h2 id="automated-provisioning"&gt;Automated provisioning&lt;/h2&gt;
&lt;p&gt;Automated provisioning refers to tooling that can handle the tedious tasks related to setting up a client or server automatically.
Many different kinds of tooling can be used, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scripting like &lt;a href="https://www.gnu.org/software/bash/"&gt;bash&lt;/a&gt;:&lt;/li&gt;
&lt;li&gt;declarative-style recipes like &lt;a href="https://www.ansible.com/"&gt;ansible&lt;/a&gt; and &lt;a href="https://puppet.com/"&gt;puppet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;immutable builds like &lt;a href="https://www.packer.io/"&gt;packer&lt;/a&gt; or &lt;a href="https://www.docker.com/"&gt;docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;infrastructure-as-code like &lt;a href="https://www.terraform.io/"&gt;terraform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Scripting&lt;/strong&gt; is one of the oldest and most widespread approaches to automation.
In scripting, you define a list of commands that each run in sequence after each other, generally from the top of the script to the bottom.
Scripting is &lt;em&gt;procedural&lt;/em&gt;, meaning you define &lt;em&gt;how&lt;/em&gt; a specific action is to be carried out, like a recipe.
The advantage of scripting is that it is simple and ubiquitous.
On the other side of the equation are all issues related to state manipulation.
As a script runs through its sequence of steps, it changes the state to end up in a desired target state.
In theory, you can write your scripts in such a way that they can adapt for any kind of initial or in-between state.
Also, you could attempt to write some parts of your scripts in an &lt;em&gt;idempotent&lt;/em&gt; way, meaning that they perform the same action regardless of their input.
At larger scale however, scripting quickly becomes unwieldy of this state manipulation it needs to take into account and idempotency is simply not always possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt; tools take a different approach, not by defining &lt;em&gt;how&lt;/em&gt; the provisioning should take place, but rather &lt;em&gt;what&lt;/em&gt; the end-result needs to be.
Particularly in the case of provisioning, this makes for an interesting programming paradigm since ideally after running the provisioning tool, it will always result in the same end state, regardless of any initial state the node was in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Immutable build&lt;/strong&gt; tools also operate in this fashion, the difference being that the provisioning process itself consists of picking a pre-built image (virtual machine image or container image) and deploying that entirely.
Doing updates in this fashion just means killing the entire image and replacing it with another one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure as code&lt;/strong&gt; augments this approach by extending it to any kind of resource.
Cloud providers make all of their virtualised infrastructure components available via APIs.
Infrastructure as code provides a common code-based framework to declaratively define your desired cloud architecture.&lt;/p&gt;
&lt;p&gt;All of these have their place, but for now, I did focus on using &lt;a href="https://www.ansible.com/"&gt;ansible&lt;/a&gt; as a declarative provisioning tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scripting is too much of a pain, considering the state manipulation issues mentioned before&lt;/li&gt;
&lt;li&gt;immutable build tools seem really interesting, but might not fit my use case where I would manually need to swap out built images&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;infrastructure as code: this does not apply here, since I want to run the tooling on the unipi unit itself&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;I recently did learn about ansible and also a good use case to apply it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some basic &lt;em&gt;ansible-lingo&lt;/em&gt; before we dive into the details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ansible&lt;/code&gt; is the tool you run on your local machine to perform actions on a remote system&lt;/li&gt;
&lt;li&gt;an action on a remote machine is a &lt;em&gt;task&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;a set of tasks can be combined into a &lt;em&gt;role&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;a set of roles can be further combined into a &lt;em&gt;playbook&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&amp;hellip; further higher levels of grouping also still exist (collections?), but I didn&amp;rsquo;t give that much extra thought&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ansible-playbook&lt;/code&gt; is the tool to orchestrate the execution of a playbook&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For my purposes, I did write a &lt;em&gt;playbook&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="initial-setup"&gt;Initial setup&lt;/h2&gt;
&lt;p&gt;Ansible has some basic initial conditions for it to be run on the target host:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the target host needs to be reachable via SSH (ideally key-based)&lt;/li&gt;
&lt;li&gt;the target host should run some version of python (as ansible itself is based on python)&lt;/li&gt;
&lt;li&gt;depending on the permissions you need to execute, the connecting user needs to have root access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fortunately, unipi does provides such an image that already satisfies these constraints (most modern linux-based OS images would do, actually): the &lt;a href="https://kb.unipi.technology/en:files:software:os-images:00-start#neuron_opensource_os"&gt;Neuron OpenSource OS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Practical next steps include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;flash the SD card for the unipi with this image&lt;/li&gt;
&lt;li&gt;ensure an internet connection, preferably wired, although a &lt;a href="https://www.raspberrypi.com/documentation/computers/configuration.html#connect-to-a-wireless-network"&gt;headless raspberry pi wifi install&lt;/a&gt; would also work&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From here on, you can start running the playbook!&lt;/p&gt;
&lt;h2 id="running-evok2mqtt-ansible"&gt;Running &lt;code&gt;evok2mqtt-ansible&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The playbook can be found on github &lt;a href="https://github.com/mhemeryck/evok2mqtt-ansible"&gt;evok2mqtt-ansible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From the README file:&lt;/p&gt;
&lt;p&gt;The first step is to clone the repository:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/mhemeryck/evok2mqtt-ansible.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need some extra requirements which can be installed using &lt;code&gt;ansible-galaxy&lt;/code&gt; (a tool to pull in other people&amp;rsquo;s playbooks).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible-galaxy install -r requirements.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adapt the &lt;code&gt;hosts.yaml&lt;/code&gt; file for your own purposes.&lt;/p&gt;
&lt;p&gt;Run the playbook with&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible-playbook site.yaml -i hosts.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="a-closer-look"&gt;A closer look&lt;/h2&gt;
&lt;p&gt;Taking a step back; what does this playbook actually do?
Let&amp;rsquo;s check the folder structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── hosts.yaml
├── LICENSE.txt
├── README.md
├── requirements.yaml
├── roles
│   ├── account
│   │   └── tasks
│   │   └── main.yaml
│   ├── evok2mqtt
│   │   ├── handlers
│   │   │   └── main.yaml
│   │   ├── tasks
│   │   │   └── main.yaml
│   │   └── templates
│   │   └── evok2mqtt.service.j2
│   ├── hostname
│   │   ├── tasks
│   │   │   └── main.yaml
│   │   └── vars
│   │   └── hostnames.yaml
│   ├── security
│   │   ├── handlers
│   │   │   └── main.yaml
│   │   └── tasks
│   │   └── main.yaml
│   └── wifi
│   ├── handlers
│   │   └── main.yaml
│   ├── tasks
│   │   └── main.yaml
│   └── templates
│   └── wpa_supplicant.conf.j2
└── site.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The playbook consists of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;site.yaml&lt;/code&gt; file which is the main entry point which invokes the different roles&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;hosts.yaml&lt;/code&gt; file which lists the groups of target hosts to run the commands against (you would definitely need to adapt this to your needs)&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;requirements.yaml&lt;/code&gt; file which defines external roles and plugins to be used&lt;/li&gt;
&lt;li&gt;5 different folders, each related to a specific role; a set of tasks that are available for execution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Discussing each of the roles themselves might be a bit too much (you can just was well read the source for that), but I want to discuss the main &lt;code&gt;site.yaml&lt;/code&gt; file which brings all of the roles together:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;hosts&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;testpi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;vars&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ansible_python_interpreter&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/usr/bin/python3&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Force py3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ansible_ssh_password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;unipi.technology&lt;/span&gt; &lt;span style="color:#75715e"&gt;# not really a secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ansible_ssh_user&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;unipi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mqtt_host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;shuttle.lan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;username&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mhemeryck&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;wifi_ssd&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;luctor_IoT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;wifi_passphrase&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{{ lookup(&amp;#39;community.general.passwordstore&amp;#39;, &amp;#39;home/luctor&amp;#39;) }}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;roles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;account&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;hostname&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;wifi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;evok2mqtt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;hosts&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;testpi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;vars&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ansible_python_interpreter&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/usr/bin/python3&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Force py3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;username&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mhemeryck&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;roles&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;security&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The main file defines &lt;strong&gt;two sets of roles&lt;/strong&gt;, referring to the different roles that are made available.
The first set is the biggest one, the second one can only run after the first has completed.
The reason for this split is that when the initial image is made available, it does include a default &lt;code&gt;unipi&lt;/code&gt; user with a fixed password.
The first set of roles will run as this user (as this is the only user available at that time).
Afterwards, we can run a second security-related role as a newly available hardened user and then remove that &lt;code&gt;unipi&lt;/code&gt; user &amp;ndash; you obviously can&amp;rsquo;t remove the &lt;code&gt;unipi&lt;/code&gt; user while you are executing tasks with it.
You can see that the first set of roles does include the &lt;code&gt;ansible_ssh_user&lt;/code&gt; and &lt;code&gt;ansible_ssh_password&lt;/code&gt;: these refer to those standard credentials.&lt;/p&gt;
&lt;p&gt;In terms of &lt;strong&gt;credentials&lt;/strong&gt;, I did include those ssh user / password as variables in plain text, as this publicly available knowledge.
For the &lt;code&gt;wifi_passphrase&lt;/code&gt; however, I did use my password manager &lt;a href="https://www.passwordstore.org/"&gt;&lt;code&gt;pass&lt;/code&gt;&lt;/a&gt; and the related ansible &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/passwordstore_lookup.html"&gt;&lt;code&gt;pass&lt;/code&gt; plugin&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;A quick look at the &lt;strong&gt;different roles&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;account&lt;/code&gt;: this provisions my own user with proper root access and copies in my public SSH keys from github&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hostname&lt;/code&gt;: this role changes the host name to a sensible one. The mapping for the host name is determined from a variables file&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wifi&lt;/code&gt;: this sets up &lt;code&gt;wpa_supplicant&lt;/code&gt; with proper WIFI credentials for my home network.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evok2mqtt&lt;/code&gt;: the main job; it will do a full system upgrade, install &lt;code&gt;evok&lt;/code&gt;, install &lt;code&gt;evok2mqt&lt;/code&gt;, create a system unit file and make sure it runs as a daemon&lt;/li&gt;
&lt;li&gt;&lt;code&gt;security&lt;/code&gt;: some cleanup tasks, like disabling password-based SSH logins (key-based only) and removing the default &lt;code&gt;unipi&lt;/code&gt; user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After running these tasks, the unipi unit should in theory be in my ideal end state!&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;Given the central place my unipi units play in my home automations setup, &lt;strong&gt;having any failures on them is, however rare, not fun&lt;/strong&gt;.
With this in mind and also a general interest in learning infrastructure automation, I did set out to automate those tasks with ansible.&lt;/p&gt;
&lt;p&gt;The current &lt;strong&gt;downside&lt;/strong&gt; of this approach is that the full system upgrade will mean that the unipi is occupied for a large portion of time.
Consequently, I haven&amp;rsquo;t yet set out to use it on my live environment.
However, any breaking failures on my unipi units could now be quickly mitigated by flashing an SD-card and running this playbook against, which is a huge improvement over the previous situation.&lt;/p&gt;
&lt;p&gt;Alternative &lt;strong&gt;future solutions&lt;/strong&gt; could consist of changing the software required to run on the unipi units (would simplify the install process) and / or fully immutable (packer) builds, which completely prepare the image upfront.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;in the cloud server world, this is of course an entirely different situation.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;a platform that could mimic infrastructure-as-code for local development would be really cool, though!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I did not include my own file, since this could contain sensitive information &amp;hellip;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Service</title><link>https://mhemeryck.xyz/posts/2021-08-03-home_automation_service/</link><pubDate>Tue, 03 Aug 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-08-03-home_automation_service/</guid><description>&lt;p&gt;Up to this point, I did already lay out all of the tidbits concerning the electricity, the I/O hardware units, even some custom software to provide an event-based API to address all of the various components in the overall system.
The final fundamental piece to bring everything together is what I call the &lt;em&gt;service&lt;/em&gt; layer, and specifically &lt;a href="https://www.home-assistant.io/"&gt;home assistant&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;</description><content:encoded>&lt;p&gt;Up to this point, I did already lay out all of the tidbits concerning the electricity, the I/O hardware units, even some custom software to provide an event-based API to address all of the various components in the overall system.
The final fundamental piece to bring everything together is what I call the &lt;em&gt;service&lt;/em&gt; layer, and specifically &lt;a href="https://www.home-assistant.io/"&gt;home assistant&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;
&lt;p&gt;To recap, have a look at this drawing from the earlier &lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;home automation architecture post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/architecture.png" alt="architecture"&gt;&lt;/p&gt;
&lt;p&gt;In summary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a central MQTT broker: this component takes in &lt;em&gt;events&lt;/em&gt; on a &lt;em&gt;topic&lt;/em&gt; from the various &lt;em&gt;publishers&lt;/em&gt; and forwards them to any of the &lt;em&gt;subscribers&lt;/em&gt; for that topic&lt;/li&gt;
&lt;li&gt;all the unipi units: can take in any MQTT event to handle as &lt;em&gt;commands&lt;/em&gt; and pushes out events as &lt;em&gt;state&lt;/em&gt; updates.&lt;/li&gt;
&lt;li&gt;home assistant: has logic to trigger specific events on other events incoming&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that for this publish / subscribe system, all of the clients can (and will) function as both publisher and subscriber.&lt;/p&gt;
&lt;h2 id="home-assistant"&gt;home assistant&lt;/h2&gt;
&lt;p&gt;I already did mention home assistant, but what is it really?&lt;/p&gt;
&lt;p&gt;Home assistant is an open source software platform that was created with the vision of being able to integrate all sorts of IoT solutions.&lt;/p&gt;
&lt;p&gt;It is bundled with a huge amount of &lt;em&gt;integrations&lt;/em&gt; for various different vendors; check out the &lt;a href="https://www.home-assistant.io/integrations/"&gt;home assistant integrations&lt;/a&gt; page for an overview.
The way the platform is structured is via a number of &lt;a href="https://developers.home-assistant.io/docs/core/entity/"&gt;entities&lt;/a&gt;, e.g. a generic switch, light, etc &amp;hellip;
Each integration can then implement such entities.
The home assistant community would typically encourage new contributors to put the interfacing logic (e.g. an API client) in its own, open source library, to be called from the custom integration.
The added value of this approach is twofold:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the community rapidly gets more of these open source libraries&lt;/li&gt;
&lt;li&gt;the home assistant installation only needs to pull in the source libraries it needs, instead of having of a big blob of code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The advantage of running your own platform is that you are more in control of your own hardware, less reliant on cloud solutions.&lt;/p&gt;
&lt;p&gt;Though not required, I did find it interesting to learn about more about the way home assistant itself is structured, check the &lt;a href="https://developers.home-assistant.io/docs/architecture_index"&gt;home assistant architecture dev docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The integration I have been mostly using is &lt;a href="https://www.home-assistant.io/integrations/mqtt/"&gt;home assistant MQTT integration&lt;/a&gt;.
At this point, this means that all of the I/O that is provided by the unipi units via an MQTT interface can now be readily represented as entities directly in home assistant!&lt;/p&gt;
&lt;h2 id="scenario"&gt;Scenario&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s make it more tangible by presenting the scenario from the &lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;home automation architecture post&lt;/a&gt; again, where a push button triggers a light.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/flow.png" alt="flow"&gt;&lt;/p&gt;
&lt;p&gt;In terms of the home assistant MQTT integration, the notion of &lt;em&gt;command&lt;/em&gt; and &lt;em&gt;state&lt;/em&gt; topics are quite relevant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;command topic: this is a specific MQTT topic, e.g. &lt;code&gt;/{device}/{io}/set&lt;/code&gt; which accepts and incoming command to update the I/O. This scenario is typically coming from home assistant to the IO module.&lt;/li&gt;
&lt;li&gt;state topic: this topic is specific for reporting state updates back to any listeners, e.g. &lt;code&gt;/{device}/{io}/state&lt;/code&gt;. This is for the opposite scenario where the IO module itself updates its state back again to home assistant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this pair of topics for each entity, a typical flow combining command and state topics means:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;home assistant requesting a state update on the command topic of the IO module&lt;/li&gt;
&lt;li&gt;the IO module updates its value (e.g. toggles a relay for a light)&lt;/li&gt;
&lt;li&gt;the IO module sends out an update on the corresponding state topic&lt;/li&gt;
&lt;li&gt;home assistant then updates its matching entity according to the state topic&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In relation to the flow diagram above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;on the push of the button, an MQTT event is pushed out to the &lt;em&gt;state&lt;/em&gt; topic&lt;/li&gt;
&lt;li&gt;the MQTT broker pushes the state update to home assistant&lt;/li&gt;
&lt;li&gt;home assistant updates the matching &lt;a href="https://www.home-assistant.io/integrations/switch.mqtt/"&gt;home assistant MQTT switch&lt;/a&gt; entity&lt;/li&gt;
&lt;li&gt;home assistant triggers an &lt;em&gt;automation&lt;/em&gt; which connects a state update for the push button entity to a matching light entity&lt;/li&gt;
&lt;li&gt;to update the light entity, an event is published on that &lt;a href="https://www.home-assistant.io/integrations/light.mqtt/"&gt;home assistant MQTT light&lt;/a&gt; entity&lt;/li&gt;
&lt;li&gt;the MQTT broker pushes the command to the subscribed I/O module&lt;/li&gt;
&lt;li&gt;the I/O module takes in the command to update its state and toggles the light accordingly&lt;/li&gt;
&lt;li&gt;after that, the I/O module triggers the relay, it pushes back an update on its state topic&lt;/li&gt;
&lt;li&gt;the MQTT broker forwards the state update again to home assistant&lt;/li&gt;
&lt;li&gt;home assistant sees the state update for that light entity and updates its internal state&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="containers-and-services"&gt;Containers and services&lt;/h2&gt;
&lt;p&gt;Home assistant has multiple ways of installing and running the platform, see the &lt;a href="https://www.home-assistant.io/installation/"&gt;home assistant installation&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;First of all, you should be aware that the overall platform consists of multiple layers and the meaning of each of those has changed over time.
I would refer to the following diagram from the &lt;a href="https://developers.home-assistant.io/docs/architecture_index"&gt;home assistant architecture dev docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://developers.home-assistant.io/img/en/architecture/full.svg" alt="home assistant layers img"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;core: at the center, there&amp;rsquo;s the main home assistant software.&lt;/li&gt;
&lt;li&gt;supervisor: this component manages the core software, but it also has the capability to run other services besides home assistant, yet integrating them in a single UI&lt;/li&gt;
&lt;li&gt;operating system: the way I understand this, this is a minimal linux-based OS just to be able to run the supervisor for you. The idea is that at this level, you can just download a disk image, flash an SD-card and fire it up in a raspberry pi.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The core itself is actually a &lt;em&gt;containerized&lt;/em&gt; version of the main home assistant software, which in turn is a python application.
Check out the &lt;a href="https://hub.docker.com/r/homeassistant/home-assistant"&gt;home assistant core docker container&lt;/a&gt; on docker hub.
The rationale behind containerization is that you specify an image that contains an isolated environment for your application to run in.
Containers are different from &lt;em&gt;virtual machines&lt;/em&gt; in that multiple containers would share the same underlying OS kernel, which is better in terms of performance.&lt;/p&gt;
&lt;p&gt;Containerization has become dominant in current-day software development and infrastructure management; I use it on a daily basis.
A lot can be said on the topic of containerization and IT infrastructure, but I would like to keep this out of the discussion here.
If you would be considering gaining more knowledge on this, before anything, understand that containers are there to support a particular model of software development, i.e. &lt;em&gt;service-based&lt;/em&gt; development.
This typically means &lt;em&gt;stateless&lt;/em&gt;, &lt;em&gt;web-based&lt;/em&gt;, &lt;em&gt;services&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Apart from the container itself, you typically need something like a &lt;em&gt;supervisor&lt;/em&gt; or &lt;em&gt;container orchestrator&lt;/em&gt; to run the containers.
The orchestrator is responsible for e.g. starting the containers, making sure the correct amount of containers are running at the same time (redundancy, scaling), moving containers between hosts (in a multi-node setup), resource allocation, external networking, &amp;hellip;&lt;/p&gt;
&lt;p&gt;As mentioned, home assistant has its own orchestrator; the supervisor.
An orchestrator I often use for quick-and-dirty local development is &lt;a href="https://docs.docker.com/compose/"&gt;docker-compose&lt;/a&gt;.
The current de-facto industry standard is the google-backed &lt;a href="https://kubernetes.io/"&gt;kubernetes&lt;/a&gt;, abbreviated to k8s.
The lightweight version of this is &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt;.
This probably not the end of the list, others like &lt;a href="https://www.nomadproject.io/"&gt;hashicorp nomad&lt;/a&gt; also exist.&lt;/p&gt;
&lt;p&gt;All of these have their own merits and disadvantages:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;orchestrator&lt;/th&gt;
&lt;th&gt;pros&lt;/th&gt;
&lt;th&gt;cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;home assistant supervisor&lt;/td&gt;
&lt;td&gt;built into the eco-system of home assistant&lt;/td&gt;
&lt;td&gt;container orchestration is &lt;em&gt;hard&lt;/em&gt; to do right, why roll your own?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;allows extra functionality in the same UI&lt;/td&gt;
&lt;td&gt;to me, this indicates a bad design, where a supervisor is responsible for more than what it&amp;rsquo;s intended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;supposedly more suited for embedded platforms (raspi 3)&lt;/td&gt;
&lt;td&gt;low-memory footprint k8s alternatives exist, like the k3s mentioned before.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docker-compose&lt;/td&gt;
&lt;td&gt;simple syntax&lt;/td&gt;
&lt;td&gt;only supports a subset of features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;easy to get up an running&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;k8s&lt;/td&gt;
&lt;td&gt;the de-facto standard&lt;/td&gt;
&lt;td&gt;standard for web-based services and IT infrastructure management, might be overkill for IoT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;a lot of features, e.g. zero-downtime deployments!&lt;/td&gt;
&lt;td&gt;lots of concepts to grasp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Over the course of time, I think I have actually ran home assistant in all of these configurations (even including directly from source).
Like everything in IT, there is no real &lt;em&gt;right&lt;/em&gt; or &lt;em&gt;wrong&lt;/em&gt; solution, it depends on your specific situation.
My current approach is based on k3s &amp;ndash; but this is better left as the topic of a future post.&lt;/p&gt;
&lt;h2 id="sample-setup"&gt;Sample setup&lt;/h2&gt;
&lt;p&gt;All of the theoretical babble about containers and orchestration is just to come to this point where I can actually show something to run.
For this sample setup, I will build part of the service layer using only &lt;code&gt;docker-compose&lt;/code&gt; and a set of related home assistant configuration files.&lt;/p&gt;
&lt;p&gt;The final folder structure will look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;├── automations.yaml
├── blueprints
│   ├── automation
│   │   └── homeassistant
│   │   ├── motion_light.yaml
│   │   └── notify_leaving_zone.yaml
│   └── script
│   └── homeassistant
│   └── confirmable_notification.yaml
├── configuration.yaml
├── deps
├── docker-compose.yaml
├── groups.yaml
├── home-assistant.log
├── scenes.yaml
├── scripts.yaml
├── secrets.yaml
├── tts
└── ui-lovelace.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For those interested, have a look at &lt;a href="https://mhemeryck.xyz/2021-08-03/hass.zip"&gt;&lt;code&gt;hass.zip&lt;/code&gt;&lt;/a&gt; for all of the source files.&lt;/p&gt;
&lt;p&gt;Most of these files were actually generated automatically when home assistant started the first time.
The format for all of the most relevant files is YAML, which stands for &amp;ldquo;Yet Another Markup Language&amp;rdquo;.
YAML is fairly widespread nowadays as a format for configuration files.
To me, one of the most obvious advantages of the format is its conciseness and readability.
Indentation matters though, so be sure you use a proper (plain text) editor when editing in this format.&lt;/p&gt;
&lt;p&gt;I will go over the most important files in here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;configuration.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ui-lovelace.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="docker-composeyaml"&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This the main file that will start all of the required containers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;homeassistant&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;homeassistant/home-assistant:2021.6.3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;8123:8123&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;.:/config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;links&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;mosquitto&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;db&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mosquitto&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;eclipse-mosquitto:1.6.14&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;1883:1883&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;db&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;postgres:13.3-alpine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;POSTGRES_DB&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;POSTGRES_USER&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;POSTGRES_PASSWORD&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;hass-db:/var/lib/postgresql/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hass-db&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file defines 3 &lt;em&gt;services&lt;/em&gt;, &lt;code&gt;homeassistant&lt;/code&gt; (the main home assistant service), &lt;code&gt;mosquitto&lt;/code&gt; (an MQTT broker) and &lt;code&gt;db&lt;/code&gt; (a database &amp;ndash; although this isn&amp;rsquo;t strictly required).
Some notes on the keys used in the configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ports&lt;/strong&gt;: this is a mapping of the ports within the container to the outside world, e.g. &lt;code&gt;&amp;quot;8123:8123&amp;quot;&lt;/code&gt; means &amp;ldquo;maps the port 8123 inside the container to the host on port 812 &amp;ldquo;. Port 8123 is the port home assistant runs on by default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;volumes&lt;/strong&gt;: this is mapping of the folder structure inside the container to the outside host. For the &lt;code&gt;homeassistant&lt;/code&gt; service, this means to map the configuration folder to the location where &lt;code&gt;docker-compose&lt;/code&gt; is executed. This also means it&amp;rsquo;s easy to put your own configuration inside the container this way. Volumes needn&amp;rsquo;t be mapped to an explicit host folder though, see the example for the &lt;code&gt;db&lt;/code&gt; service where there&amp;rsquo;s just a named volume called &lt;code&gt;hass-db&lt;/code&gt;, managed by docker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;links&lt;/strong&gt;: links take care of the networking between containers. A link from &lt;code&gt;homeassistant&lt;/code&gt; to &lt;code&gt;mosquitto&lt;/code&gt; means that the home assistant instance will be able to reach that other service with the name &lt;code&gt;mosquitto&lt;/code&gt;. &lt;code&gt;docker-compose&lt;/code&gt; takes care of creating the proper docker network for that. It also means that if the &lt;code&gt;homeassistant&lt;/code&gt; service is started, the other 2 services are also automatically started.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, you can actually already just fire up home assistant using &lt;code&gt;docker-compose up&lt;/code&gt;!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker-compose up
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Starting hass_mosquitto_1 ... &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Starting hass_db_1 ... &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Starting hass_homeassistant_1 ... &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Attaching to hass_mosquitto_1, hass_db_1, hass_homeassistant_1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:56.893 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: starting PostgreSQL 13.3 on x86_64-pc-linux-musl, compiled by gcc &lt;span style="color:#f92672"&gt;(&lt;/span&gt;Alpine 10.2.1_pre1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 10.2.1 20201203, 64-bit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:56.893 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on IPv4 address &lt;span style="color:#e6db74"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;, port &lt;span style="color:#ae81ff"&gt;5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:56.893 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on IPv6 address &lt;span style="color:#e6db74"&gt;&amp;#34;::&amp;#34;&lt;/span&gt;, port &lt;span style="color:#ae81ff"&gt;5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:56.902 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: listening on Unix socket &lt;span style="color:#e6db74"&gt;&amp;#34;/var/run/postgresql/.s.PGSQL.5432&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:56.912 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;21&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: database system was interrupted; last known up at 2021-06-08 20:37:13 UTC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mosquitto_1 | 1623352796: mosquitto version 1.6.14 starting
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mosquitto_1 | 1623352796: Config loaded from /mosquitto/config/mosquitto.conf.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mosquitto_1 | 1623352796: Opening ipv4 listen socket on port 1883.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mosquitto_1 | 1623352796: Opening ipv6 listen socket on port 1883.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mosquitto_1 | 1623352796: mosquitto version 1.6.14 running
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:57.219 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;21&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: database system was not properly shut down; automatic recovery in progress
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:57.227 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;21&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: redo starts at 0/1920BF0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:57.231 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;21&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: invalid record length at 0/1931478: wanted 24, got &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:57.231 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;21&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: redo &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; at 0/1931450
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;s6-init&lt;span style="color:#f92672"&gt;]&lt;/span&gt; making user provided files available at /var/run/s6/etc...exited 0.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_1 | 2021-06-10 19:19:57.298 UTC &lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; LOG: database system is ready to accept connections
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;s6-init&lt;span style="color:#f92672"&gt;]&lt;/span&gt; ensuring user provided files have correct perms...exited 0.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;fix-attrs.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; applying ownership &amp;amp; permissions fixes...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;fix-attrs.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;cont-init.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; executing container initialization scripts...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;cont-init.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; udev.sh: executing...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;cont-init.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; udev.sh: exited 0.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;cont-init.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;services.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; starting services
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;homeassistant_1 | &lt;span style="color:#f92672"&gt;[&lt;/span&gt;services.d&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should actually have created the other configuration files for you.&lt;/p&gt;
&lt;h3 id="configurationyaml"&gt;&lt;code&gt;configuration.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The next file to discuss is the main home assistant configuration file.&lt;/p&gt;
&lt;p&gt;Here is my final adapted version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Configure a default setup of Home Assistant (frontend, api, etc)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;default_config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Text to speech&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;platform&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;google_translate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;group&lt;/span&gt;: !&lt;span style="color:#ae81ff"&gt;include groups.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt;: !&lt;span style="color:#ae81ff"&gt;include scripts.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;scene&lt;/span&gt;: !&lt;span style="color:#ae81ff"&gt;include scenes.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;lovelace&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;recorder&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;db_url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;postgresql://hass:hass@db/hass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;mqtt&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;broker&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mosquitto&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Entities representing push buttons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;switch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;platform&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mqtt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;study room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command_topic&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tesla/input/2_01/set&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;state_topic&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;tesla/input/2_01/state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Entities representing lights&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;light&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;platform&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mqtt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;study room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command_topic&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;edison/relay/2_05/set&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;state_topic&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;edison/relay/2_05/state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;optimistic&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt; &lt;span style="color:#75715e"&gt;# only needed because of demo now&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Automation to link the two together&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;automation&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;alias&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Toggle study room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;trigger&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;platform&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;entity_id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;switch.study_room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;off&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;on&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;service&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;light.toggle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;entity_id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;light.study_room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The top part of the file contains mostly &lt;strong&gt;boilerplate code&lt;/strong&gt; that was added by homeassistant itself, e.g. the &lt;code&gt;default_config&lt;/code&gt; and &lt;code&gt;tts&lt;/code&gt;.
The &lt;code&gt;group&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt; and &lt;code&gt;scene&lt;/code&gt; blocks were also added automatically and use the &lt;code&gt;!include&lt;/code&gt; keyword, which home assistant would interpret as including those other yaml files within this main configuration file.
When you have a large number of entities to add, this would be the typical way to structure them.
For the discussion here, I did not do that though.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;recorder&lt;/code&gt;&lt;/strong&gt; part is about the &lt;a href="https://www.home-assistant.io/integrations/recorder/#custom-database-engines"&gt;home assistant database integration&lt;/a&gt;.
This contains the connection URL (including the credentials).
If you compare this again with the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, you will see those credentials which were set from the &lt;code&gt;environment&lt;/code&gt; key for the &lt;code&gt;db&lt;/code&gt; postgres service.
In a proper setup, you&amp;rsquo;d use the &lt;a href="https://www.home-assistant.io/docs/configuration/secrets/"&gt;secrets system&lt;/a&gt;, but as the database integration isn&amp;rsquo;t that important here, I did not do that.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;mqtt&lt;/code&gt;&lt;/strong&gt; integration is quite relevant here.
This should point to your MQTT broker.
Checking &lt;code&gt;docker-compose.yaml&lt;/code&gt;, this service was called &lt;code&gt;mosquitto&lt;/code&gt; and should be reachable with just thus name from inside the docker container network&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The following entry is a configuration for a &lt;strong&gt;push button&lt;/strong&gt; using the &lt;a href="https://www.home-assistant.io/integrations/switch.mqtt/"&gt;home assistant MQTT switch&lt;/a&gt; integration.
It minimally needs the integration platform, the name (which is relevant for later referring to the entity) and a command and state topic, the meaning of which was described earlier.&lt;/p&gt;
&lt;p&gt;Next would be the matching &lt;strong&gt;light&lt;/strong&gt; which we would like to have triggered whenever the push button is toggled, based on the &lt;a href="https://www.home-assistant.io/integrations/light.mqtt/"&gt;home assistant MQTT light&lt;/a&gt; integration.
It also has the MQTT platform, name (which matches now, but needn&amp;rsquo;t really), command and state topic.
I also configured the light to be in &lt;code&gt;optimistic&lt;/code&gt; mode.
This means the that the state of the light internally in home assistant is updated regardless of changes on the state topic.
I did this because this is a dummy setup and this light is not linked to an actual device that will acknowledge commands with a matching update on the state topic.
In my real setup, this is obviously omitted.&lt;/p&gt;
&lt;p&gt;At this point, both push button and light are known to home assistant, but they are not linked.
The &lt;strong&gt;automation&lt;/strong&gt; takes care of that.
The general outline of an &lt;a href="https://www.home-assistant.io/docs/automation/basics/"&gt;home assistant automation&lt;/a&gt; is:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;| trigger | when the switch state changes |
| condition | and it&amp;#39;s nighttime |
| action | then toggle the light |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;condition&lt;/code&gt; part is optional for the automation, and for the example given here quite nonsensical indeed.
Important here is that we only allow toggles when the push buttons gets toggled from off to on, not the other way around &amp;ndash; otherwise if would follow the push buttons state, which means the light would only be on for the duration the button is pressed.
The &lt;code&gt;light.toggle&lt;/code&gt; is an action that is built-in to home assistant and does just take: if the light was off, it will turn it on, and vice versa.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href="https://www.home-assistant.io/docs/configuration/"&gt;home assistant configuration&lt;/a&gt; for more extensive documentation and further links.&lt;/p&gt;
&lt;h3 id="ui-lovelaceyaml"&gt;&lt;code&gt;ui-lovelace.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This file contains the visual outline for home assistant.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;views&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Home&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;icon&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;mdi:home&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;entities&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Entities&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;entities&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;switch.study_room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;light.study_room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;automation.toggle_study_room&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This just makes the demo a bit easier to look at.&lt;/p&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see it in action!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-08-03/demo.gif" alt="demo"&gt;&lt;/p&gt;
&lt;p&gt;The top left terminal just shows the &lt;code&gt;docker-compose&lt;/code&gt; output, containing the aggregated log output of the different services.
The right hand part shows the simplified home assistant UI, containing (top to bottom) the switch state, the light state and the automation.
The middle left terminal uses the &lt;a href="https://mosquitto.org/man/mosquitto_sub-1.html"&gt;&lt;code&gt;mosquitto_sub&lt;/code&gt;&lt;/a&gt; tool to read all events from the broker; the left one subscribes to &lt;code&gt;tesla/#&lt;/code&gt; meaning any topic below &lt;code&gt;tesla/&lt;/code&gt; (for the push button) and the right for &lt;code&gt;edison/&lt;/code&gt; (the light).&lt;/p&gt;
&lt;p&gt;The bottom left terminal is used to simulate the push of a button by sending out events directly to the push button&amp;rsquo;s state topic, using another tool called &lt;a href="https://mosquitto.org/man/mosquitto_pub-1.html"&gt;&lt;code&gt;mosquitto_pub&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The commands themselves are:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mosquitto_pub -h localhost -t tesla/input/2_01/state -m ON
mosquitto_pub -h localhost -t tesla/input/2_01/state -m OFF
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each of the lines represent a push to &lt;em&gt;host&lt;/em&gt; &amp;ldquo;&lt;code&gt;h&lt;/code&gt;&amp;rdquo; &lt;code&gt;localhost&lt;/code&gt;, on &lt;em&gt;topic&lt;/em&gt; &amp;ldquo;&lt;code&gt;t&lt;/code&gt;&amp;rdquo; &lt;code&gt;tesla/input/2_01/state&lt;/code&gt; with &lt;em&gt;message&lt;/em&gt; &amp;ldquo;&lt;code&gt;m&lt;/code&gt;&amp;rdquo; &lt;code&gt;ON&lt;/code&gt; or &lt;code&gt;OFF&lt;/code&gt;.
By rapidly firing them off shortly after each other, this simulates the push of a button (first on, then off again).&lt;/p&gt;
&lt;p&gt;When the push button state updates are pushed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;home assistant updates the internal state of the push button entity&lt;/li&gt;
&lt;li&gt;the automation kicks in, and updates the matching light entity is toggled&lt;/li&gt;
&lt;li&gt;the light entity sends out the update on the command topic&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Apart from controlling the light with the push button, it can also be controlled directly from home assistant; when toggling the light entity directly in home assistant, it just sends out the command on the MQTT topic directly.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;This concludes my approach to using (abusing?) home assistant and the MQTT integration as the central part of my home automation setup.&lt;/p&gt;
&lt;p&gt;I did use MQTT as the standard to centralize all my components on, have a look at the other posts presented in the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt; of how to get there.
Examples of other components that use MQTT in the same fashion are window contacts, PIR, smoke and water detectors, motorized blinds, etc.&lt;/p&gt;
&lt;p&gt;The advantage of home assistant though is that it does not need to all be the same protocol, so it is quite easy to add other integrations in the mix as well.
A notable example of this is an automation where I link one of these push buttons to a &lt;a href="https://www.home-assistant.io/integrations/hue/"&gt;Philips Hue light&lt;/a&gt;; as far as home assistant is concerned, both of these are light entities.&lt;/p&gt;
&lt;p&gt;A disadvantage of using home assistant this way and by extent the overall approach is the &lt;em&gt;centralized&lt;/em&gt; nature of it.
Home assistant needs to be online all the time for everything to work.
Initially, I did run home assistant using &lt;code&gt;docker-compose&lt;/code&gt;, which was quite stable.
However, doing updates was never fun, since it meant that one container needs to go down before the next comes up, leaving in a short (but stressful) period of downtime.
Using k3s as the supervisor certainly fixed that.
Given that the majority of my interfacing hardware is actually all on MQTT, I might at some point add another service in the mix that mimics the automation engine as a backup.&lt;/p&gt;
&lt;p&gt;A similar argument can be made about the &lt;code&gt;mosquitto&lt;/code&gt; broker.
Currently, I actually run that as a dedicated service on the host itself.
Alternative solutions such as &lt;a href="https://www.emqx.com/en/blog/emqx-mqtt-broker-k8s-cluster"&gt;emqx&lt;/a&gt; exist though, which would run MQTT in a high-availability mode (multiple, load balanced instances).&lt;/p&gt;
&lt;p&gt;Currently though, the system works quite fine and everyone at home has gotten so accustomed to it, they only really notice when not being at home.
The backlog of improvements and extra bells and whistles is still quite long, so I&amp;rsquo;ll hope to write more on this later!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;the &lt;code&gt;mosquitto&lt;/code&gt; version used in the &lt;code&gt;docker-compose.yaml&lt;/code&gt; version was explicitly pinned to a version &amp;lt; 2.x, which did not enforce authentication, hence why the broker configuration just needs a host name to connect to.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Software</title><link>https://mhemeryck.xyz/posts/2021-07-27-home_automation_software/</link><pubDate>Tue, 27 Jul 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-07-27-home_automation_software/</guid><description>&lt;p&gt;After connecting everything on the different &amp;ldquo;physical&amp;rdquo; layers, it is time to have a look at how to connect all on the &lt;strong&gt;software&lt;/strong&gt; side of things.
This post shall describe my current setup, specifically the different software components and the protocols they rely on for the unipi hardware.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;</description><content:encoded>&lt;p&gt;After connecting everything on the different &amp;ldquo;physical&amp;rdquo; layers, it is time to have a look at how to connect all on the &lt;strong&gt;software&lt;/strong&gt; side of things.
This post shall describe my current setup, specifically the different software components and the protocols they rely on for the unipi hardware.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;p&gt;Particularly relevant is the &lt;a href="https://mhemeryck.xyz/posts/2021-07-20-home_automation_hardware/"&gt;hardware&lt;/a&gt; post, since I my software builds on the unipi neuron platform.&lt;/p&gt;
&lt;h2 id="software"&gt;Software&lt;/h2&gt;
&lt;p&gt;The following diagram shows the different software &lt;em&gt;layers&lt;/em&gt;, going from the kernel which polls the IO-boards to eventually pushing out / pulling in MQTT events.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-27/software.png" alt="software layers"&gt;&lt;/p&gt;
&lt;h3 id="kernel-polling-spi"&gt;Kernel: polling SPI&lt;/h3&gt;
&lt;p&gt;Unipi provides their own &lt;strong&gt;&lt;a href="https://kb.unipi.technology/en:files:software:os-images:00-start#neuron_opensource_os"&gt;neuron open source OS&lt;/a&gt;&lt;/strong&gt;, which is a modified version of &lt;a href="https://www.raspberrypi.com/software/operating-systems/"&gt;raspbian&lt;/a&gt; including custom kernel drivers to poll the I/O boards over &lt;a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface"&gt;SPI&lt;/a&gt;.
The kernel driver makes the data available via the &lt;a href="https://simplymodbus.ca/"&gt;modbus&lt;/a&gt; protocol.
The modbus protocol is a widely accepted, open industry standard for interfacing with PLCs.
I believe you would typically use it in a setup where you have one main controller and a series of follower controllers - which is pretty much the situation you would have using SPI.
Note that the unipi units also have an &lt;a href="https://en.wikipedia.org/wiki/RS-485"&gt;RS-485&lt;/a&gt; connection, commonly used for daisy chaining multiple controllers.&lt;/p&gt;
&lt;p&gt;Apart from the modbus interface, unipi also provides a &lt;strong&gt;&lt;a href="https://kb.unipi.technology/en:sw:02-apis:04-sysfs"&gt;sysfs interface&lt;/a&gt;&lt;/strong&gt;, which essentially maps the I/O states and controls to a number of files in a fixed file system structure.
I have used this sysfs interface in another project, &lt;a href="https://github.com/mhemeryck/unipitt"&gt;unipitt&lt;/a&gt;, but since I am currently not using this one, I will not discuss this further.&lt;/p&gt;
&lt;h3 id="evok-modbus-to-web-apis"&gt;Evok: modbus to web APIs&lt;/h3&gt;
&lt;p&gt;Unipi also provides an open source library called &lt;strong&gt;&lt;a href="https://github.com/UniPiTechnology/evok"&gt;evok&lt;/a&gt;&lt;/strong&gt; that will periodically poll the underlying modbus interface and make it available via all sorts of common web API formats and protocols: JSON / REST, JSONRPC, SOAP, &amp;hellip;
See the &lt;a href="https://evok.api-docs.io/1.0/jkctke5arbcnjt8az"&gt;evok API docs&lt;/a&gt; for an extensive overview of all possible interfaces.&lt;/p&gt;
&lt;p&gt;The most interesting interface provided by evok is the &lt;a href="https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket"&gt;evok websockets interface&lt;/a&gt;, since this is the only interface that can also trigger actions for particular events.
This is a definite requirement here, since you&amp;rsquo;d need to be able toggle the relays fast in case of an incoming request.&lt;/p&gt;
&lt;p&gt;For the &lt;em&gt;receiving&lt;/em&gt; of messages (I/O board → outside) you would need to register a number of callback functions to the websocket.
The arguments to the callback function then hold information about the specific I/O action that triggered them.
For the &lt;em&gt;sending&lt;/em&gt; of messages (outside → I/O board), you can just send a structured message directly.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a python snippet directly taken from the &lt;a href="https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket"&gt;evok websockets interface&lt;/a&gt; docs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; websocket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;url &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ws://your.unipi.ip.address/ws&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on_message&lt;/span&gt;(ws, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loads(message)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; circuit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;circuit&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(message)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on_error&lt;/span&gt;(ws, error):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on_close&lt;/span&gt;(ws):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;Connection closed&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# receiving messages&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws &lt;span style="color:#f92672"&gt;=&lt;/span&gt; websocket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WebSocketApp(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; on_message&lt;span style="color:#f92672"&gt;=&lt;/span&gt;on_message,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; on_error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;on_error,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; on_close&lt;span style="color:#f92672"&gt;=&lt;/span&gt;on_close,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run_forever()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sending messages&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws &lt;span style="color:#f92672"&gt;=&lt;/span&gt; websocket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WebSocket()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(url)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws&lt;span style="color:#f92672"&gt;.&lt;/span&gt;send(&lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;cmd&amp;#34;:&amp;#34;set&amp;#34;,&amp;#34;dev&amp;#34;:&amp;#34;relay&amp;#34;,&amp;#34;circuit&amp;#34;:&amp;#34;3&amp;#34;,&amp;#34;value&amp;#34;:&amp;#34;1&amp;#34;}&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ws&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="evok2mqtt-websocket-to-mqtt"&gt;&lt;code&gt;evok2mqtt&lt;/code&gt;: websocket to MQTT&lt;/h3&gt;
&lt;p&gt;The final layer is to &lt;strong&gt;translate the websocket &amp;ldquo;events&amp;rdquo; from and to MQTT events&lt;/strong&gt;.
This translation is required since this is the standard message-based system interface that was chosen to interface with &lt;a href="https://www.home-assistant.io/"&gt;home assistant&lt;/a&gt;.
In order to implement this, I did create a small application called &lt;a href="https://github.com/mhemeryck/evok2mqtt"&gt;evok2mqtt&lt;/a&gt;.
The application is written in python3 and uses the &lt;code&gt;websockets&lt;/code&gt; library for interfacing with the evok websocket and &lt;a href="https://pypi.org/project/paho-mqtt/"&gt;&lt;code&gt;paho-mqtt&lt;/code&gt;&lt;/a&gt; to do the same thing for MQTT.
This application runs alongside evok on the unipi neuron itself.
Note that the application could in theory also run elsewhere on the network as the websocket and the MQTT broker can just be addressed over the network.
Given that the program itself was actually quite simple and fun to write, I will outline how it works with some example code snippets.&lt;/p&gt;
&lt;p&gt;To express actions that need to occur on the receiving of messages, the MQTT library works pretty similar to the websockets library using callback functions, see e.g. &lt;a href="https://github.com/mhemeryck/evok2mqtt/blob/96bf7e19063b15e96522fac038fab89959f16475/evok2mqtt/__init__.py#L158-L195"&gt;this evok2mqtt &lt;code&gt;on_message&lt;/code&gt; snippet&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;on_message&lt;/span&gt;(client, userdata, message):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Callback for MQTT events&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Incoming MQTT message for topic &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;topic&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; with payload &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;payload&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;MQTT},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; MQTT_COMMAND_TOPIC_REGEX&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;(message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;topic)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; device_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;device_name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; circuit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;group(&lt;span style="color:#e6db74"&gt;&amp;#34;circuit&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; device_name &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DEVICE_NAME:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;warning(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Handling incoming message for device &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, expected &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; device_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DEVICE_NAME,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;MQTT},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Update state topic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;publish(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MQTT_TOPIC_FORMAT&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; device_name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;device_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dev&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dev,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; circuit&lt;span style="color:#f92672"&gt;=&lt;/span&gt;circuit,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hass_action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HASS_ACTION&lt;span style="color:#f92672"&gt;.&lt;/span&gt;STATE,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;payload,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Send to websocket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; message&lt;span style="color:#f92672"&gt;.&lt;/span&gt;payload &lt;span style="color:#f92672"&gt;==&lt;/span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;MQTT_PAYLOAD_ON &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Push to output &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;dev&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;circuit&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;value&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; asyncio&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run(_ws_trigger(dev, circuit, value))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This snippet essentially details all actions that occur for incoming events from the MQTT broker (originating from home assistant):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the message is logged&lt;/li&gt;
&lt;li&gt;the message topic is checked to ensure whether we should process this message further&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;the state of the I/O is updated to acknowledge to home assistant that the update went OK&lt;/li&gt;
&lt;li&gt;a call is issued to the websocket to do the update on the I/O&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the opposite action, a websocket event that needs to be translated to MQTT, &lt;a href="https://github.com/mhemeryck/evok2mqtt/blob/96bf7e19063b15e96522fac038fab89959f16475/evok2mqtt/__init__.py#L127-L137"&gt;a similar callback function&lt;/a&gt; is implemented.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_ws_loop&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Main loop polling incoming events from websockets&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Connecting to &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET_URI,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; websockets&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(_settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET_URI) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; websocket:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; websocket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;recv()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; _ws_process(payload)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This snippet sets up the websocket loop that will continually check the websocket for newly incoming messages.
Note that this uses and async-await on the websocket, a feature provided in python&amp;gt;3.5&amp;rsquo;s &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio&lt;/a&gt; library.
The key thing to understand about asyncio here is that even though this is an infinite loop, during the waiting times for new data to come in from the websocket (or more generally, during the waiting time for &lt;em&gt;any&lt;/em&gt; I/O), the thread scheduler can yield control to another thread to execute concurrently.&lt;/p&gt;
&lt;p&gt;The function &lt;a href="https://github.com/mhemeryck/evok2mqtt/blob/96bf7e19063b15e96522fac038fab89959f16475/evok2mqtt/__init__.py#L100-L124"&gt;&lt;code&gt;_ws_process&lt;/code&gt;&lt;/a&gt; that processes the incoming payload is next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_ws_process&lt;/span&gt;(payload):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Process incoming websocket payload, push to MQTT&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loads(payload)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;debug(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Incoming message for websocket &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, obj, extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; topic &lt;span style="color:#f92672"&gt;=&lt;/span&gt; MQTT_TOPIC_FORMAT&lt;span style="color:#f92672"&gt;.&lt;/span&gt;format(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; device_name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;_settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DEVICE_NAME,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dev&lt;span style="color:#f92672"&gt;=&lt;/span&gt;obj[&lt;span style="color:#e6db74"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; circuit&lt;span style="color:#f92672"&gt;=&lt;/span&gt;obj[&lt;span style="color:#e6db74"&gt;&amp;#34;circuit&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hass_action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HASS_ACTION&lt;span style="color:#f92672"&gt;.&lt;/span&gt;STATE,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;MQTT_PAYLOAD_ON
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; obj[&lt;span style="color:#e6db74"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; _settings()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;MQTT_PAYLOAD_OFF
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _mqtt_client()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;publish(topic, payload&lt;span style="color:#f92672"&gt;=&lt;/span&gt;payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;MQTT publish &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt; to topic &lt;/span&gt;&lt;span style="color:#e6db74"&gt;%s&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; topic,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extra&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: LOG_KIND&lt;span style="color:#f92672"&gt;.&lt;/span&gt;WEBSOCKET},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The operations it does, are quite straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;load the object representation from the incoming payload (as JSON).&lt;/li&gt;
&lt;li&gt;construct the MQTT topic based on the object name and circuit.&lt;/li&gt;
&lt;li&gt;construct the MQTT payload from the object payload&lt;/li&gt;
&lt;li&gt;publish the MQTT payload to the topic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There you have it: translation from-and-to websockets / MQTT.
Note that this translation covers both inputs (push buttons) as well as outputs (relays).&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;At this point, with all of these layers of software in between, the unipi neuron unit provides a standard MQTT-based interface to home assistant.&lt;/p&gt;
&lt;p&gt;The main downside I&amp;rsquo;ve felt at this point is that the reaction speed from the &lt;code&gt;evok&lt;/code&gt; interface is sometimes a bit too slow to react quickly enough to push button toggles.
On rare occasions, this can result in push buttons events being missed &amp;ndash; which most of the times can be easily fixed by pushing the buttons a bit longer.&lt;/p&gt;
&lt;p&gt;Also, having multiple of these translation layers makes the overall solution a bit more complex and will surely also add to the latency.&lt;/p&gt;
&lt;p&gt;I do intend at some point to have another look at this setup.
One option is to look at the (open source) implementation of evok and replace the async parts there with the more modern asyncio library.
However, if performance is key, I will more likely have a look again at the sysfs interface and my &lt;a href="https://github.com/mhemeryck/unipitt"&gt;unipitt&lt;/a&gt; project, which uses &lt;a href="https://golang.org/"&gt;golang&lt;/a&gt; under the hood.&lt;/p&gt;
&lt;p&gt;Nonetheless, this current setup has been working quite OK for me thus far.&lt;/p&gt;
&lt;h2 id="update-from-unipi"&gt;Update from unipi&lt;/h2&gt;
&lt;p&gt;A while after this post went live, I did get some &lt;a href="https://forum.unipi.technology/post/5807"&gt;friendly&lt;/a&gt; &lt;a href="https://forum.unipi.technology/post/5824"&gt;feedback&lt;/a&gt; on &lt;a href="https://forum.unipi.technology/topic/1410/home-assistant-diy-setup-blog"&gt;my unipi forum post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some selected parts of the notes I think I should share&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;evok&lt;/code&gt; might get an update, &lt;em&gt;including MQTT support&lt;/em&gt;!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Evok is planned (well, maybe &amp;ldquo;wished&amp;rdquo; is a better word:)) to be rewritten to Python 3 and to a multi-threaded application.
Along with that, major changes to the API will happen.
We will abandon some of the less used APIs (RPC and SOAP probably) and definitely add native MQTT support since it is a driving force in the industry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Missed state changes should be possible to detect with the digital input counter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The missed state changes of the DI can be detected by checking the state of the DI counter.
The counter is implemented in the HW and can count as fast as 10kHz signals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The low-level communication I did outline using SPI and modbus is slightly different from what I did present above:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And a bit of explanation of the low level communication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The HW boards are connected over SPI&lt;/li&gt;
&lt;li&gt;The protocol on the SPI level is modified ModbusRTU&lt;/li&gt;
&lt;li&gt;The Unipi kernel module (part of the &lt;code&gt;unipi-kernel-modules&lt;/code&gt; package) exposes this communication channel as &lt;code&gt;/dev/unipispi device&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The Unipi ModbusTCP server (part of the &lt;code&gt;unipi-modbus-tools&lt;/code&gt;) exposes this as a ModbusTCP server running on TCP/502&lt;/li&gt;
&lt;li&gt;The rest is correct. Evok polls the ModbusTCP server as fast as it can. And creates a &amp;ldquo;system image&amp;rdquo; of the HW state.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The IO boards might also get a firmware-level upgrade!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And in the firmware of the IOs we plan to introduce a &amp;ldquo;latching&amp;rdquo; state of the DIs.
Those will be registers and coils which will hold the leading edge of the DIs until they are read from the software. That will prevent the losing of the edge altogether.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I really appreciate this kind of feedback from the unipi team.
It is nice to see how they are continuously improving their product (e.g. the idea of including native MQTT support).
More importantly, I think this company really understands well the value of their product as an open platform and is actively working on community-building.
Like I did mention in some of my earlier posts in this series, this openness is one of the reasons I am really glad about their products!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;this could actually also be solved using configuration on the MQTT client setup, by only registering callbacks to certain topics.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;mostly literal, some formatting changes mine.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Hardware</title><link>https://mhemeryck.xyz/posts/2021-07-20-home_automation_hardware/</link><pubDate>Tue, 20 Jul 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-07-20-home_automation_hardware/</guid><description>&lt;p&gt;In this post, I will give some more details about the rationale for the &lt;strong&gt;hardware&lt;/strong&gt; that I had chosen for interpreting the input signals and controlling the outputs, specifically the &lt;a href="https://www.unipi.technology/products/unipi-neuron-3?categoryId=2&amp;amp;categorySlug=unipi-neuron"&gt;unipi neuron series&lt;/a&gt;.
Note that I am in no way affiliated with unipi, nor do I intend to do an in-depth description of their hardware.
For more details from unipi themselves, have a look at the &lt;a href="https://www.unipi.technology/"&gt;unipi&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;</description><content:encoded>&lt;p&gt;In this post, I will give some more details about the rationale for the &lt;strong&gt;hardware&lt;/strong&gt; that I had chosen for interpreting the input signals and controlling the outputs, specifically the &lt;a href="https://www.unipi.technology/products/unipi-neuron-3?categoryId=2&amp;amp;categorySlug=unipi-neuron"&gt;unipi neuron series&lt;/a&gt;.
Note that I am in no way affiliated with unipi, nor do I intend to do an in-depth description of their hardware.
For more details from unipi themselves, have a look at the &lt;a href="https://www.unipi.technology/"&gt;unipi&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;h2 id="inside"&gt;Inside&lt;/h2&gt;
&lt;p&gt;The following picture shows what is actually &lt;strong&gt;inside such a unit&lt;/strong&gt;: a &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-3-model-b-plus/"&gt;raspberry pi 3B+&lt;/a&gt; main controller board and then (depending on the neuron series you have), 1 to 3 I/O boards, connected via &lt;a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface"&gt;SPI&lt;/a&gt; &amp;ndash; Serial Peripheral Interface.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;SPI interface&lt;/strong&gt; works by connecting a number of nodes in a series, &lt;em&gt;daisy-chained&lt;/em&gt;, on the same SPI-line(s).
The raspberry pi functions as the main &lt;em&gt;server&lt;/em&gt; node, the I/O boards are the &lt;em&gt;clients&lt;/em&gt;.
The server nodes periodically &lt;em&gt;selects&lt;/em&gt; one of the clients to become active.
Consequently, the selected client becomes active and puts its data on the SPI-lines that were made available.
Other clients remain inactive for that duration of time they were not selected.
Afterwards, the server moves on to the next client.
This process continues until all clients have been consulted, at which point the cycle concludes and a new one starts.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-20/unipi-hardware.png" alt="unipi inside"&gt;&lt;/p&gt;
&lt;p&gt;The main controller board has all the other &lt;strong&gt;functionalities&lt;/strong&gt; the raspberry pi has, including bluetooth, wifi, wired ethernet, USB ports, and even a HDMI interface&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.
The I/O boards either feature digital inputs, relay outputs, analog inputs, analog outputs or a combination of these.
Additionally, there are connections like a RS-485 serial lines (e.g. for interfacing with modbus) as well as a connector for a 1-wire bus (e.g. for temperature sensors).&lt;/p&gt;
&lt;h2 id="rationale"&gt;Rationale&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://kb.unipi.technology/_media/en:hw:neuron_m103-top.jpg" alt="unipi module"&gt;&lt;/p&gt;
&lt;p&gt;Considering the hardware platform, I think the unipi platform provides a number of &lt;strong&gt;advantages&lt;/strong&gt; I couldn&amp;rsquo;t find with other vendors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;open platform&lt;/strong&gt;: the main controller board is a raspberry pi 3B+, meaning you can run any software on it you would want.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;software support&lt;/strong&gt;: next to the open platform, they also provide open source OS images as well as software libraries to extend yourself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;local&lt;/strong&gt;: obviously, it runs from my local network inside of my home.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;wired&lt;/strong&gt;: the I/O interfaces use standard voltages, for instance the power supply to the unit uses 24V, the digital inputs use 24V, the relay outputs can switch 240V&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;, &amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;form factor&lt;/strong&gt;: the modules come in form factors of 4, 8 and 12 DIN rail modules and fit nicely on a household DIN rail in the electric cabinet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;wiring connectors&lt;/strong&gt;: related to the form factor, it is quite easy to connect a large amount of I/O in a relatively small space.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;low cost&lt;/strong&gt;: the units themselves do not come that cheap, but calculated as a &lt;em&gt;cost per I/O&lt;/em&gt;, they are quite OK compared to other solutions like industrial PLCs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;community&lt;/strong&gt;: while working on the units and my own custom software, I would often reach out to the &lt;a href="https://forum.unipi.technology/"&gt;unipi community forum&lt;/a&gt;. They often reply quite quickly and have been a great help!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;connectivity&lt;/strong&gt;: apart from the I/O, the unipi units come with a lot of extra connectivity options such as wifi, wired ethernet, bluetooth, RS-485, one-wire, &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="specific-units"&gt;Specific units&lt;/h2&gt;
&lt;p&gt;I have number of different unipi units in my setup, since I have a large amount of functionalities to support, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;push button read-out&lt;/li&gt;
&lt;li&gt;light control (relays)&lt;/li&gt;
&lt;li&gt;alarm system (window / door contacts, PIR detector, &amp;hellip; but also on the output side an indoor-and outdoor siren).&lt;/li&gt;
&lt;li&gt;shades (relays)&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While you can have both inputs and outputs on the same unipi unit, I did decide to dedicate a bigger unipi unit for the push button control and another one for the lights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lights, relay control: &lt;a href="https://www.unipi.technology/unipi-neuron-l403-p102"&gt;unipi neuron l403&lt;/a&gt;, which has up to 56 relay outputs&lt;/li&gt;
&lt;li&gt;push button read-out: the &lt;a href="https://www.unipi.technology/unipi-neuron-l303-p101"&gt;unipi neuron l303&lt;/a&gt;. This was an upgraded version of the &lt;a href="https://www.unipi.technology/unipi-neuron-m303-p98"&gt;unipi neuron m303&lt;/a&gt; with up to 64 digital inputs, but at the time of writing, it seems this model is no longer supported.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="unipi-axon"&gt;Unipi axon&lt;/h2&gt;
&lt;p&gt;As a side note, I also have a &lt;strong&gt;unipi axon S605&lt;/strong&gt; for &lt;em&gt;DALI light control&lt;/em&gt;.
The &lt;a href="https://www.unipi.technology/products/unipi-axon-135?categoryId=13&amp;amp;categorySlug=unipi-axon"&gt;unipi axon series&lt;/a&gt; is another series of PLCs from the same brand.
It differs mainly in the controller board it features: instead of being based on raspberry pi, it is based on an Allwinner H5 ARM processor.
Also, instead of having the main hard drive on a removable flash memory, it has internal eMMC memory.&lt;/p&gt;
&lt;p&gt;I did not look into the rest of the axon series as it wasn&amp;rsquo;t available at the time when I acquired them.
Additionally, I also favored the neuron series because I have more familiarity with the raspberry platform in general.&lt;/p&gt;
&lt;p&gt;DALI light control is something I haven&amp;rsquo;t yet fully deployed, but the advantage is that you can have more all-digital control of lights &amp;ndash; wherever the light fixtures support it.
The main feature I do like about DALI is the ability to dim lights directly from the LED driver that already needs to perform the AC / DC conversion, making it more effective.&lt;/p&gt;
&lt;p&gt;Note however that this specific unit also isn&amp;rsquo;t able anymore, I suspect due to low demand.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;This was just a short description mainly about &lt;em&gt;why&lt;/em&gt; I did choose unipi neuron as the main hardware platform for my home automation.&lt;/p&gt;
&lt;p&gt;Note that this sort of hardware is particularly suited for a wired, &lt;a href="https://en.wikipedia.org/wiki/Network_topology#Star"&gt;star topology&lt;/a&gt; setup.
If I were to name a &lt;strong&gt;disadvantage&lt;/strong&gt;, it would be that working with this kind of hardware requires a lot of &lt;em&gt;tedious manual wiring&lt;/em&gt; which also takes up a lot of &lt;em&gt;cabinet space&lt;/em&gt;.
This is however more of an issue of the overall setup, not the hardware itself.
The alternative is a bus-system, where all of the signaling is done on a shared bus.
The disadvantage here is either all of the hardware on the bus is more complex and costly (e.g. KNX, DALI) and / or that you are tied to a single hardware vendor.
Nonetheless, DALI (and maybe even KNX) is something I would still like to examine a bit more in detail at some point in the future.&lt;/p&gt;
&lt;p&gt;Another issue I have faced is with the &lt;strong&gt;removable flash drives&lt;/strong&gt; as these tend to go &lt;em&gt;corrupt after many read / write cycles&lt;/em&gt;.
Again, this is more of an issue of the raspberry pi and flash drives in general, but it could still pose an issue for the reliability of the overall system.
After changing the flash drives to a more durable pSLC SD card, I can confirm that I haven&amp;rsquo;t faced any issues with corrupt flash drives lately (fingers crossed!)
Check the &lt;a href="https://kb.unipi.technology/en:hw:02-neuron:suitable-sd-card"&gt;unipi SD card reference&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;Overall, I have been very happy with the unipi hardware platform though!&lt;/p&gt;
&lt;p&gt;For more commercial details check the &lt;a href="https://www.unipi.technology/"&gt;unipi&lt;/a&gt; main website.
On the technical side, check out the &lt;a href="https://kb.unipi.technology/en:hw:02-neuron"&gt;unipi kb&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;the HDMI interface isn&amp;rsquo;t readily exposed&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;for highly inductive loads, like motors, it is recommended to switch the loads not directly, but instead via an intermediary set of relays&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Electrical cabinet</title><link>https://mhemeryck.xyz/posts/2021-07-13-home_automation_cabinet/</link><pubDate>Tue, 13 Jul 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-07-13-home_automation_cabinet/</guid><description>&lt;p&gt;In earlier posts, I did provide an overview of the architecture of my custom-made home automation system.
I also did describe some specificities related to a &lt;em&gt;wired&lt;/em&gt; home automation setup.
For my system, I decide on wiring everything in a &lt;em&gt;star-configuration&lt;/em&gt;, meaning all wiring around the house for lights, push buttons, &amp;hellip; all converges in a &lt;strong&gt;centralized electrical cabinet&lt;/strong&gt;.
The focus of this post is to show you some details of what this cabinet looks like and some pointers for practical issues I had run into.&lt;/p&gt;</description><content:encoded>&lt;p&gt;In earlier posts, I did provide an overview of the architecture of my custom-made home automation system.
I also did describe some specificities related to a &lt;em&gt;wired&lt;/em&gt; home automation setup.
For my system, I decide on wiring everything in a &lt;em&gt;star-configuration&lt;/em&gt;, meaning all wiring around the house for lights, push buttons, &amp;hellip; all converges in a &lt;strong&gt;centralized electrical cabinet&lt;/strong&gt;.
The focus of this post is to show you some details of what this cabinet looks like and some pointers for practical issues I had run into.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;h2 id="inside-the-cabinet"&gt;Inside the cabinet&lt;/h2&gt;
&lt;p&gt;Given the star-configuration for all of the lights, the amount of wiring in the central electricity cabinet quickly adds up.
The following picture shows a work-in-progress view while laying out the cables:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/cabinet-wip.jpg" alt="cabinet-wip"&gt;&lt;/p&gt;
&lt;p&gt;Be sure you have a consistent &lt;em&gt;labelling scheme&lt;/em&gt; while pulling all of the cable.
Looking back at these pictures, I think it is a bit of miracle I did not miss anything &amp;hellip;&lt;/p&gt;
&lt;h3 id="terminal-clamps"&gt;Terminal clamps&lt;/h3&gt;
&lt;p&gt;Besides from labelling the cables during the pulling of the cables, I also did use &lt;strong&gt;terminal clamps&lt;/strong&gt;, like the &lt;a href="https://www.wago.com/global/electrical-interconnections/discover-rail-mount-terminal-blocks"&gt;WAGO rail-mount terminal blocks&lt;/a&gt;.
On one end of such a block, you have one or more terminals where you can connect your wires.
The other end on this block can then later be used to connect whatever you need.&lt;/p&gt;
&lt;p&gt;The next figure details the terminal clamps I did use, the &lt;a href="https://www.wago.com/global/rail-mount-terminal-blocks/multilevel-installation-terminal-block/p/2003-7642"&gt;WAGO TOPJOB S 2003-7642&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/wago.png" alt="wago"&gt;&lt;/p&gt;
&lt;p&gt;The details include the nominal voltage and current ratings, wire gauges, the physical width of the clamp itself, etc.
The lines next to the clamp indicate the &lt;strong&gt;different clamp variants&lt;/strong&gt; available.
The first 2 variants are 2 &amp;ldquo;stories&amp;rdquo; high (2 electrical connections on a single clamp) whereas the bottom 2 feature 3 &amp;ldquo;stories&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Each &lt;strong&gt;story&lt;/strong&gt; represents a single electrical connection, isolated from the other stories.
The number of stories all have to do with saving space inside the cabinet: 2 stories really mean you need only half the amount of clamps.
Additionally, since most mains voltage wiring is for single phase electrical connection, you can group the phase and neutral wire in one such clamp.&lt;/p&gt;
&lt;p&gt;Aside from the first row indicating the details about the terminal clamp, the figure also details many different variants of &lt;strong&gt;jumpers&lt;/strong&gt; available.
These jumpers can be placed in the middle ends of the clamps and provide a connection between the different clamps, which facilitates making electrical parallel connections.&lt;/p&gt;
&lt;p&gt;Functionally, these terminal clamps does not do all that much.
In terms of costs, a single block is not that expensive but since the amount of blocks you need quickly adds up, so do the costs.
Nonetheless, &lt;strong&gt;I would still highly recommend it&lt;/strong&gt; to keep a clear overview while inside of your cabinet.&lt;/p&gt;
&lt;h3 id="relays"&gt;Relays&lt;/h3&gt;
&lt;p&gt;The next picture shows 2 DIN-rails with all of the terminal blocks for my lights.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/cabinet-clamps.jpg" alt="cabinet-clamps"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top row: termination of phase (brown) and neutral (blue) wires.&lt;/li&gt;
&lt;li&gt;bottom row: termination of 2 more wires which I intend to use as a [DALI] bus&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;; DALI+ (grey) and DALI- (black).&lt;/li&gt;
&lt;li&gt;jumpers: middle of terminal blocks&lt;/li&gt;
&lt;li&gt;orange spacers: each set of blocks between these spacers represent a single electric circuit. This makes it easier to map the one-wire diagram to the actual wiring inside the cabinet.&lt;/li&gt;
&lt;li&gt;green / yellow wires: these are terminated separately on the copper bar (visible between the two DIN rails) and directly connected to the main earth wire.&lt;/li&gt;
&lt;li&gt;two-level terminal blocks: the terminal blocks used here have 2 levels, i.e. the outer 2 terminals are connected as well as the inner 2. A multitude of such terminal blocks exists, ranging for single to multiple levels as well as different functionalities (built-in diodes, resistors, &amp;hellip;). Here, two-level terminal blocks were used mostly because of space constraints.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that at this point that the wires from the light fixtures in the house were just connected to these terminal blocks: having these blocks there means you can later still have all &lt;strong&gt;flexibility&lt;/strong&gt; for connecting them elsewhere.&lt;/p&gt;
&lt;p&gt;The next part is connecting them to the unit that contains the relays themselves.
The &lt;a href="https://kb.unipi.technology/en:hw:02-neuron:description-of-io:03-description-of-ro"&gt;unipi relay outputs KB&lt;/a&gt; lists all of the details for connecting a single relay:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kb.unipi.technology/_media/en:hw:010_connection_of_io.png" alt="unipi relay outputs img"&gt;&lt;/p&gt;
&lt;p&gt;This connection in itself is not all that complex for a &lt;em&gt;single&lt;/em&gt; relay.
Since multiple lights on the same circuit are connected to the same &lt;strong&gt;circuit breaker&lt;/strong&gt;, there is the practical concern of how to ensure these are all properly connected in parallel while still maintaining again a good overview.
The following picture shows another set of terminal blocks that I did use for this (orange blocks on the left of the unipi unit):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/cabinet-unipi.jpg" alt="cabinet-unipi"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;phase wire (brown): the terminal block holders (orange, left from the unipi module) hold a set of terminal blocks which have one end connected to the circuit breaker, the other ends serve to &lt;em&gt;distribute&lt;/em&gt; the voltage to multiple relays.&lt;/li&gt;
&lt;li&gt;neutral wire (blue): since all neutral wires of a single circuit are placed next to each other, these can just be connected in parallel using a jumper.&lt;/li&gt;
&lt;li&gt;relay: each of the lights are connected to &amp;ldquo;RO&amp;rdquo; connections coming from the unipi module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="push-buttons"&gt;Push buttons&lt;/h3&gt;
&lt;p&gt;Next to the series of clamps for terminating the cables for the lights, I also did use the same type of clamps for &lt;strong&gt;terminating the SVV analog signaling cable&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/cabinet-clamps-svv.jpg" alt="cabinet-clamps-svv"&gt;&lt;/p&gt;
&lt;p&gt;At the top, the &lt;strong&gt;incoming signaling cable&lt;/strong&gt; is terminated.
Spacers are placed series of clamps which belong to the same signaling cable run.
The bottom part then connects each of the signaling wires to a digital input somewhere lower in the cabinet.
In the middle of the clamps, you can see a number of overlapping jumpers.
These jumpers have been set up such that they only make an electrical contact in a fixed number of slots, in this case for connecting one of the wires for each cable run to 24V.&lt;/p&gt;
&lt;p&gt;As before, the following image shows the electrical connection from the signaling cable to the unipi digital input unit.
The &lt;code&gt;DIGND&lt;/code&gt; corresponds to the ground level of your 24VDC power source.
The &lt;code&gt;DI&lt;/code&gt; level is the signaling cable to one of the push buttons.
Pushing a button means the 24V level gets connected to the signal cable and thus giving a &lt;code&gt;DI&lt;/code&gt; level of 24V.
For more technical details, consult the &lt;a href="https://kb.unipi.technology/en:hw:02-neuron:description-of-io:01-description-of-di"&gt;unipi digital input KB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kb.unipi.technology/_media/en:hw:001_connection_of_io.png" alt="unipi digital inputs img"&gt;&lt;/p&gt;
&lt;p&gt;As can be seen from the following picture, making the physical connection between the incoming signal wires and the unipi units is easier than for the relay unit, shown earlier.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-13/cabinet-unipi-di.jpg" alt="cabinet-unipi-di"&gt;&lt;/p&gt;
&lt;p&gt;There is no need to have dedicated circuit breakers for the different signal wire cable runs; all of them can run on the same 24VDC power source.
Also, since the wire diameter is much lower, the screw connectors can just be used directly to terminate multiple wires and effectively provide electrical parallel connections.
Since each of the green screw connectors could have up to 4 signal wire connections and 1 ground level, I did use 4-wire SVV cable to connect them to terminal clamps higher up in the cabinet.
Note that I also did use my color coding again here (red / yellow / blue / white).&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;I started this post with some scary pictures of electrical wiring spaghetti.&lt;/p&gt;
&lt;p&gt;However, it turns out that with proper &lt;strong&gt;planning&lt;/strong&gt;, &lt;strong&gt;schematics&lt;/strong&gt; and some &lt;strong&gt;determination&lt;/strong&gt;, it was actually quite doable to wire the full cabinet together.
&lt;strong&gt;Terminal blocks&lt;/strong&gt; also help a lot in this process: it makes it easier to visually separate everything, removes the need to do all of the wiring in the beginning and enables rewiring at some later point in time.&lt;/p&gt;
&lt;p&gt;As before in the series, I did try to explain how to make the connection for &lt;strong&gt;push buttons&lt;/strong&gt; (inputs) and &lt;strong&gt;relays&lt;/strong&gt; (outputs).
The connections for the inputs were a bit simpler in the sense that the wiring runs on a safer lower voltage of 24VDC.
For the relays, as it runs on mains voltage, there are the additional constraints of the need to add in a circuit breaker for each electrical circuit as well as a higher wire gauge.
Consequently, I did some extra clamps to ensure the proper connections.&lt;/p&gt;
&lt;p&gt;I did opt for this approach at the time, since it seemed the most simple to me (comes closest to a &amp;ldquo;classical&amp;rdquo; setup), the most flexible in terms of rewiring (all wires are within reach) and does not depend on some proprietary, more expensive closed-off bus system.
In hindsight, the main &lt;strong&gt;disadvantage&lt;/strong&gt; of such a central star-configuration is the excessive amount of wiring you need to deal with.
For costs, you have the additional costs of the wiring material itself.
In terms of work, I did do all of the connections myself, so even though I did not lose any money on that, it surely was a very long, laborious process I might rather avoid in the future.
With that in mind, at some point in the future I will surely have a closer look at bus-based systems.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;DALI is a bus system for digital light control. In theory, you could do full light control with DALI only (removing the need for relay-based control), but this means all of your light fixtures need to be DALI-aware.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Schematics</title><link>https://mhemeryck.xyz/posts/2021-07-06-home_automation_schematics/</link><pubDate>Tue, 06 Jul 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-07-06-home_automation_schematics/</guid><description>&lt;p&gt;In previous posts, I did discuss the importance of &lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;planning and design&lt;/a&gt; as well as details on the &lt;a href="https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/"&gt;physical wires&lt;/a&gt; to be used.&lt;/p&gt;
&lt;p&gt;Here, I&amp;rsquo;d like to focus on two types of schematics which are not only very useful, but also mandatory for doing the electrical wiring for a Belgian house: the &lt;strong&gt;one-wire diagram&lt;/strong&gt; and the &lt;strong&gt;floor plan&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;</description><content:encoded>&lt;p&gt;In previous posts, I did discuss the importance of &lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;planning and design&lt;/a&gt; as well as details on the &lt;a href="https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/"&gt;physical wires&lt;/a&gt; to be used.&lt;/p&gt;
&lt;p&gt;Here, I&amp;rsquo;d like to focus on two types of schematics which are not only very useful, but also mandatory for doing the electrical wiring for a Belgian house: the &lt;strong&gt;one-wire diagram&lt;/strong&gt; and the &lt;strong&gt;floor plan&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;h2 id="one-wire-diagram"&gt;One-wire diagram&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;one-wire diagram&lt;/strong&gt; is an easy representation of all of the circuits and its related consumers and actors in an electrical installation.
As its name implies, it does not show each of the individual wires of the installation but rather how the cables are laid out logically.
Initially, this diagram seemed a bit strange to me in the sense that it seems overly simple and does not take the different conductors into account, i.e. the neutral, phases, earth wire &amp;hellip;
From the point of view of an installer, this diagram actually does make a lot of sense and includes all the details you need.&lt;/p&gt;
&lt;p&gt;Note that I no longer make the distinction between push buttons and relays, as was the case in the previous posts.&lt;/p&gt;
&lt;p&gt;According to my local regulations, it is not required and even discouraged to include low voltage wiring on the one-wire diagram since it adds to the overall complexity of the diagram.
This does have some implications on the representation of light-related circuits though.&lt;/p&gt;
&lt;p&gt;In a &amp;ldquo;traditional&amp;rdquo; light circuit, a light toggle switch forms a direct mechanical connection to the mains voltage, either through terminating one (one-pole connection) or two (two-pole connection) of the conductors in the wire.
Two-pole connections are considered more safe and are even mandatory for humid environments.
In terms of the one-wire diagram, the diagram would reflect which toggle switch would connect to which light.&lt;/p&gt;
&lt;p&gt;For my setup however, none of the lights are directly connected to the push buttons as these run on their own circuit of 24VDC, which is not on the diagram.
Given the push button assignment is controlled by software, it might not even make sense to include them in this diagram.
As for safety, since the push button is nowhere near the physical mains voltage cabling, this obviously much safer.&lt;/p&gt;
&lt;p&gt;To illustrate, here&amp;rsquo;s a small annotated (red) part of my installation&amp;rsquo;s one-wire diagram:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-06/onewire.png" alt="onewire"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The bottom left corner holds the entry-point from the street level where the main cable enters the house. In this case, it is a 400V &lt;a href="https://en.wikipedia.org/wiki/Three-phase_electric_power"&gt;three-phase connection&lt;/a&gt;: 3 phase wires plus 1 neutral&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;Straight after entering the house, the main cable passes through the utility company&amp;rsquo;s power meter.&lt;/li&gt;
&lt;li&gt;Main residual current device, ΔI=300 mA. This means that if the circuit breaker detects a leakage current ≥ 300 mA, it will automatically shut down the entire installation. Note that for Belgium, the 300 mA is mandatory for the overall installation, for circuits that pass through more humid environments (e.g. bathrooms, toilets), an additional 30 mA residual-current device is required.&lt;/li&gt;
&lt;li&gt;At this level, all of the various circuits are placed in parallel, each with their own circuit breaker.&lt;/li&gt;
&lt;li&gt;Wire specs: note that the circuit breakers need to match the requirements of the wires and the related consumers, as indicated in the &lt;a href="https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/"&gt;physical wires&lt;/a&gt; post.&lt;/li&gt;
&lt;li&gt;This level just illustrates two kinds of consumers; first a power socket, then a generic consumer.&lt;/li&gt;
&lt;li&gt;Another example with emergency lighting.&lt;/li&gt;
&lt;li&gt;An example circuit with a 240VAC / 24VDC transformer.&lt;/li&gt;
&lt;li&gt;An example circuit for my solar panel installation.&lt;/li&gt;
&lt;li&gt;This is a circuit exclusively containing lights: each of the branches indicates which lights are wired against the same control point. This means that each of the branches are switched using the same switch, or in this case relay. Note that this does not relate to how they are wired electrically: typically everything is wired in parallel&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;This is another example of a circuit with lights.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="floor-plan"&gt;Floor plan&lt;/h2&gt;
&lt;p&gt;Next to the one-wire diagram, there&amp;rsquo;s an additional diagram that lays out the circuits and their consumers on a floor plan.
Low voltage wiring is also not required for the official floor plan, but it does make sense to have it mapped out somewhere, even if that would be on a separate diagram.
The push buttons are obviously also on this plan.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-07-06/layout.png" alt="layout"&gt;&lt;/p&gt;
&lt;p&gt;Essentially, it just re-uses the numbering from the one-wire diagram presented earlier, so I won&amp;rsquo;t discuss these further.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;Even though these diagrams only represent part of the information, they proved to be really valuable to me along the way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;planning: since there&amp;rsquo;s a finite number of consumers you can add to a circuit, it forces you to think ahead of the number of consumers on a single wire run.&lt;/li&gt;
&lt;li&gt;installation: on the construction site, you really don&amp;rsquo;t want to spend too much time (re)thinking your setup.&lt;/li&gt;
&lt;li&gt;posterity: in case anything breaks down (e.g. a circuit breaker switching off), you just want an easy to digest overview of the whole thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;the voltage between each phase wire and the neutral wire is 240V&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;although it &lt;em&gt;could&lt;/em&gt; be wired in series, e.g. for a series of current-controlled LED fixtures&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Wiring</title><link>https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/</link><pubDate>Tue, 29 Jun 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/</guid><description>&lt;p&gt;In this post, I will take a deeper dive into the bottom layer, the &lt;strong&gt;wiring&lt;/strong&gt;.
Different kinds of inputs and outputs can be discussed, but for the sake of simplicity, I will focus on a push button operating a lamp.
These components will also come back later on in the series.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;</description><content:encoded>&lt;p&gt;In this post, I will take a deeper dive into the bottom layer, the &lt;strong&gt;wiring&lt;/strong&gt;.
Different kinds of inputs and outputs can be discussed, but for the sake of simplicity, I will focus on a push button operating a lamp.
These components will also come back later on in the series.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts!&lt;/p&gt;
&lt;p&gt;I will cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;caution: safety first&lt;/li&gt;
&lt;li&gt;principle: operating principle behind a light push button and relay&lt;/li&gt;
&lt;li&gt;conductors: the physical wires and cables themselves&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-mandatory-word-of-caution-"&gt;A mandatory word of caution (!)&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/danger.jpg" alt="danger"&gt;&lt;/p&gt;
&lt;p&gt;This post will touch a number of points where working with mains voltage is required.
If you do not have any background in working with electricity, please be sure you get help from a &lt;em&gt;qualified electrician&lt;/em&gt;.
Also, some of the details really only apply to my local regulations (Belgium), so make sure you are well aware of &lt;em&gt;your&lt;/em&gt; local regulations.
Related to this, some of the terminology used might be a poor translation of some term which would have been straightforward in my native language (Dutch).
Even though I had a background as an electrical engineer, I still got a local professional electrician involved to ensure the overall safety &amp;hellip;&lt;/p&gt;
&lt;h2 id="principle"&gt;Principle&lt;/h2&gt;
&lt;h3 id="relays"&gt;Relays&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/principle.png" alt="principle relay"&gt;&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Relay"&gt;relay&lt;/a&gt;&lt;/strong&gt; is an electrically operated switch.
The voltage over two of its terminals can then control (switch) the voltage over the two other terminals.
The control voltage will typically be on a safe, low voltage, whereas the other terminals can carry higher (mains) voltage.
You can use it to control any other electrically switched device, e.g. lights or motors.&lt;/p&gt;
&lt;h3 id="push-buttons"&gt;Push buttons&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/principle_push_button.png" alt="principle push button"&gt;&lt;/p&gt;
&lt;p&gt;Where the relay serves as the output, the &lt;a href="https://en.wikipedia.org/wiki/Push-button"&gt;push button&lt;/a&gt;, together with a digital input sensor, serves as &lt;strong&gt;the input&lt;/strong&gt;.
The push button itself is actually a simple mechanical contact.
Pushing the button results in the internal mechanical contact making an electrical contact.&lt;/p&gt;
&lt;p&gt;There are two &lt;strong&gt;types of contacts&lt;/strong&gt;: a &lt;em&gt;normally open&lt;/em&gt; (NO) contact or a &lt;em&gt;normally closed&lt;/em&gt; (NC) contact.
Normally open means that the mechanical contact maintains an open electrical contact for the majority of the time, unless the button is pressed.
Normally closed is then the opposite situation, meaning a closed electrical contact whenever the button is &lt;em&gt;not&lt;/em&gt; pressed.
For push buttons, such as those to operate lamps, an NO contact would typically be used.
NC contacts make sense for situations where you want to be sure that the electrical contact is always guaranteed, e.g. a sensor for an alarm system.
Loss of signal for an NC contact might indicate the sensor has been tampered with.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;voltage&lt;/strong&gt; used for a push button is typically not mains voltage (240VAC), but rather a lower, safer voltage like 24VDC.
Typical digital input sensors such as those found on DIY devices like the &lt;a href="https://www.arduino.cc/en/Tutorial/Foundations/DigitalPins"&gt;arduino&lt;/a&gt; or &lt;a href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio-and-the-40-pin-header"&gt;raspberry pi&lt;/a&gt; would operate on 5V or 3.3V, respectively.
For wiring a house, this is not an option because of the longer line runs and the resulting voltage drop related to the resistance of the wiring itself.&lt;/p&gt;
&lt;p&gt;An important thing to consider in the push button - lamp constellation is that the way the push button is used is &lt;strong&gt;stateless&lt;/strong&gt;.
Considering the lamp is off, one push would switch the light on.
Another push would switch it back off again.
Based on the position of the push button itself, you would not be able to tell the state of the light.
It differs from a rocker switch in that it only maintains the contact for the duration of the push of the button.
While these observations might be rather trivial, for the purposes of modeling them higher up on the stack, they are relevant.&lt;/p&gt;
&lt;h2 id="conductors-belgium-only"&gt;Conductors (Belgium only)&lt;/h2&gt;
&lt;h3 id="relays-1"&gt;Relays&lt;/h3&gt;
&lt;p&gt;The following image shows a typical tube containing 3 wires with diameters of 1.5mm², fit for lights.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/cable-3g15.jpg" alt="cable 3G 1.5mm2"&gt;&lt;/p&gt;
&lt;p&gt;For the cables carrying mains voltage, the following &lt;strong&gt;rules&lt;/strong&gt; are relevant (non-exhaustive see e.g. &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;, &lt;a href="https://www.plantyn.com/webshop/product/project-huisinstallatie-naslagwerk-9789030142942"&gt;project huisinstallatie naslagwerk&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for circuits up to 16A (typically lights), a min 1.5 mm² cross-section wire is to be used&lt;/li&gt;
&lt;li&gt;for circuits up to 20A (lights and / or others like power sockets), a min 2.5 mm² cross-section wire is to be used&lt;/li&gt;
&lt;li&gt;each of the circuits need to have proper matching circuit breakers&lt;/li&gt;
&lt;li&gt;all cables consist of neutral wire, live wire and an earth wire&lt;/li&gt;
&lt;li&gt;color coding:
&lt;ul&gt;
&lt;li&gt;neutral: blue&lt;/li&gt;
&lt;li&gt;live: brown&lt;/li&gt;
&lt;li&gt;earth wire: green / yellow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;max 8 consumers (lights, power sockets) per circuit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;circuit breakers&lt;/em&gt; protect &lt;em&gt;your wires&lt;/em&gt; from burning up&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Residual-current_device"&gt;residual-current devices&lt;/a&gt;&lt;/em&gt; protect &lt;em&gt;living things&lt;/em&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that this is only a subset of the rules and pointers to keep into account while doing the physical wiring.
Other things to consider are the type of cable (in a plastic tube or as a cable), solid vs stranded wire, stiff vs flexible wire, etc. &amp;hellip;&lt;/p&gt;
&lt;h3 id="push-buttons-1"&gt;Push buttons&lt;/h3&gt;
&lt;p&gt;Contrary to the cabling for the lights, the push buttons run on the lower, safer voltage of 24VDC.
Consequently, the wire diameter can be a lot smaller and there is no strict requirement on the use of circuit breakers.
An additional advantage in terms of safety compared to a more &amp;ldquo;traditional&amp;rdquo; installation is that the push button itself is never connected to the consumer on the higher mains voltage.&lt;/p&gt;
&lt;p&gt;The following image shows an &lt;em&gt;SVV&lt;/em&gt; &lt;strong&gt;signal cable&lt;/strong&gt;, which is typically used in Belgium for analog signaling applications.
I did some online research into the international naming for this type of cable, but there does not seem to be any.
For the sake of the discussion here however, the characteristics are actually more important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small diameters, on the order of 0.8mm²&lt;/li&gt;
&lt;li&gt;solid core, as this makes connecting to the push buttons easier as they mostly have screw connectors&lt;/li&gt;
&lt;li&gt;multiple wires in 1 cable (as shown in the picture). These cables mostly come with either 4, 8, 12 or 16 wires.&lt;/li&gt;
&lt;li&gt;visually clearly differing wires, i.e. highly contrasting colors for the wires&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/cable-svv.jpg" alt="cable SVV"&gt;&lt;/p&gt;
&lt;p&gt;The idea of having this multitude of wires in one signal cable is that you would ideally need &lt;strong&gt;one cable run&lt;/strong&gt; for e.g. the push buttons for one floor.
Each push button contact then uses one of the wires of the signal cable.
Apart from the push button contacts, you also need to reserve one of the wires for the positive level.
Essentially, the push buttons thus internally connect 1 signal wire (on ground level) to the common positive level &amp;ndash; which can be detected by your digital input readout.&lt;/p&gt;
&lt;p&gt;As a side note, some manufacturers also have push buttons with a built-in &lt;strong&gt;LED light&lt;/strong&gt;.
If you want to provide power to that LED, you would also need to reserve an extra wire for that feature.&lt;/p&gt;
&lt;p&gt;Concerning the &lt;strong&gt;various colors&lt;/strong&gt; available in the signal cable, I did find it useful to have a fixed order in which to assign them to the different inputs.
I would always use the red wire for the positive (24V) level.
The remainder of the wires would then be assigned according to the &lt;a href="https://en.wikipedia.org/wiki/Electromagnetic_spectrum"&gt;colors on the visible electromagnetic spectrum&lt;/a&gt;.
For example, you could go from black, grey, brown, orange, green, blue, &amp;hellip; all the way to white.&lt;/p&gt;
&lt;p&gt;The following image shows the &lt;strong&gt;wiring diagram&lt;/strong&gt; for a simple NO &lt;a href="https://www.niko.eu/en/products/switching-material-and-socket-outlets/switches-and-sockets/push-button-with-3-connection-terminals-n-o-productmodel-niko-025f93c2-c921-59b0-b204-af07ed3ee2a8"&gt;push button from Niko&lt;/a&gt;, a Belgian manufacturer of electrical switches and sockets.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-29/push-button-wiring.png" alt="push-button-wiring"&gt;&lt;/p&gt;
&lt;p&gt;The diagram indicates the push button can be used to switch voltages up the 230VAC, but since we&amp;rsquo;re only interested in for the signaling application, it might just as well be a lower voltage.
Also visible is the optional LED light.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;The focus of this post was first to describe the &lt;strong&gt;basic principle of some key components&lt;/strong&gt;, the relay and the push button.
I did want to highlight what exactly they are and also how the operating principle relates to the modelling higher up in the stack, e.g. the fact that I mostly consider the push button to be stateless.&lt;/p&gt;
&lt;p&gt;The second part of the post dealt with the details of the &lt;strong&gt;physical electrical conductors&lt;/strong&gt; connecting to and from these components.
Relays, for lights, use 3 x 1.5mm², whereas higher loads require a higher wire gauge of 3 x 2.5mm².
The push buttons can use a lower wire gauge of e.g. 0.8mm² since you would run these on a safer, lower voltage of 24VDC.
While the regulations pertaining to the wiring are specific to my country&amp;rsquo;s regulations, I still hope someone else might learn from it!&lt;/p&gt;
&lt;p&gt;Finally, always keep safety into account!
Even you feel you are really knowledgeable, I still think it was an added value for me to involve a local professional electrician to get proper hands-on advice.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;ISBN/EAN 9789030142942&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;the official Belgian regulation is the &lt;a href="https://economie.fgov.be/nl/publicaties/algemeen-reglement-op-de"&gt;AREI&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Architecture</title><link>https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/</link><pubDate>Tue, 22 Jun 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/</guid><description>&lt;p&gt;Prior to actually building anything, it is important to think on a higher level what the various components are and how they will interact with each other, i.e. the &lt;em&gt;architecture&lt;/em&gt;.
Over the process of doing my own setup, I came across a large number of different possible configurations, each with their own benefits and downsides.
For the first part of this post, I will focus on the final layout.&lt;/p&gt;
&lt;p&gt;While discussing the architecture, it is important to realize there are a number of different &lt;em&gt;layers of abstraction&lt;/em&gt; to reason about these.
This will be the topic of the second part of this post.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Prior to actually building anything, it is important to think on a higher level what the various components are and how they will interact with each other, i.e. the &lt;em&gt;architecture&lt;/em&gt;.
Over the process of doing my own setup, I came across a large number of different possible configurations, each with their own benefits and downsides.
For the first part of this post, I will focus on the final layout.&lt;/p&gt;
&lt;p&gt;While discussing the architecture, it is important to realize there are a number of different &lt;em&gt;layers of abstraction&lt;/em&gt; to reason about these.
This will be the topic of the second part of this post.&lt;/p&gt;
&lt;p&gt;To make everything more tangible, I did add a third section &lt;em&gt;detailing toggling a light with push button&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This post is a part of a larger series of posts on my home automation setup.
See the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt;, to learn about the rationale and a description of the other posts.&lt;/p&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;
&lt;p&gt;The following diagram gives an high-level overview of the overall architecture.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/architecture.png" alt="architecture"&gt;&lt;/p&gt;
&lt;p&gt;Its main characteristics are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;distributed communication:
&lt;ul&gt;
&lt;li&gt;communication is (mostly) done via an event-based system, MQTT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;centralized wiring:
&lt;ul&gt;
&lt;li&gt;all I/O (push buttons, relays for lights, &amp;hellip;) is connected to modules which fit in a centralized electric cabinet&lt;/li&gt;
&lt;li&gt;there is a central service with automations connecting the different modules (home assistant)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s elaborate a bit more on the constituent components.&lt;/p&gt;
&lt;h3 id="mqtt-broker"&gt;MQTT broker&lt;/h3&gt;
&lt;p&gt;In the center of the drawing is a &lt;a href="https://hub.docker.com/_/eclipse-mosquitto"&gt;mosquitto MQTT broker&lt;/a&gt;.
MQTT is a &lt;em&gt;publish-subscribe&lt;/em&gt; protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there is one central instance, called the &lt;em&gt;broker&lt;/em&gt;, which manages different &lt;em&gt;topics&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;different clients can &lt;em&gt;publish&lt;/em&gt; updates, called &lt;em&gt;events&lt;/em&gt;, to the topics&lt;/li&gt;
&lt;li&gt;in addition, all clients can also &lt;em&gt;subscribe&lt;/em&gt; to topics to receive the events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MQTT is a popular choice for IoT since its event-based model fits well for a lot of problems (e.g. a push button toggle).
Another advantage is that the each of the different clients only needs to maintain a connection to the broker and the topics they want to be subscribed to.
This makes for a very &lt;em&gt;loosely coupled&lt;/em&gt; and &lt;em&gt;scalable&lt;/em&gt; architecture: it is quite easy to add new components.
A disadvantage however is that all connections rely on the availability of the central broker.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id="mqtt-clients"&gt;MQTT clients&lt;/h3&gt;
&lt;p&gt;The principal other components of the setup are all MQTT clients which both subscribe and publish to specified topics, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;subscribe: they listen to topics which instruct them to do some behavior (&amp;ldquo;command topics&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;publish: they publish events to topics that indicates their state (&amp;ldquo;state topics&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The clients in this setup can provide (a combination of):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;sensors&lt;/em&gt;: push buttons, motion sensors, smoke detectors, &amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;actors&lt;/em&gt;: lights, covers, sirens, &amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;logic&lt;/em&gt;: send out events on a certain condition, e.g. on another event (an automation)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="unipi"&gt;unipi&lt;/h4&gt;
&lt;p&gt;For the &lt;strong&gt;sensors&lt;/strong&gt; and &lt;strong&gt;actors&lt;/strong&gt; I did use hardware from &lt;a href="https://www.unipi.technology/"&gt;unipi&lt;/a&gt;.
In essence the unipi neuron units are DIN-rail mountable units housing a raspberry pi and a couple of I/O boards depending on the model you have.
The role of the unipi units is thus to provide an MQTT event-based interface for all home automation I/O: reading push buttons, toggling lights, controlling shades, reading smoke and movement detectors, etc.&lt;/p&gt;
&lt;h4 id="home-assistant"&gt;home assistant&lt;/h4&gt;
&lt;p&gt;Whereas the I/O modules themselves are distributed both physically and logically, I did want to &lt;strong&gt;centralize the controlling logic&lt;/strong&gt; in a dedicated service.
Cue in home assistant.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.home-assistant.io/"&gt;home assistant&lt;/a&gt; is an open source software project which aims to integrate all sorts of smart home devices.
Given my choice of MQTT as an interfacing protocol throughout my setup, I heavily rely on the &lt;a href="https://www.home-assistant.io/integrations/mqtt/"&gt;home assistant MQTT integration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From a high level, all sensors and actors which are available as MQTT topics can be represented as &lt;em&gt;entities&lt;/em&gt; within the home assistant configuration.
The home assistant instance itself connects to the central MQTT broker and listens to those MQTT topics for which it has entities.
Automations, which are also part of the configuration, can then link actions from one entity to another.
An obvious example is the push of a button that switches a light on.
The rationale behind using automations to link inputs with outputs, is that nothing is really hard-wired in this setup!&lt;/p&gt;
&lt;p&gt;Note that home assistant supports a wide range of integrations beyond MQTT.
The thermostat in the schematic for instance has an integration based on REST / HTTP.&lt;/p&gt;
&lt;h2 id="layers-of-abstraction"&gt;Layers of abstraction&lt;/h2&gt;
&lt;p&gt;Planning for a DIY wired open home automation setup requires some other specific thinking in terms of the different &lt;strong&gt;layers of abstraction&lt;/strong&gt;.
The following diagram focuses on these different layers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/layers.png" alt="layers"&gt;&lt;/p&gt;
&lt;p&gt;From low to high level:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;wiring&lt;/strong&gt;: the physical wiring which connects to electrical components such as push buttons and light fixtures&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hardware&lt;/strong&gt;: the physical hardware component that will interface with the electrical components (I/O).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;software&lt;/strong&gt;: a client library interfacing with the hardware and which can translate low-level hardware events to protocol events&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;network&lt;/strong&gt;: layer which connects the different components together using the protocols&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;service&lt;/strong&gt;: the services are those parts which actually talk to each other over the network&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that not all components need to have all of the layers.
Services like home assistant and the mosquitto broker act completely on the network itself.&lt;/p&gt;
&lt;p&gt;Why does this matter?
For the majority of commercially available IoT devices, you would get something that takes care of most of the lower levels.
Wiring mostly isn&amp;rsquo;t an issue (it is typically wireless) and the hardware and software are part of the package that you buy.
In this wired layout with custom components however, all of these layers need to be addressed.&lt;/p&gt;
&lt;p&gt;As an example, have a look at the wiring I did for my cover control:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/wiring.jpg" alt="wiring"&gt;&lt;/p&gt;
&lt;p&gt;Most of my wiring was done in a &lt;a href="https://en.wikipedia.org/wiki/Network_topology#Star"&gt;star topology&lt;/a&gt;.
This means for example that you do need a single cable run for &lt;em&gt;each&lt;/em&gt; of the individual (group of) lights you want to control.
The upside is that this is very flexible towards the future, since physical rewiring is still possible from the electrical cabinet.
The (visible) downside is that the amount of wires piles up rapidly and you will need to think of some kind of system to keep a sane overview of this.&lt;/p&gt;
&lt;h3 id="bus-based-systems"&gt;Bus-based systems&lt;/h3&gt;
&lt;p&gt;Alternatively, the wiring could have been done with a &lt;strong&gt;bus-based system&lt;/strong&gt; like &lt;a href="https://knx.org"&gt;KNX&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Digital_Addressable_Lighting_Interface"&gt;DALI&lt;/a&gt;.
The advantage of systems like these is that the wiring would have been a lot easier and they can also provide some of the functionalities on other parts of the stack.
For the case of KNX for instance, each of the components (push buttons, light control, &amp;hellip;) are provisioned individually, resulting in a fully distributed, more fault-tolerant system.
Note that there is a &lt;a href="https://www.home-assistant.io/integrations/knx/"&gt;home assistant KNX integration&lt;/a&gt;, so even in this case, it could fit the proposed architecture (but obviously less reliant on MQTT).&lt;/p&gt;
&lt;p&gt;At this point, I did not choose for a bus-based system though.
The first reason was one of cost, notably in the case of KNX.
Also, I did want the possibility to easily (re)program my setup using general purpose programming languages, whereas KNX relies on (costly, vendor-restricted) visual programming tools.
All of my light sockets are wired with 5-wire cable, so at some point I might have another look at using DALI for controlling my lights.&lt;/p&gt;
&lt;h2 id="toggling-a-light-switch-"&gt;Toggling a light switch &amp;hellip;&lt;/h2&gt;
&lt;p&gt;To illustrate the theory, let&amp;rsquo;s just walk through all the steps of toggling a push button:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mhemeryck.xyz/2021-06-22/flow.png" alt="flow"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;a push button is pushed down, making contact (off-on) and then released again (on-off)&lt;/li&gt;
&lt;li&gt;the unipi digital input unit sees this change and translates this to an MQTT state change event which is pushed to the central mosquitto broker&lt;/li&gt;
&lt;li&gt;the home assistant instance has a subscription on the broker for the input button state change and updates its internal entity state&lt;/li&gt;
&lt;li&gt;the home assistant instance has an automation configured for the push button entity and triggers the matching light entity to an action&lt;/li&gt;
&lt;li&gt;the light entity update pushes out another event to the light action topic to turn on&lt;/li&gt;
&lt;li&gt;another unipi unit which is connected to the physical light and has a subscription to the light action topic pulls in the update&lt;/li&gt;
&lt;li&gt;the unipi unit toggles a relay, switching on the light&lt;/li&gt;
&lt;li&gt;finally, after the light has turned on, a state update is also pushed out from the unipi unit, which updates the internal state on home assistant&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;The focus of this post was to outline the main components and links in my home automation setup, principally based on MQTT and home assistant as the brains behind it all.&lt;/p&gt;
&lt;p&gt;The main advantage of this setup is the flexibility if offers since everything is configurable from within home assistant.
On the other side of the equation, once home assistant breaks down, the entire system can break down, which poses high reliability constraints on such central components.&lt;/p&gt;
&lt;p&gt;I did not yet dive down into the details of the different components i.e. the sensors and actuators, most notably the unipi platform.
Also, the different applications themselves, such as push buttons, lighting fixtures, DALI, shades, an alarm system, &amp;hellip; all merit their own discussion.
Finally, I did also point the different layers of abstraction to consider when laying out a wired home automation system.&lt;/p&gt;
&lt;p&gt;This should conclude the &amp;ldquo;theoretical&amp;rdquo; part of the home automation series.
Check the &lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;home automation overview post&lt;/a&gt; for the more &amp;ldquo;practical&amp;rdquo; upcoming posts.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;although even this could be mitigated using a highly available MQTT broker. &lt;a href="https://www.emqx.com/en/blog/emqx-mqtt-broker-k8s-cluster"&gt;EMQX&lt;/a&gt; seems to be an example of this, but I haven&amp;rsquo;t tried it myself yet.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>Why</title><link>https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/</link><pubDate>Tue, 15 Jun 2021 00:00:00 +0000</pubDate><guid>https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/</guid><description>&lt;p&gt;This post shall be the first in a series of blog posts where I intend to outline my home automation setup.&lt;/p&gt;
&lt;p&gt;For this first post, I want to start by first explaining &lt;em&gt;why&lt;/em&gt; I worked on this.
Secondly, I want to give some more attention to the &lt;em&gt;vision&lt;/em&gt; and guiding principles I will use further on.
At the end, there is an overview of all the future posts that I have currently planned for this series.&lt;/p&gt;</description><content:encoded>&lt;p&gt;This post shall be the first in a series of blog posts where I intend to outline my home automation setup.&lt;/p&gt;
&lt;p&gt;For this first post, I want to start by first explaining &lt;em&gt;why&lt;/em&gt; I worked on this.
Secondly, I want to give some more attention to the &lt;em&gt;vision&lt;/em&gt; and guiding principles I will use further on.
At the end, there is an overview of all the future posts that I have currently planned for this series.&lt;/p&gt;
&lt;h2 id="why"&gt;Why&lt;/h2&gt;
&lt;p&gt;I am an electrical engineer by &lt;strong&gt;background&lt;/strong&gt;, specialized in signal processing and programming for embedded devices (high complexity on small systems).
Within my career, I did make the switch to software engineering, doing the opposite (low complexity on large systems).&lt;/p&gt;
&lt;p&gt;One of the things I really did enjoy while building this system (and by extension: my house) was being able to &lt;strong&gt;build something from the ground up&lt;/strong&gt;.
Along the way, I was able to learn about a myriad of things: electricity and lighting, IoT hardware, various concepts, paradigms, languages &amp;hellip; related to software development, networking and network-related infrastructure &amp;hellip;
Non-technical aspects, such as project management, were also quite important.&lt;/p&gt;
&lt;p&gt;Even though I feel there&amp;rsquo;s still a lot of room left for improvement, I did consider that &lt;strong&gt;now was a good time do a write-up&lt;/strong&gt; of everything accomplished thus far.
Firstly, I believe that writing everything down would help me in documenting everything.
Additionally, this provides the opportunity to reflect back on those things that went well as well as other parts that might still benefit from some improvements.
The process of writing itself is also a good exercise to improve on my technical writing (in a non-native language).
Possibly these posts might help or even inspire my readers to get into this topic.
Lastly, I do want to reach out to a larger community and get feedback.&lt;/p&gt;
&lt;h2 id="vision"&gt;Vision&lt;/h2&gt;
&lt;p&gt;There are many different approaches to build a home automation system.
Since the open source &lt;a href="https://www.home-assistant.io/"&gt;home assistant&lt;/a&gt; platform plays a central role in my system, I did borrow some inspiration from &lt;a href="https://www.home-assistant.io/blog/2016/01/19/perfect-home-automation/"&gt;home assistant&amp;rsquo;s vision&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Considering my own ideal system, the following are the key ideas.&lt;/p&gt;
&lt;h3 id="wired"&gt;Wired&lt;/h3&gt;
&lt;p&gt;A wired solution has the advantage of not being affected by unreliable wireless links.
Also, there&amp;rsquo;s generally no issue of ensuring a power supply, so there is no need to keep an eye on batteries.
The disadvantage is however that the overall setup is less flexible and needs to be thought out really well in advance.
The process of wiring itself can also be quite time consuming, costly or simply impossible, depending on your housing situation.&lt;/p&gt;
&lt;h3 id="open"&gt;Open&lt;/h3&gt;
&lt;p&gt;All hardware and software should be &amp;ldquo;open&amp;rdquo; in the sense that it can readily understood and changes can be made to it.
It should be possible to take a given component and swap it out for another one, unrelated to the original vendor.
On the software-side, it should be possible to program against it with standard protocols using general-purpose programming languages.
Combining different sorts of hardware and software should be straightforward.&lt;/p&gt;
&lt;h3 id="local"&gt;Local&lt;/h3&gt;
&lt;p&gt;The home automation should always work, even in the absence of an Internet connection.
The data from the home automation system itself should be owned by yourself.
This does not mean that you cannot use common network protocols, you can still have everything connected via a local network (LAN).
Nor does this mean that you cannot have non-critical integrations which are web-based, e.g. an integration that pulls in a weather forecast.
Ideally though, the majority of communications the home automations needs to do to function properly should be in the home itself.&lt;/p&gt;
&lt;h3 id="low-cost"&gt;Low cost&lt;/h3&gt;
&lt;p&gt;Home automation systems have been around for quite some time, but what has changed the latest years is the availability of both hardware and software that allows to mix and match.
Also open hardware platforms like the raspberry pi have made it much easier to develop own platforms.
The disadvantage of such DIY platforms is the lower intrinsic robustness, compared to an off-the-shelf commercial solution.&lt;/p&gt;
&lt;h3 id="reproducible"&gt;Reproducible&lt;/h3&gt;
&lt;p&gt;This is a key idea coming from my background as a software developer.
Essentially, this means that the provisioning and deployment of the overall setup should be automated to the extent that in case of any failure it can readily be reconstructed.&lt;/p&gt;
&lt;h2 id="posts"&gt;Posts&lt;/h2&gt;
&lt;p&gt;In the following series of blog posts, I will outline more of the details of the actual setup that I did design and build.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-06-15-home_automation_why/"&gt;why&lt;/a&gt; introduction to the series, this post&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-06-22-home_automation_architecture/"&gt;architecture&lt;/a&gt; topology of the home automation components and different levels of abstraction&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-06-29-home_automation_wiring/"&gt;wiring&lt;/a&gt; details about basic components (relays and push buttons) and the related wiring&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-07-06-home_automation_schematics/"&gt;schematics&lt;/a&gt; how to represent the electrical wiring in a one-wire diagram and floor plan&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-07-13-home_automation_cabinet/"&gt;cabinet&lt;/a&gt; a peek inside my electrical cabinet&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-07-20-home_automation_hardware/"&gt;hardware&lt;/a&gt; details about the central hardware units I did use&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-07-27-home_automation_software/"&gt;software&lt;/a&gt; different layers of interfacing software&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mhemeryck.xyz/posts/2021-08-03-home_automation_service/"&gt;service&lt;/a&gt; bringing it all together with home assistant&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item></channel></rss>