We're a family of four. I like it cool in my bedroom all day and night. The kids need their rooms to be a little warm when they get up in the week, and for longer at the weekends. The living room should be comfortable in the evening. And so on.
The UK's default heating algorithm, "heat the entire house based on the temperature of the living room and an arbitrary timer", means we waste a lot of energy. The solution is to have a custom programme on every single radiator in the house. This setup cost me about £200 (plus several days of time, but it was fun). I won't know for a year how much it saves me on energy bills, but I'm guessing quite a lot.
Basic controls from my phone:
Lots of nice graphs for tracking energy usage and heat levels:
It's something like this:
The most expensive part is the thermostatic radiator control system.
The EQ Max system is the best value system out there, and apparently fairly reliable. It's worth mentioning Tado, which appears to do much of the following for you, but it's a lot more expensive.
The thermostat heads communicate over 868MHz RF to a "Cube" device. The Cube allows you to TRVs into rooms, set programs (e.g. 21 degrees centigrade between 0900 and 1300 on a Monday), check battery life, etc. The cube is controlled via Java software which runs as a web server on your computer. It is packaged for Windows and Mac but a community member has helpfully repackaged it for Linux. The protocol has been reverse engineered, of which there are various implementations around the web.
Each head stores its program locally, which means they'll continue to work even when your Cube goes down. They can be individually "boosted" with hardware buttons.
There's a nice decalcification feature, which will fully depress and release the pin that operates the radiator valve (once a week by default)
The default mode of operation is "automatic". This means each head will follow its programmed schedule. When you override the schedule (by increasing the temperature), this change will stick until the next programmed temperature change, when the head will revert to the programmed settings.
A wall thermostat (for better ambient temperature measurement, and convenient setting of multi-radiator groups), and window sensors (which by default will automatically dial down your heating for 10 minutes when a window opens) are also available.
By all accounts the cube is a little buggy and forgets its associations between the TRV devices and the named programs. Instead of using the cube, you can consider using the open source culfw
software, and either (a) flashing the cube (German instructions) or (b) using your own CUL module and flashing that.
A traditional setup turns all the radiators on and off with a thermostatic switch attached to the boiler (a combi-boiler in my case). This needs to be turned off automatically when there is no heat demand. I did this with a Raspberry Pi 3+ and a relay. I added a physical switch as an override
If the Raspberry Pi were to break, we'd want to to have confidence we could still turn the boiler on and off, so I added a physical switch which will bypass the Pi when turned on. It looks like this:
The heads use M30 x 1.5mm standard thread size which fits most modern radiators. If you're lucky, you can just unscrew the union nut by hand (or with a bit of help from a plumber's wrench) and attach the EQ-MAX TRV with its own union nut.
Older radiators are more of a pain. Two of the radiators in my house are Comap Westherm which uses M28 x 1mm. You'll have to hope you can find an adaptor (I could), or you'll need to get a plumber to change the radiator fittings for you. Here's a handy list of adapter sizes. (The heads also come with a Danfoss RA adaptor which is rarely used in the UK)
Sometimes you'll see an F2
error message on the adaptor after fitting. This means it's not seated properly. I gave older valves a squirt with some lubricant and jiggled the little stud that opens and closes the valve with some soft hammer taps. Where there's a long thread you have to tighten the nut a bit further than you expect to get a snug internal fit against the stud. On the radiators wheren I used adaptors, the problem was the pin in the adaptor was not snug against the inner pin (on the radiator), so I placed a 1p coin inside the adaptor, and this seemed to work
For my main living space, which is open plan with the kitchen, I bought a wall thermostat. It's useful because:
I'm not particularly knowledgable about electronics, so I opted for a pre-built relay board (ModMyPi PiOT Relay Board) and case rather than faffing around with resistors, GPIO chatter, etc. I'm pleased with the quality of this unit and case, and it was straightforward to assemble and use.
It makes sense to use the Pi as the home automation hub (running OpenHabian), and I've plugged a 4TB USB drive into a USB port for backups, and doubling up as a simple NAS.
The relay takes the place of the old thermostatic wall switch. My thermostat was wired from the combi-boiler with a neutral and two lives (refer to your boiler manual for details). The switch in the thermostat breaks the live; the neutral suppled power to the thermostat. Replacing the old wall thermostat involved cutting back and taping the neutral (not needed), and then attaching the two lives to COM
("common") and NO
("normally open") terminals on one of the relays. The PiOT Relay Board has a nice interface for binding a relay to a GPIO which can then be addressed from the Pi.
There's a few open source home automation hubs out there. I picked Openhab2 because it looked OK and has EQ3-Max! integration.
To start with, you have to pair the thermostatic heads (and wall thermostat) with the cube, so it knows what it can address (the documentation calls this "teaching-in"). You need to start the Max! software to access the Cube's control interface. This is Java software which runs as a web server on your computer that you access using your web browser. It is packaged for Windows and Mac but a community member has helpfully repackaged it for Linux.
Theoretically, you don't have to use that software: the protocol has been reverse engineered, of which there are various implementations around the web, but none of them (that I've found) are complete. Most of these can report on current thermostat state and set current temperature, but that's all.
Once you've taught-in the thermostats, you can set their daily schedule: up to 7 heat/time pairs per day, per thermostatic head. The EQ-Max! software is very clunky for this, particularly as you typically want to do a lot of cut-and-paste, so I've added a command to python-maxcube-api to allow you to save and set programmes in JSON. I also used the official software to group the three radiators and the wall thermostat in our open plan living space into one room.
Note: When playing with scripting, I found TRVs would randomly stop responding. It turns out this is because of the "1% rule" - each RF band has a "duty cycle" which is the maximum ratio of time on the air per hour. Basically, 1% means you can speak 36s per hour.
openHABian is a Raspberry Pi SD image which includes OpenHab and some other somewhat useful configuration.
I ended up tweaking it quite a lot so not sure I'd start with it next time, but it's OK. It's based on Raspbian Lite and comes with reasonable install instructions.
The OpenHab2 MAX! Cube integration allows you to:
One you have paired the thermostatic heads with the cube, you can progress to adding them as "things" in OpenHab2. Start by adding the MAX! Binding, and take it from there. You'll have to read the openhab2 docs for full details, but the short version is:
TODO: I'm not entirely sure if you actually have to set up things in the inbox as well as in files. This is just the order I did it in. Check!
Now you're ready to set up text-based configuration files which will allow you to:
Each thermostatic head has a 6-byte RF address and a 10-character serial number. Both of these can be inspected in the OpenHab Paper UI. The serial number is also on a sticker inside the battery compartment in the thermostatic head.
When setting a programme via the python script I wrote, you need to address each device by its RF address; when addressing devices via Openhab, you use the serial. For example, a device might look like this (expressed as JSON):
{
"rf_address": "150A88",
"room_id": 2,
"name": "Kitchen",
"serial": "MET1821362",
"battery": 0,
}
To address it via Openhab configuration files, you need to create a thing
and one or more channel
s. A thing
plus channel
is called an item
, and items
are how you build user interfaces and automatic rules.
Here's a single line of configuration:
Number thermostat_kitchen_set_temp "Set Temperature [%.1f °C]" <Temperature> (Thermostat,ThermostatSetter,GroundFloor) {channel="max:thermostat:KET0436364:MET1821362:set_temp"}
This defines something called thermostat_kitchen_set_temp
. It allows you to set the temperature of a thermostat. The final part (channel=..
) is the critical part. It says that the thing is a max thermostate, addressed at KET0436364
(the Cube serial) :MET1821362
(the thermostatic head serial), and we're interested in the set_temp
channel. Most of the other fields are related to how the thing wil be displayed on a screen. The part in brackets assigns the thing to three different Groups.
The boiler relay can be defined at its gpio pin, thus:
Switch boiler_switch "Boiler switch" <heating> (Basement) {gpio="pin:4 initialValue:low"}
The items
format is described in full here.
Once you have set up all your things (full example here), you can set up rules. Here's the rule for turning the boiler on and off:
rule "A valve changed - switch boiler"
when
Member of Valves changed
then
var allClosed = Valves.members.filter[ i | i.state == 0 ].size == Valves.members.size
if(boiler_switch.state == ON && allClosed) {
boiler_switch.sendCommand(OFF)
logDebug("boiler", "Boiler turned off")
}
if(boiler_switch.state == OFF && !allClosed) {
boiler_switch.sendCommand(ON)
logDebug("boiler", "Boiler turned on")
}
end
The full documentation for rules is here. My configuration also has a rule for a software switch to turn on "holiday mode" (every thermostat set to 14C), so I can flip it off when I'm leaving the house for a day or two. There's also a rule to email me when any batteries are low.
Finally, you can set up a simple frontend (with switches, sliders, etc) using a sitemap
. The sitemap driving the phone screenshot above looks like this:
// https://docs.openhab.org/configuration/sitemaps.html#element-type-setpoint
sitemap default label="Heating"
{
Switch item=holiday_switch hoplabel="Holiday mode"
Switch item=boiler_switch label="Boiler"
Switch item=networkdevice_sebphone_present label="Seb at home"
Text item=wall_thermostat_get_temp label="Living space actual [%.1f °C]"
Setpoint item=wall_thermostat_set_temp label="Living space target [%.1f °C]" step=1
Group item=Batteries label="Low batteries [%d]"
Group item=Valves label="Max valve position [%d%%]"
}
This guide covers what you need to know
It's a bit of a fiddle working out how to set up new graphs for the first time, but worth the hassle, as it allows you to fine-tune your programmes.
As well as installing the openhab client (which displays sitemaps
configured as above), I've installed Tasker, as an experiment in automating things when I leave home.
Tasker is a very powerful and complex system for setting up rules for automating stuff on your phone. I've installed it because I something that detects my presence at home, so I can build up some rules for automatically turning on holiday mode. Tasker can use proximity to specific mobile phone masts for this, which uses considerably less battery than GPS. When I'm near a given list of masts, I've set up Tasker to send an HTTP POST to the Openhab REST API to toggle a "Phone presence" switch.
If I decide it's accurate enough, the plan is to put it on my partner's phone too, and then when none of us is in, automatically pop up Openhab on my phone with a message asking if I want to turn on Vacation mode.
In order to access graphana and the openhab sitemap from my phone, I set up my router's firewall to forward direct to the Pi. You'll save yourself some hassle if you forward between identical ports (e.g. 3333
on your router to 3333
on your Pi).
It took a few days to get a system that I was happy with. It would be horrible to lose it all. Backups are important. Here's some notes I made about backing up the system.
As well as a full disk image, I've got incremental backups of the configuration, including a JSON dump of the current programme state (created via [this script](), which hasn't yet been merged into max-cube-python
, so until it is you'll want to use my fork).