My original post about home automation discussed the fact that one of my motivations was improving control over my central heating system. In the last few weeks I’ve finally brought enough pieces together to make that a reality. My boiler is controlled by a Siemens RCR10/433 thermostat. Ross Harper has a good write-up about decoding the Siemens RCR10/433 and I was able to extend my Energenie Atmel 433MHz transmitter to treat the boiler as another switch. Slightly different timing than the Energenie switches, but exactly the same principle. My TEMPer USB clone provides a reading of the living room temperature. Finally mqtt-arp lets me work out whether anyone is home or not.

Everything is tied together with Home Assistant. The configuration has ended up more involved than I expected, but it’s already better than the old 24 hour timer. There’s definitely still room for improvement in terms of behaviour as the weather starts to get colder and I collect further data. Presently it looks like this:

Home Assistant heating controls

Top left is the control card; “Heating” is a climate control with a target temperature and a current temperature (from the living room sensor), an on/off state for the boiler (linked to the 433MHz transmitter), a switch to indicate if the timer is on or not and finally a slider to control what the target temperature should be when the heating is active.

Top right is a history card showing the various temperature sensors around the house as well as the target temperature state of the heating over the past 24 hours.

The bottom two cards show the timer times for week days and weekends. I did consider making a full 7 day timer, but this ends up good enough and I couldn’t find a better way to represent a set of start + end times that would have allowed a clean interface display of a full week. The times control when the “Heating timer” control in the top left is switched on + off.

These 4 cards provide the ability to see the current state of the heating, and tweak it, ideally meaning there’s no need to hand edit config files during normal operation. Rough theory of operation is:

  • If the timer is on and someone is at home, raise the target temperature to the value set in the temperature slider.
  • If the timer turns off or everyone leaves the house, lower the target temperature to 5°C.

The core is a generic thermostat:

climate:
  - platform: generic_thermostat
    name: Heating
    heater: switch.gas_boiler
    target_sensor: sensor.living_room_temperature
    min_temp: 5
    max_temp: 25
    ac_mode: false
    hot_tolerance: 0.5
    cold_tolerance: 0.1
    min_cycle_duration:
      minutes: 5
    keep_alive:
      minutes: 30
    initial_operation_mode: 'auto'

This is always active, and climate.set_temperature used to control what the target temperature is.

The temperature control slider is a simple input_number:

input_number:
  heating_temperature:
    name: Temperature
    min: 5
    max: 25
    step: 0.5
    icon: mdi:thermometer

The timer is where it gets complex. There are 8 input_datetime entries to deal with the different start/stop times. It seems like there should be an easier way, but this is what I have:

Heating start/stop time inputs
input_datetime:
  weekday_morning_start:
    name: Week day morning start
    has_time: true
    has_date: false
  weekday_morning_stop:
    name: Week day morning stop
    has_time: true
    has_date: false
  weekend_morning_start:
    name: Weekend morning start
    has_time: true
    has_date: false
  weekend_morning_stop:
    name: Weekend morning stop
    has_time: true
    has_date: false
  weekday_evening_start:
    name: Week day evening start
    has_time: true
    has_date: false
  weekday_evening_stop:
    name: Week day evening stop
    has_time: true
    has_date: false
  weekend_evening_start:
    name: Weekend evening start
    has_time: true
    has_date: false
  weekend_evening_stop:
    name: Weekend evening stop
    has_time: true
    has_date: false

For the automations I also needed to add a time & date sensor:

sensor:
  - platform: time_date
    display_options:
      - 'time'

And finally the output input_boolean to represent if the timer is active or not:

input_boolean:
  heating_timer:
    name: Heating timer
    icon: mdi:toggle-switch

These get tied together with a bunch of automations:

Automations for heating timer
automation:
  - id: heating_morning_on_wd
    alias: Turn heating on (weekday mornings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekday_morning_start.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
    action:
      service: input_boolean.turn_on
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_morning_off_wd
    alias: Turn heating off (weekday mornings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekday_morning_stop.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
    action:
      service: input_boolean.turn_off
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_evening_on_wd
    alias: Turn heating on (weekday evenings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekday_evening_start.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
    action:
      service: input_boolean.turn_on
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_evening_off_wd
    alias: Turn heating off (weekday evenings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekday_evening_stop.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
    action:
      service: input_boolean.turn_off
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_morning_on_we
    alias: Turn heating on (weekend mornings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekend_morning_start.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - sat
        - sun
    action:
      service: input_boolean.turn_on
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_morning_off_we
    alias: Turn heating off (weekend mornings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekend_morning_stop.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - sat
        - sun
    action:
      service: input_boolean.turn_off
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_evening_on_we
    alias: Turn heating on (weekend evenings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekend_evening_start.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      - condition: time
        weekday:
          - sat
          - sun
    action:
      service: input_boolean.turn_on
      data_template:
        entity_id: input_boolean.heating_timer

  - id: heating_evening_off_we
    alias: Turn heating off (weekend evenings)
    trigger:
      platform: template
      value_template: "{{ states('sensor.time') == (states.input_datetime.weekend_evening_stop.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}"
    condition:
      condition: time
      weekday:
        - sat
        - sun
    action:
      service: input_boolean.turn_off
      data_template:
        entity_id: input_boolean.heating_timer

The timer boolean switch and the group.all_devices presence information are then tied together to raise/lower the target temperature as appropriate. I’ve used 4 automations for this - one triggered for timer on, one for timer off, one for someone arriving at home, one for everyone leaving. Again, there might be a better way, but this does what I need:

Automations to raise/lower target temperature
automation:
  - id: heating_timer_on
    alias: Turn heating on based on timer
    trigger:
      platform: state
      entity_id: input_boolean.heating_timer
      to: 'on'
    condition:
      condition: state
      entity_id: group.all_devices
      state: 'home'
    action:
      service: climate.set_temperature
      data_template:
        entity_id: climate.heating
        temperature: "{{ states('input_number.heating_temperature') }}"

  - id: heating_timer_off
    alias: Turn heating off based on timer
    trigger:
      platform: state
      entity_id: input_boolean.heating_timer
      to: 'off'
    action:
      service: climate.set_temperature
      data:
        entity_id: climate.heating
        temperature: 5

  - id: heating_on_when_get_home
    alias: Turn heating on on arrival if timer on
    trigger:
      platform: state
      entity_id: group.all_devices
      from: "not_home"
      to: "home"
    condition:
      condition: state
      entity_id: input_boolean.heating_timer
      state: 'on'
    action:
      service: climate.set_temperature
      data_template:
        entity_id: climate.heating
        temperature: "{{ states('input_number.heating_temperature') }}"

  - id: heating_off_when_leave_home
    alias: Turn heating off when we leave home
    trigger:
      platform: state
      entity_id: group.all_devices
      from: "home"
      to: "not_home"
    action:
      service: climate.set_temperature
      data:
        entity_id: climate.heating
        temperature: 5

Finally there’s the UI configuration, which I’ve done using Lovelace. The use of ‘:’ as the name for the climate.heating element is a kludge - I haven’t figured out yet how to name each individual data entry it adds to the history graph. I’m not particularly fond of the input method for controlling times - something closer to the Android analog clock with digits at the top would be nicer, but I’m not a UI guy and this works well enough.

Lovelace configuration for heating controls
views:
  - title: Heating
    cards:
      - type: entities
        title: Controls
        show_header_toggle: false
        entities:
          - climate.heating
          - switch.gas_boiler
          - input_boolean.heating_timer
          - input_number.heating_temperature

      - type: history-graph
        title: Temperatures
        entities:
          - entity: sensor.kitchen_temperature
            name: Kitchen
          - entity: sensor.living_room_temperature
            name: Living Room
          - entity: sensor.master_bedroom_temperature
            name: Master Bedroom
          - entity: sensor.outside
            name: Outside
          - entity: sensor.study_temperature
            name: Study
          - entity: climate.heating
            name: ":"

      - type: entities
        title: Week day Timer
        entities:
          - input_datetime.weekday_morning_start
          - input_datetime.weekday_morning_stop
          - input_datetime.weekday_evening_start
          - input_datetime.weekday_evening_stop

      - type: entities
        title: Weekend Timer
        entities:
          - input_datetime.weekend_morning_start
          - input_datetime.weekend_morning_stop
          - input_datetime.weekend_evening_start
          - input_datetime.weekend_evening_stop