Automating Our Heating

A little while ago, I wrote some musings on Home Automation and made reference to our heating setup.

As it's had a bit of time (and some poor weather) to run and be improved upon, I thought it might be helpful/interesting to lay out a bit more detail on the setup I'm now using.

We got a NEST thermostat during an unexpected boiler replacement, unfortunately it's smart features didn't live up to expectations, trying to overcome that led me down the path that I'll describe in this post.

Requirements

My intention was that the eventual system should meet a few basic requirements

  • Certain rooms should be able to call for heat when needed (not supported by the NEST product line as they don't currently have radiator heads)
  • Boiler/Heating usage should be minimised where comfortably possible
  • Decisions should be auditable - ok, the heating came on, but why?

 

 

HomeAssistant

The core of my Home Automation setup is HomeAssistant, running on a dedicated Raspberry Pi 4.

I had initially run it in a VM, but decided that I didn't want my automations to go down if my VM host died for some reason, a decision that was then reinforced by the host's USB passthrough sometimes locking up.

HomeAssistant has a Web-interface (and Android app) that you can create automations through. For ease of sharing, I'll use the underlying YAML in this post, but the reality is that when you create/edit them, they tend to be in this (more accessible) format

 

Topology

The physical toplogy is rather simple:

Heating automation topology

We have a bunch of Zigbee temperature sensors in rooms around the house (the diagram only lists those directly relevant to this post).

HomeAssistant processes events from those sensors, and if needed triggers automations - using NEST's API (via HTTPS) in order to trigger changes on the thermostat (the next time we need to replace the thermostat, it'll almost certainly be a Zigbee or ZWave thermostat going in so that the dependency on our internet connection is removed).

There are also some smart sockets/switches hooked up to electric heating devices - I'll cover how these are controlled next.

 

Auxillary Heating

Although we've central heating, there are 2 rooms where this isn't sufficient.

Our shower room doesn't have a radiator in at all, instead having an electric towel rail - when we first moved in it was on a timer + thermostat. In effect I've replicated this but a little smarter by connecting the rail up to a (suitably rated) smart switch.

The second room is our attic room (room in roof).

There's a radiator up there, but being an attic room, sometimes it needs a little extra help. Triggering the central heating to warm this room might lead to other rooms getting uncomfortably hot, so an electric oil radiator is used as an independent backup  - this is plugged into a smart socket.

Both are paired with their appropriate temperature sensor to create a generic thermostat in Home Assistant

climate:
  - platform: generic_thermostat
    name: Shower Room
    heater: switch.feibit_fnb56_skt1jxn1_0_302e1804_on_off
    target_sensor: sensor.shower_sensor_temperature
    min_temp: 7
    max_temp: 19
    target_temp: 9
    min_cycle_duration: 00:05:00
    hot_tolerance: 0.5
    cold_tolerance: 1.0
    away_temp: 8
    initial_hvac_mode: "heat"
    precision: 0.1

Which gives us a temperature controller in HomeAssistant

The shower room's default temperature is set at a level (9c) that should prevent mould and/or frozen pipes.

However, it's not that comfortable a temperature for having a shower, so we've also got an automation to bring the temperature up ahead of evening showers as well as one to turn it back down

- id: '1612009479672'
  alias: Shower Room Sunday Night Heat
  description: ''
  trigger:
  - platform: time
    at: '16:00'
  action:
  - service: script.towel_rail_power_on
    data: {}
  - service: notify.mobile_app_claire_mi_mix_2
    data:
      message: Shower room started warming for showers
      title: Shower Room Warming
  - service: notify.mobile_app_bens_mi_mix_2
    data:
      message: Shower Room started warming
      title: Shower Room Warming
  mode: single

# Reset the value back down
- id: '1608568721514'
  alias: Towel Rail Off Overnight
  description: ''
  trigger:
  - platform: time
    at: '22:30'
  condition: []
  action:
  - service: script.towel_rail_power_off
    data: {}
    entity_id: climate.shower_room
  mode: single

We can see in HomeAssistant's graphing when the towel rail turned on, as well as what the measured (and target) temperatures were

Shower room temperature graph

 

The room-in-roof Oil radiator has a similar underlying setup, but I'll cover that in a bit more detail later.

 

NEST Temperature Control and scheduling

The main central-heating temperature scheduling is still achieved via NEST's app

Part of the reason for this is that there just isn't a good calendar scheduling interface like this in HomeAssistant. I rarely need to make changes, so it isn't yet with the effort of trying to replace it.

 

Oil Radiator Scheduling

The Room-in-Roof heater though, did need scheduling - there are only certain times of day that it's actually needed.

As the needs were fairly simple, I created 2 schedules - weekday and weekend

This then feeds into a pair of automations which control the state of the generic thermostat

- id: '1608736437897'
  alias: Attic Weekday Room Heating On
  description: ''
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.sensor.attic_weekday_on.state  }}'
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: input_boolean.attic_weekday_heating_on
      state: 'on'
    - condition: time
      weekday:
      - mon
      - tue
      - wed
      - thu
      - fri
  action:
  - service: climate.turn_on
    data: {}
    entity_id: climate.attic_room

- id: '1608738383422'
  alias: Attic Weekday Room Heating Off
  description: ''
  trigger:
  - platform: template
    value_template: '{{ states.sensor.time.state == states.sensor.attic_weekday_off.state  }}'
  condition:
  - condition: time
    weekday:
    - mon
    - tue
    - wed
    - thu
    - fri
  action:
  - service: climate.turn_off
    data: {}
    entity_id: climate.attic_room
  mode: single

(There's a separate pair for the weekend)

So, at the configured time, the thermostat will be changed to auto mode (i.e. try to maintain configured temperature) and then at the off time it'll be switched back to off (turn off the switch and don't attempt to heat).

 

Not Heating Unused Rooms

One of the aims of this setup was to minimise boiler usage where possible, in order to help achieve that, I decided it was better not to actively heat (at least, within reason) rooms at times we knew that they'd not generally be occupied - ensuring energy used at the boiler led to heat going into rooms that mattered.

The scheduler above already shows an example of that - we're only allowing oil radiator backup at certain times of day. I wanted to go further though, and there were 3 obvious candidates:

  • My office (not generally occupied overnight, callouts excepted)
  • Guest bedroom (playroom when no guests here, but not occupied during the day/night in termtime)
  • Attic Room (only occupied at certain times)

In order to experiment with this as cheaply as possible, I bought some Honeywell programmable Thermostatic Radiator Valve heads.

Unfortunately, I was unable to fit one in the attic room as the valve is a non-standard size - replacing that is on my list for when the weather improves this year.

I was able to fit a head in both my office and the Guest bedroom though, so configured a schedule for each:

  • My office: heat normally during the day, only heat if <12 outside core ours. Reducing potential active heating time by 77hrs/week
  • Guest Bedroom: Don't heat unless <12 during day, allow warming for 3 hours in evening, overnight only if <12. Reducing potential active heating time by 168hrs/week

The Guest Bedroom schedule obviously gets changed if we have guests, and/or during school holidays etc. The result is that there are 2 (large) radiators that are only really warmed when they're actually needed, with the "low" temperature still set high enough to prevent condensation build ups.

The Guest bedroom also has one additional control in HomeAssistant that's was added to help support the functionality I will describe next

 

Overnight Heating Boosts

One of my requirements was that rooms should be able to call for heat.

In the initial iteration, only the attic room really had this kind of granular control - via the electric oil radiator. But, heating a room with an electric heater is much more expensive that using gas central heating.

So, what I wanted was to give rooms the ability to turn the heating back on for a short period to bring temperatures up a bit.

Unfortunately, the NEST thermostat doesn't have a built in "boost" feature (i.e. turn the temperature up for a short-time and then revert), so I had to create my own

boost_heating:
  alias: Boost Heating
  sequence:
  - service: input_number.set_value
    data:
      value: '{{ state_attr(''climate.downstairs'',''temperature'') }}'
    entity_id: input_number.current_heating_target
  - service: climate.set_temperature
    data:
      temperature: '{{ float(states(''input_number.current_heating_target'')) + float(states(''input_number.heating_boost_degrees''))
        }}'
    entity_id: climate.downstairs
  - delay: 00:30
  - service: climate.set_temperature
    data:
      temperature: '{{ float(states(''input_number.current_heating_target'')) }}'
    entity_id: climate.downstairs
  mode: single

The script uses a couple of input fields in order to keep state (so that we know what temperature to set the heating back to at the end of the boost), as well as inputs to control the boost amount we're boosting by, and provide a kill switch for the automation

Heating boost control

When triggered, the script will 

  1. Check the current target temperature of the thermostat
  2. Write that value into input_number.current_heating_target
  3. Add the value of the slider (so in this case 3) to that value and call set the thermostat to target that temperature
  4. Wait 30 mins
  5. Set the thermostat to target the value of input_number.current_heating_target

The automation that calls it will only trigger if the switch is on (the switch isn't checked in the script itself because I also have a button we can press to trigger the boost).

That automation runs once an hour and checks whether any of a set of rooms has fallen below a threshold - if one has then it'll trigger a boost

- id: '1609241108360'
  alias: Overnight Heating Boost
  description: See HOME-138
  trigger:
  - platform: time
    at: 01:00
  - platform: time
    at: 02:00
  - platform: time
    at: 03:00
  - platform: time
    at: 04:00
  - platform: time
    at: 05:00
  - platform: time
    at: 06:00
  condition:
  - condition: and
    conditions:
    - condition: state
      entity_id: input_boolean.automatic_overnight_heating_boost
      state: 'on'
    - condition: or
      conditions:
      - type: is_temperature
        condition: device
        device_id: 89e28239a5517cca844b899b9eb7d277
        entity_id: sensor.attic_sensor_temperature
        domain: sensor
        below: 15
      - type: is_temperature
        condition: device
        device_id: 8de14032380e2449a13b3e7b6b0caffb
        entity_id: sensor.bedroom_temperature_temperature
        domain: sensor
        below: 16
      - type: is_temperature
        condition: device
        device_id: 632bf4beb88992215b7c284bbd695ab2
        entity_id: sensor.livingroom_temperature
        domain: sensor
        below: 17
      - condition: and
        conditions:
        - type: is_temperature
          condition: device
          device_id: 4644d3d19771eb3efaf37a8d0e98c49e
          entity_id: sensor.playroom_temperature_temperature
          domain: sensor
          below: 16
        - condition: state
          entity_id: input_boolean.playroom_overnight_heating
          state: 'on'
  action:
  - service: script.boost_heating
    data: {}
  mode: single

The Playroom/Guest Room trigger is tied to whether the toggle (shown above) is turned on or not.

I decided to initially run it as an hourly check to minimise the chances of 2 runs clashing - because the house holds heat quite well it didn't really need to be any more regular than that.

We can see in the graphing that this triggered once last night at around 2am

Problems

Generally, this automation works quite well, however there have been isolated instances where the boost didn't turn off.

This has almost always been because the NEST API went unavailable, whether because my internet went down, or because the API itself had stopped working.

When this happens, the temperature stays set to the boost temperature until the next scheduled change in the NEST app.

This is the other part of why I've not put any effort into migrating routine heating schedules into HomeAssistant, the NEST schedule acts as a sort of safety net in case HomeAssistant's comms to the API fail.

It'd be nice if it was a little more resilient, but it is what it is, and I'm sure I'll think of some further improvements that can be made.

 

Reporting

One of the major advantages of using HomeAssistant is also that I can generate graphs to show temperature/humidity/heating usage over time.

This isn't really all that interesting on a day-to-day basis, but starts to come in useful when you're working on other projects. For example, I used the following to help check what difference I'd made by increase insulation around the attic room

The blue line is the attic temperature over the course of a week - the red is the thermostat temperature (included to compare whether the house temperature shifted much).

We can see that the troughs in attic room temperature are quite a bit shallower in the days following insulation, but the house temperature didn't shift much (so the attic improvement is unlikely to be due to better weather).

It's not just graphing though - changes are recorded in HomeAssistant's Logbook so it's possible to look at what changed the state of something to help understand why

Although I don't currently do so, it's also possible to call logbook.log from within scripts/automations so that you can log details of decisions/states etc.

The other, subtle, advantage here is the amount of time you can collect and check data over. NEST's app will give you a view of heating over the last fortnight, with HomeAssistant you can do month on month, or even Year on Year comparisons.

 

Avoiding Waste - Door Sensor

I made passing mention of this in my previous post, but since then have bought the sensor and implemented.

We've a set of french windows that we routinely use. When they're left open a draught runs across the thermostat causing the heating to come on. With a radiator right by the french windows we're then, effectively, warming the world around us with little change in the house.

In a grumpy old dad moment, I decided that door wasn't going to stop being left open/ajar, so instead implemented a technical solution.

The door has a Zigbee Door Sensor attached to it, which fires an event when the door is opened or closed, triggering the following automations

- id: '1612540698192'
  alias: French Window Open
  description: ''
  trigger:
  - type: opened
    platform: device
    device_id: dd09042ebb0957cee87d1dc4dd23e853
    entity_id: binary_sensor.french_windows_ias_zone
    domain: binary_sensor
    for:
      hours: 0
      minutes: 10
      seconds: 0
  condition: []
  action:
  - service: input_boolean.turn_on
    data: {}
    entity_id: input_boolean.heating_stingy_mode
  - device_id: 3c31ca4fc6ac0c95c41f3f8c9948c2aa
    domain: climate
    entity_id: climate.downstairs
    type: set_hvac_mode
    hvac_mode: 'off'
  - service: notify.mobile_app_claire_mi_mix_2
    data:
      message: Door open - heating off
      title: Door
  - service: notify.mobile_app_bens_mi_mix_2
    data:
      message: Door open - heating off
      title: Door
  mode: single

When the door's opened, it triggers a timer. If the door is still open 10 minutes later then it'll

  1. Set input_boolean.heating_stingy_mode to on
  2. Tell the central heating to turn off
  3. Send our phones a notification to say this has happened

There's another corresponding automation that'll turn the heating back on once the door has been closed for 5 minutes (but only if input_boolean.heating_stingy_mode is on - i.e. the heating was turned off by the automation).

The existence of the input_boolean also means I can see when the automation was triggered in HomeAssistant's logbook

 

Conclusion

It's not perfect, but what we've got as a result is a heating system that's controllable and auditable - no more trying to work out why the hell NEST has had the boiler on for 5 hours.

As I understand it, it is possible to achieve some of this functionality using HIVE's system - as well as the thermostat they sell radiator valves so that rooms can call for heat.

However, with HIVE's system

  • you still don't get the same level of auditability
  • the setup still appears (unfortunately) to rely on your internet connection as a hard dependency
  • There's a significant amount of vendor lock-in, you'd need to replace the whole system if it didn't meet requirements

The setup we've got doesn't suffer from these in quite the same way (with the exception of NEST needing the internet). Given the choice between NEST and Hive, I think I'd now go HIVE, but realistically the next time I'm in the market, my choice will probably be "neither".

Our energy usage has reduced with each iteration of this project - allowing rooms to call for heat reduced the amount of time that the attic room oil radiator drew power - and the house remains a comfortable temperature at the times it needs to.

When I first set out on this project, NEST was burning gas for anywhere between 5 and 8 hours a day (caused in no small part by it's True Radiant pre-warming), currently it's averaging 2-4 hours. Whilst part of that is, undoubtedly, the result of improvement in weather, we've had a few short-lived cold-snaps recently that have shown that there is definitely some improvement there.

Although a bit of effort went into getting HomeAssistant setup, it's not single purpose - it's no longer just heating that we're controlling with it, though I can write about some of the other uses another time.