Basic guide on turning a dumb boiler into a smart(er) boiler

Here in the UK we recently had a large hike in utility costs and we’re looking at up to another 80% increase in the next month.


(src: What is the energy price cap and how high will bills go? - BBC News)

So for those of us (myself included) that have a “dumb” boiler, I thought I’d share a recent simple project that made my boiler more efficient in it’s consumption by minimising the time it’s running when not needed.

In my home, my boiler has an interface that allows a simple 3times a day on/off schedule that can be programmed for each day of the week. We can set it to 24/7 hot water, off or scheduled.
I can “boost” the heater by hitting the Advance button that starts the boiler heating before entering a scheduled time and it would go off as per the schedule.

This is inefficient for us as we rarely need hot water at the same time each day. Usually we just need 20-30mins for a shower and often forget to switch it off after or in worst cases, on all night.

Prep:
To create this setup the same way we have, you need the following

  • Google Calendar integration watching a scheduling calendar. (Useful if you work a shift pattern).
  • 1x SwitchBot Smart Switch Button Pusher like this: Amazon link
  • (Optional) NFC tags for convenience

If you don’t have a Switchbot hub (I don’t) then you can still use it without. You need:

  • Bluetooth integration (available > 2022.8)
  • A Bluetooth dongle + extension cable if your Bluetooth signal isn’t strong enough to reach your boiler’s location.

Hardware Installation:
With Bluetooth integration configured, As soon as you enable the battery on the Switchbot, your HA will detect it and request you name it and give it a password. The password seems arbitrary, but just remember what you set for the device as you may need it in the future (re-install?)

For my setup, I removed all of the on/off schedules from the boiler interface, but left it on “Timed”. This is so the Advance button still triggers it to start/stop heating water + radiators.

I did a quick test of the Switchbot in my hand with a button card in Lovelace to make sure it was working ok, then I tested the positioning to make sure it would work ok. Which was handy as it needed to be closer to the button than I expected.
Then alcohol wipe and final placement of the Switchbot.

Configuration:
The way I configured this in my system is to have a single Toggle/Boolean that will trigger the script for toggling the SwitchBot. Everything else works around this one Toggle element and it also allows you to manually override any automations.

  1. Create the on/off Toggle
    In Helpers, create your toggle as “Water Heating” (input_boolean.water_heating)

  2. Prep Calendar to be used as a trigger method
    If your Google Calendar uses the "older"method of monitoring events, update your google_calendars.yaml with the search term. e.g.

- cal_id: [email protected]
  entities:
    - device_id: my_haschedule
      ignore_availability: true
      name: HA Schedule
    - device_id: heating
      name: heating
      search: "#heating"
  1. Create the Actuation script
    In Scripts, create the action that will activate the Switchbot
alias: Boiler Toggle
sequence:
  - type: toggle
    device_id: th1s157h3un1qu31df0rmysw1tchb07
    entity_id: switch.the_name_of_my_switchbot
    domain: switch
mode: single
icon: mdi:water-boiler
  1. Create some action scripts
    Now some sample scripts that we can use to preset to boost the boiler for n minutes when requested.

NOTE: Google calendars update every 15 minutes, so this is why we use the internal HA timer rather than letting the calendar manage these short heating scripts.

20 Minute Boost:
(Edit Note: Updated so that re-triggering the script will stop the heating if you don’t want the full 20min)

alias: Boiler Trigger 20 Mins
sequence:
  - if:
      - condition: state
        entity_id: input_boolean.water_heating
        state: "off"
    then:
      - service: input_boolean.turn_on
        data: {}
        target:
          entity_id: input_boolean.water_heating
      - delay:
          hours: 0
          minutes: 20
          seconds: 0
          milliseconds: 0
  - if:
      - condition: state
        entity_id: input_boolean.water_heating
        state: "on"
    then:
      - service: input_boolean.turn_off
        data: {}
        target:
          entity_id: input_boolean.water_heating
mode: restart
icon: mdi:water-boiler

Sleeping:
Trigger as you go to bed so that it’s hot in the morning (assuming you sleep 8hrs)

alias: Boiler Trigger 8hrs
sequence:
  - service: google.create_event
    data:
      summary: Heating
      description: "#heating"
      start_date_time: >-
        {{ (as_timestamp(now()) + (7.5*3600))| timestamp_custom('%Y-%m-%d
        %H:%M:%S', True) }}
      end_date_time: >-
        {{ (as_timestamp(now()) + (8*3600))| timestamp_custom('%Y-%m-%d
        %H:%M:%S', True) }}
    target:
      entity_id: calendar.my_haschedulecalendar
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
mode: single
icon: mdi:water-boiler
  1. Last of all, the 2 automations that do all the work

The first automation monitors the Toggle and actuates the Switchbot:

alias: Hot Water Boiler Actions
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.water_heating
    to: "on"
    id: sensor-on
  - platform: state
    entity_id:
      - input_boolean.water_heating
    to: "off"
    id: sensor-off
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: sensor-on
        sequence:
          - service: script.boiler_toggle
            data: {}
      - conditions:
          - condition: trigger
            id: sensor-off
        sequence:
          - service: script.boiler_toggle
            data: {}
    default: []
mode: single

The second automation watches your calendar and turns the Toggle switch off/on accordingly

alias: Calendar Scheduled - Heating
description: ""
trigger:
  - platform: state
    entity_id:
      - calendar.heating
    to: "on"
    id: event-start
  - platform: state
    entity_id:
      - calendar.heating
    to: "off"
    id: event-end
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: event-start
        sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
              entity_id: input_boolean.water_heating
      - conditions:
          - condition: trigger
            id: event-end
        sequence:
          - service: input_boolean.turn_off
            data: {}
            target:
              entity_id: input_boolean.water_heating
    default: []
mode: single

Display and Control
The way I view these scripts in my dashboard is with mushroom template cards

Heating off:
image

Heating manually:
image

Heating when triggered by a “boost” button:
image

Heating when scheduled:
image

Sample Lovelace card code:

square: false
columns: 1
type: grid
cards:
  - type: horizontal-stack
    cards:
      - type: custom:mushroom-template-card
        primary: >-
          {% if states('input_boolean.water_heating')=='on' %}

          Heating from
          {{(as_timestamp(states.input_boolean.water_heating.last_changed) |
          timestamp_custom('%H:%M'))}}

          {% else %}

          Heating OFF

          {% endif %}
        icon: |-
          {% set showstate = states('input_boolean.water_heating') %}
          {% if showstate == 'on' %}
          mdi:water-boiler
          {% else %}
          mdi:water-boiler-off
          {% endif %}
        layout: vertical
        entity: input_boolean.water_heating
        multiline_secondary: true
        tap_action:
          action: toggle
        hold_action:
          action: more-info
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('input_boolean.water_heating')=='on' %}
          yellow
          {% else %}
          blue
          {% endif %}
        fill_container: false
        secondary: ''
      - type: custom:mushroom-template-card
        primary: Cal 30
        secondary: ''
        icon: |-
          {% set showstate =states('script.boiler_trigger_30_mins_cal') %}
          {% if showstate == 'on' %}
          mdi:water-boiler
          {% else %}
          mdi:water-boiler-off
          {% endif %}
        layout: vertical
        entity: script.boiler_trigger_30_mins
        multiline_secondary: true
        tap_action:
          action: call-service
          service: script.boiler_trigger_30_mins_cal
          data: {}
          target: {}
        hold_action:
          action: none
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('script.boiler_trigger_30_mins_cal')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false
      - type: custom:mushroom-template-card
        primary: >-
          {% if states('calendar.heating')=='on' %}

          {{as_timestamp(state_attr('calendar.heating','start_time')) |
          timestamp_custom('%H:%M')}} -
          {{as_timestamp(state_attr('calendar.heating','end_time')) |
          timestamp_custom('%H:%M')}}

          {% endif %}
        secondary: >-
          {% if is_state('calendar.heating','off') and
          (state_attr('calendar.heating', 'start_time') is not none) %}

          {{as_timestamp(state_attr('calendar.heating','start_time')) |
          timestamp_custom('%H:%M')}} -
          {{as_timestamp(state_attr('calendar.heating','end_time')) |
          timestamp_custom('%H:%M')}}

          {% elif is_state('calendar.heating','off') and
          (state_attr('calendar.heating', 'start_time') == none) %}

          No Schedule

          {% else %}

          {% endif %}
        icon: |-
          {% if states('calendar.heating')=='on' %}
          mdi:timer-check
          {% else %}
          mdi:timer-outline
          {% endif %}
        layout: vertical
        entity: calendar.heating
        multiline_secondary: true
        tap_action:
          action: more-info
        hold_action:
          action: more-info
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('calendar.heating')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false
  - type: horizontal-stack
    cards:
      - type: custom:mushroom-template-card
        primary: 10min
        secondary: ''
        icon: |-
          {% set showstate =states('script.boiler_trigger_10_mins') %}
          {% if showstate == 'on' %}
          mdi:water-boiler
          {% else %}
          mdi:water-boiler-off
          {% endif %}
        layout: vertical
        entity: script.boiler_trigger_10_mins
        multiline_secondary: true
        tap_action:
          action: call-service
          service: script.boiler_trigger_10_mins
          data: {}
          target: {}
        hold_action:
          action: none
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('script.boiler_trigger_10_mins')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false
      - type: custom:mushroom-template-card
        primary: 20min
        secondary: ''
        icon: |-
          {% set showstate =states('script.boiler_trigger_20_mins') %}
          {% if showstate == 'on' %}
          mdi:water-boiler
          {% else %}
          mdi:water-boiler-off
          {% endif %}
        layout: vertical
        entity: script.boiler_trigger_20_mins
        multiline_secondary: true
        tap_action:
          action: call-service
          service: script.boiler_trigger_20_mins
          data: {}
          target: {}
        hold_action:
          action: none
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('script.boiler_trigger_20_mins')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false
      - type: custom:mushroom-template-card
        primary: 30min
        secondary: ''
        icon: |-
          {% set showstate =states('script.boiler_trigger_30_mins') %}
          {% if showstate == 'on' %}
          mdi:water-boiler
          {% else %}
          mdi:water-boiler-off
          {% endif %}
        layout: vertical
        entity: script.boiler_trigger_30_mins
        multiline_secondary: true
        tap_action:
          action: call-service
          service: script.boiler_trigger_30_mins
          data: {}
          target: {}
        hold_action:
          action: none
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('script.boiler_trigger_30_mins')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false
      - type: custom:mushroom-template-card
        primary: +8h
        secondary: ''
        icon: |-
          {% set showstate =states('script.boiler_trigger_8hrs') %}
          {% if showstate == 'on' %}
          mdi:sleep
          {% else %}
          mdi:sleep
          {% endif %}
        layout: vertical
        entity: script.boiler_trigger_8hrs
        multiline_secondary: true
        tap_action:
          action: call-service
          service: script.boiler_trigger_8hrs
          data: {}
          target: {}
        hold_action:
          action: none
        double_tap_action:
          action: none
        icon_color: |-
          {% if states('script.boiler_trigger_8hrs')=='on' %}
          yellow
          {% else %}
          grey
          {% endif %}
        fill_container: false

Summary:
It’s early days, but so far it’s working well. No unnecessary heating of the water when it’s not needed which also includes electrical consumption running the boiler pumps and internals.

Being able to schedule by calendar will be great if you work a pattern of regular or irregular hours.
Or even go on leave so you can plan in advance when to start getting your home warm for your return.

If it’s a second/rental home that’s unoccupied, If you have other sensors inside, if it gets too cold then you could have an automation to heat the home to prevent damp and/or monitor live weather to set up automations to prevent ice from splitting pipes.
Lots of automation ideas that can be applied to a once dumb appliance :slight_smile:

1 Like

Interesting. Would be curious what the actual savings is over time with this. It definitely sounds good to not heat water when you don’t need it. On the other hand when you do need it the boiler has to do more catchup work to heat colder water.

Also would be interested to know if you encounter any PAF issues around this one. I could definitely see such a device taking immediate blame from family members any time hot water runs out, even if its really not its fault.

So far I calculated that my summer gas consumption has gone down by half.
I didn’t use much before, but still the SwitchBot will have paid for itself within 6 weeks from deployment.

For those that have readings as m³, but get billed in kWH, here’s a rough conversion.
It depends on your altitude / atmospheric pressure, but this will be ballpark

m³ reading x * (1.02264*39.2/3.6)
or easier :
m³ reading x 11.4
e.g. 30m³ = 342kWH

To include in a template conversion sensor:
{{ ((states.input_number.gas_meter_reading.state) | float * (1.02264*39.2/3.6)) | round(0) }}

Here’s a visual of improvements in consumption.
For reference, I’m in the SE to the West of London and July was the hottest month in UK record (July 19th hit 40C (105F)).

We’re much cooler atm and have needed a little heat boost for some mornings and late evenings, but because we’ve been able to be more selective as to when and how long we use it, it’s resulted in less consumption even compared to that hottest month.

Here are some monthly mean temperatures for some of 2021 and 2022 to compare the difference in use vs climate. Our gas is for heating only as we have electric kitchen appliances.

2021 Oct - Dec

2022 June - Sept