DIYOW Heating Automation Concept

This will outline my design for heating automation. I am a software engineer and a tinkerer and have always wanted to automate bits of my home. Once I had a few sensors on a Raspberry PI to create a weather station as a “mickey mouse” project I looked straight at heating control as the defacto home automation application.

I decided to go completely “clean room” on it. Build it up from scratch, not accept or limit myself to any particular convention or standard and not even look at what others were doing. Just starting from basic questions about how I would like to automate my heating and building it up.

In this way there are currently no immediate integration with HAss or OH or any other high level automation system. But I’m keen to see what can be done here.

Basic concepts:

  • Data - anything that might be of use in the system. These come from places like:

    • Sensor readings
    • Device states
    • etc.
    • Example: Heating Room Temp: 20.56*C
    • Example: Main Heating: ON
    • Example: Presence Paul: HOME
  • Demands - Demands are how the system signals that something is required. It provides a decoupling point between what is asking, what it is asking for and how the system can full-fill, or ignore that demand. Demands are:

    • Timely - The have a timestamp when raised.
    • Expire - They have a life expectancy after which they are no longer effective.
    • Discretionary - The don’t have to be served or acknowledged.
    • Expiry is enforced for automatics, overridden for manual intervention.
  • Schedulers, Schedules and Conditions are the main automated mechanisms to raise demands given a set of conditions. Schedules are competing, layered and many can and will be active at anyone time.

    • Schedulers fetch data and run a set of Schedules in order.
    • Schedules - A basic schedule is just a set of conditions, if true it raises a demand for something. More advanced Schedules can do much more including raising multiple demands and monitoring multiple zones.
    • Condition - A condition accepts data and returns true or false when evaluated. Examples are, TimeOfDay, DayOfWeek, Presence, TemperatureMinimum
  • Controllers. Controllers are the integration points for controlling actual hardware. Often and especially in the case of heating there are safety stipulations and “appropriate use” mechanics and so these are contained close to the device in a controller specific to that device. Obvious examples might be “HeatingController” or “RadiatorController” - These are good candidates for replacing with HA.

  • Distribution and Isolation of concern - The system is mostly, except where I was being lazy, fully distributed. Data is stored in one place, Demands in another, Schedulers (multiple if you want) can be run anywhere you want, controllers can run anywhere you want. You can run it all on one host and use “localhost” if you wish or spread it out all over the place if you want. Each component does it’s job and trusts that other components will do theirs without caring how they do it.

  • Transient state - The acid test of this is to switch any component off and on again and see how long it takes to recover. This applies to any component or the whole system. It has to recover promptly to the correct state. To that end nothing is “stored”, nothing is “persisted” all logic is re-run periodically. Sensors send data every 5 seconds, schedules run every 30 seconds, demands are polled every minute. So it will automatically recover to the state it should be in once they run again in order. (I may have to persist dynamic configuration going forward).

  • Fail safe - If any component fails, stack dumps and exits, the heating should never be left running indefinitely but timeout and return to a safe state. Other features include locking out excessive commands at the device level, for example, anti-short-cycle for a boiler or leaving a NC radiator valve open constantly.

  • Transparent to manual intervention. “I’m sorry, I cannot let you do that Dave” is not an option. Devices should retain as much of their existing manual functionality as possible. Demand, Schedules, Conditions will just have to accept that a human may have changed some state in the system without consulting them. You cannot assume something is stil as the automation left it last.

  • Avoiding state machines - or moving them as close to the real world state as possible.

Here is an overview diagram:
Imgur

The term “bulletin board” is used to describe incredibly lightweight storage used. Any data published to the bulletin board is available for all clients to read. There is no “delete”. If data doesn’t exist before it’s published it is created, if it does exist it is overwritten. Data has a timestamp but it is up to the client of the data to choose what “too old” is. Demands have a timestamp AND an expiry and in this case it is up to the publisher to decide how old the demand is allowed to be and still be effective. Without “delete”, things just go stale and time out and become ineffective and are ignored.

In the current implementation in Python, Data hub and Demand hub share a process. Scheduler and Demand Processor share a process. So excluding sensors there are currently 4 processes.

Schedules:
Imgur

This shows a basic overview of how schedules work. Note the conditions are not limited to the intuitive “Time of day” etc. But can very easily trigger on any published data such as Presence or whether another device is ON or OFF.

The MultiroomTemperatureSchedule is probably the most used Schedule as it single handedly encapsulates multi-zone temperatures, presence and standard time scheduling in one place. It consumes from a simple Python Dictionary of target temps for various modes, there are several of these schedules with different conditions and different target modes or profiles, such as “Eco” or “Comfort” etc.

Multiple schedules will most likely be active. When I get up in the morning I have:
MaintenanceSchedule checking the temp is about 6C everywhere in the house and above 1C in the garage.
RoutineWeekDayMorningSchedule checking the temps in various rooms are meeting the “eco” target temps.
PresenceSchedule checking the temps in various rooms are meeting the “comfort” target temps.

The fact that more than one of these may raise a demand for heating is all sorted out in the Demand Processor.

Integration points:
Imgur

Not a lot of these are currently very useful for HA intergartion. Most are raw sockets that send/receive JSON payloads. The REST end points are not really REST and more HTTP CGI. This is where I plan to do a lot of work leveraging better standards such as RESTful and maybe MQTT.

(Config hub is shown here as a future idea on how to manage dynamic configuration, encapsulating it into another network service)

An example message flow might look something like this:
Imgur

This is close to what I have presently as I have no yet got automated radiator valves, so the while the Demand processor receives a demand for “livingroom heating”, all it can do is emit a demand for Main heating.

This is an example flow if I had automated radiator valves and shows how multiple zone demanding should works.
Imgur

Given the latest sensor reading for livingRoom and office their temps are below target, based on the config for this schedule being “comfort” and the target temps for comfort being “20C”. The schedule raises demands for those zones.

You should also note the cool feature of RRD data logging. Regardless of what you send to the Data Hub it will create or update an RRD for that value and log it once a minute. No configuration required.

Imgur

The Demand processor does the logic to determine how to satisfy the incoming demands. In this case I imagined it would raise demands for the various device states. Controllers (unshown) would receive those demands and command the devices (if required and if appropriate).

Logic for things like “Normally Closed, Normally Open” would be handled in the Controller. The Demand Processor raising the demand just wants the radiator open it doesn’t care how the controller does it.

A lot of the design comes from what I call “Scenario Driven Development”. It’s about thinking through scenarios and dry running (usually in my head) through scenarios to evaluate solutions and if they work or not.

In operation a demand is currently raised by the automatics to expire in 5 minutes. This means that if the computer running all or part of the operation dies or falls off the planet the heating will switch off in 5 minutes and all valves will return to their default state. This logic stretches down into the switch which controls the boiler relay, although not fully implemented with timeouts there yet (an open TODO for safety).

No schedules currently exist that demand the heating to be OFF. This is possible, but there isn’t a need in normal operation. Once the conditions that cause a demand to be raised are no longer true the existing demand will simply expire. While they are still true the demand will be refreshed periodically.

Demand Expiry cannot be overridden by automatics. If I manually publish a demand for 1 hours heating, then a schedule is forbidden from publishing that same demand until it expires or until it’s expiry would be ahead of the existing demand’s. This allows manual intervention overrides for both OFF and ON.

Demand Expiry can be overridden by manual intervention. A button used to command heating for 1 hour will not bother checking if a demand exists, it wins. “Sorry Hal.”

Hacks:
Presence state is currently published into the Demand hub as if it were a demand. This allows it to take advantage of the expiration and so I don’t need to worry about setting the presence as “AWAY”, I just let the Presence HOME expire.

Lighting Controller. This was hooked up to control group lights using the same system. SOnOff S20s or the RF Bridge via MQTT publish demands for these lights and the lighting controller switches them on. It has revealed that polling demand based control has draw backs for things you would expect to be have “realtime” response. So the Lighting Controller polls the demands once a second, creating a load of network noise, but can still result in a seconds delay between hitting the switch and the lights coming on.

Home Assistant:
Obviously my heating system was not designed with HA (or any other) system in mind. You could certainly integrate as is with Scripts running “netcat” to inject JSON onto bulletin boards and collect JSON back from them. You can control the devices with HTTP.

I realise a sore point will be the Data Hub. It kinda ties things together and it is where all my sensors publish to. They publish a JSON blob like:
{ “livingRoom”: { “key”:“livingRoom”, “shortName”:“LR”, “name”:“Living Room”, “units”:"*C", “value”: 19.56, “type”:“float” }
So you “could” create an integration which dumps all of your sensor values from HA to the data hub, or, an MQTT handler (not written yet) could subscribe and/or discover your current sensors and publish them for you.
Control of the system could be done by publishing and reading demands.
The heating controller (and lighting controller or any other) seem like a good candidate to just move to HA. I imagine an integration polling demands and a JSONPath expression to extract the one you want and send the signals to the switches/relays.

You will notice the lack of what I call half-smarts. I don’t like radiator valves which turn themselves on or off although I realise these exist. I have open TODOs for migrating some meta info into demands such as what Schedule raised the demand in the first place and where appropriate what target temperature was it trying to achieve. This could be used to push a set-point temp to an automated radiator valve.

Other TODOs for the next version is a predictive scheduler. A predictive scheduler will take a normal scheduler reference and run it in the future to ask the question “In Half an hour, would the heating need to be on?”, if that returns true, then the demands it would raise in the future can be checked and if appropriate the heating enabled early in order to reach the target temp in time. This is predictive or what I can “pre-ramping” the heating to achieve a temperature at a future time. By wrapping the whole scheduler in this mechanism means it support the same things as the actual Schedule, just running in the future.

What is missing.
UIs.
Dynamic config, such as altering the target temperature and persistence of.
Pretty displays
Proper standards at integration points.

Code - it’s a bit messy:

One final word. This is not the same kind of project as HA. I have never had intentions of making this pre-canned and ready to configure for anyone or everyone as a public project. A lot of it is simply hard-coded for my use. This keeps things very simple, but it means if you wanted to use the system you will need to be able to roll up your sleaves and get into editing some Python and/or C code.

I forgot to mention I am creating DAOs (Data Access Objects) around network calls to get/publish Data or Demands. In theory that provides a way to choose between publishing to my data hub or publishing somewhere else as long as it’s pollable data.

A few pretty graphs:
The main heating graph, includes most of the temperatures as written by the data hub to RRDs. Also includes the “presenceHome” data item which is shaded green when I’m home and the “Heating” data item which is shared blue when the heading is on.
Imgur

Things to note. I intervened in the livingroom on Saturday evening by manually putting on the electric fire. This raised the temperature much faster than the heating and the heating shut off. I intend to automate the electric fire if the conditions of both living room doors being closed is met and the living room is the only room requesting heating. It’s not a hardroom to heat with the doors shut, but hard to heat with the doors open.

This morning I was up early and as the schedules, including Presence are currently set to become active at 06:30 and I was up at 05:30 I intervened by holding the button on the heating relay SOnOff which generates a 1 hour demand. This obviously then meant the normal run at 6:30 wasn’t needed as target temps were already met.

I also hope to automate what I consider “Up” or “In bed”. I’m thinking about using an RFID reader or BLE sensor on the bedside table. If my phone sets down there I’m in bed and that would enable a different set of Schedules. Of course full multi-room presence awareness is on the cards too.

Heating loop graph, showing in pipe and out pipe.
Imgur

Solar panel power graph 50W panel, but today was miserably dark and cloudy:
Imgur

A snapshot from the scheduler logs, right now. Three schedules active, no demands.

Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: Weekend Routine Active - checking temps eco
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: livingRoom: 19.12 < 18
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: office: 20.38 < 18
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: bedroom: 21.19 < 16
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: garage: 16.0620 < 2
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: Presence Schedule Active - checking temps preferred
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: livingRoom: 19.12 < 19
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: office: 20.38 < 20
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: bedroom: 21.19 < 18
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: garage: 16.0620 < 2
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: Maintenance Schedule Active - checking temps default
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: livingRoom: 19.12 < 6
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: office: 20.38 < 6
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: bedroom: 21.19 < 6
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: garage: 16.0620 < 1
Sep 15 19:15:18 raspberrypi Home Scheduler[18446]: Active demands []

This evening, office triggers the heating:

Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: Weekend Routine Active - checking temps eco
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: livingRoom: 19.31 < 18
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: office: 19.88 < 18
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: bedroom: 21.25 < 16
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: garage: 16.2500 < 2
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: Presence Schedule Active - checking temps preferred
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: livingRoom: 19.31 < 19
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: office: 19.88 < 20
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: bedroom: 21.25 < 18
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: garage: 16.2500 < 2
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: Maintenance Schedule Active - checking temps default
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: livingRoom: 19.31 < 6
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: office: 19.88 < 6
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: bedroom: 21.25 < 6
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: garage: 16.2500 < 1
Sep 15 18:08:37 raspberrypi Home Scheduler[18446]: Active demands [HEATING office True 0 0]

A corresponding blob from the logs of the heating controller at the same time:

Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Heating demandState ON demandExpiry 300 timestamp 1568567317.958224 currentTime 1568567330.3889806
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Demand state True current state True
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Getting heating status status http://10.0.0.9/status
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: <Response [200]>
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Heating status: ['ON']
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Heating already in correct status
Sep 15 18:08:50 raspberrypi Heating Controller[29686]: Sending JSON datum to 10.0.0.3: {"heating": {"timestamp": 0, "shortName": "HT", "units": " ", "name": "Heating", "value": 1, "type": "bool", "key": "heating"}}
Sep 15 18:09:20 raspberrypi Heating Controller[29686]: {"heating": {"category": "NONE", "expiry": "300", "timestamp": 1568567349.5943844, "key": "heating", "demandState": "ON"}, "presenceHome": {"category": "NONE", "expiry": 120, "timestamp": 1568567349.4742188, "key": "presenceHome", "demandState": "ON"}, "groupLight1": {"category": "NONE", "expiry": "60000", "timestamp": 1568567162.4977343, "key": "groupLight1", "demandState": "OFF"}}

And finally an electronics project, the LCD display hooked up to the datahub and display a filtered selection of data:
Imgur

In that case you would need to license it.

Done, thanks for pointing that out.