Heating X: Schedule Thermostats with Calendars (DEPRECATED)

Heating X offers comprehensive control of one or more heater or radiator thermostats in a zone using a calendar. Features include manual override, plus optional door/window opening sensors and/or occupancy sensors.

Note: the word ‘Zone’ is used here in the plumbing sense – typically a room, or a Home Assistant ‘area’, but can also be a single heater, such as a hot water cylinder, for which no door, window or occupancy sensors would be specified. It is unrelated to map zones. Invoke the Blueprint once for each zone in your home.

  • Thermostat(s): One automation typically controls one thermostat from a calendar, but you can specify more than one if, for example, you have two radiators in the same room. If you specify more than one thermostat for a zone, they will share the same calendar and the same door/window and occupancy sensors if applicable; a manual override on one is synchronised to the others.

  • Calendar: Sets heating temperature according to your specification in events in a calendar for each zone.

  • Manual override: Following a manual change – on one of the thermostat devices itself, on the dashboard, or by your voice assistant – that temperature is adopted for a defined period on all the thermostats in the same zone. After the defined period, the temperature setting reverts to the schedule.

  • Door or window open sensors (optional): one or more door or window open sensors can be specified; then, if any of those doors or windows is left open for a defined period the heating is turned off. When all doors and windows are closed, the temperature reverts to the previous manual or scheduled setting.

  • Room occupancy sensors (optional): one or more room occupancy sensors can be specified; then, if the room is unoccupied for a defined period, the heating is turned off. When the room is occupied again, the temperature reverts to the previous manual or scheduled setting.

The following description, the Blueprint defaults, and the temperature icons assume that you are using Celsius as the temperature scale, but they could easily be adapted to Fahrenheit.
It is assumed that Heating X is used for heating, but it could be readily adapted for cooling.

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

… Or use this link for import in HA https://gist.github.com/AndySymons/911ac751480ca62ce2a562e62b59f3ac


1. Install the physical devices

  • Thermostats. Thermostats can be physical devices such as radiator thermostats, or a Generic Thermostat that links a temperature sensor and a switch – anything with domain ‘climate’.
  • Door or window opening sensors (optional). Install contact sensors or similar as required. They should have device class ‘opening’. If you have none, the Heating X automations will act as if all doors and windows are always closed.
  • Occupancy sensors (optional). Install a PIR motion sensor, mm Wave human presence sensors or similar as required. They should have device class ‘occupancy’. If you have none, Heating X automations will act as if the zone is always occupied.

2. Set up the calendars

  • For each zone, create a calendar using the Home Assistant Local Calendar integration (you need Home Assistant 2022.12.1 or later).
  • Add events to the calendars for each period that you want to specify the temperature. In the summary field put the name you want to appear on the dashboard – e.g., “Daytime setting”, “Night setting”, “Evening boost”. Set the events to repeat daily or on specific days of the week as required.
  • Add the required temperature anywhere in the description field between two hashes: e.g., “Set temperature to #21.5# C”. Do not use another hash before the one that introduces the temperature, or write anything else between the hashes
  • Events will be treated as consecutive if a new one starts within 2 minutes after the previous one ended. Otherwise, in the gap between events the heating is turned off.

3 (a). Create Template Sensors and Helpers using the Code Generator

The blueprint requires a number of template sensors helpers and helpers. You can created these by hand, but the simplest method is to use my Cone Generator
AndySymons / Heating-X-code-generator

The Code Generator uses Microsoft Mail Merge, so you will need a system running Microsoft Office (sorry, I could not find a free app for this).

It generates all the YAML code you need to define the template sensors and helpers required by the Blueprints for any size of installation. You simply list your zones and radiators in an EXCEL spreadsheet, then use WORD files with Mail Merge toı create the YAML. Full instructions are included in the ZIP file.

3 (b). Create Template Sensors and Helpers by hand

If you cannot or do not want to use the Code Generator, you can create the following template sensors and helpers by hand for each automation (heating zone) that you are going to create with the blueprint.

  • Thermostat set temperatures – for each of the thermostats. Here is an example template
### radiator X set temperature 
- sensor: 
  - name: Radiator X set temperature
    unique_id: "5f2be1c9-79a0-410f-b33d-139bbc7f9623"
    unit_of_measurement: 'C'
    icon: mdi:thermometer
    state: "{{ state_attr('climate.radiator_x_thermostat', 'temperature') }}"

Create the following helpers for each zone:

  • input_text helpers:
    • Event name – to hold the name (aka Summary) of the current event
    • Event description – to hold the description of the current event
    • Setting reason – into which the automation writes the reason for the current setting (for use
  • input_datetime helpers
    • Event start – to hold the start date and time of the current event
    • Event end – to hold the end date and time of the current event
  • input_number helpers
    • Event temperature – to hold the temperature specified in the current event
    • Manual temperature – to hold the temperature specified by manual control
  • Timer helpers
    • Warmup timer – to hold the timer for the event warmup period
    • Manual override timer – to hold the timer for a manual intervention
    • Door or window open timer – to hold the time until the heating will be turned off following a door or window opening (is paused when they are all closed)
    • Room unoccupancy timer – to count down the time until the heating will be turned off following the room becoming unoccupied (is paused when it is occupied)
    • Echoblock timer – for use inside the automation to distinguish genuine manual changes of the set temperature from those set by the automation

4. Create automations by invoking the Blueprint

Use the Heating X Blueprint to create an automation for each zone.

  • The Blueprint inputs.

    • Calendar: Identify the calendar for this zone that you set up in step 2.
    • Thermostats – identify the thermostat entity or entities in the current zone.
    • Thermostat set temperature sensors – for each of the thermostats you just specified
    • Door or window opening sensors (optional)
    • Occupancy sensors (optional)
  • Now identify each of the helpers for this thermostat automation, created in step 4:

    • Event name (aka Summary)
    • Event start
    • Event end
    • Event description
    • Setting reason
    • Event temperature
    • Manual temperature
    • Warmup timer
    • Manual override timer
    • Door or window open timer
    • Room unoccupancy timer
    • Echoblock timer
  • Finally, you have the opportunity to modify a number of constant parameters used by the automation. They all have defaults so you might want to skip this step at first and come back later to tune the automation.

    • Frost setting – the temperature to be used when the heating is turned off. Default 5. Must not be lower than the minimum accepted by the thermostat(s).
    • Minimum thermostat temperature – the minimum temperature that your thermostat can be set to (default 5C)
    • Maximum thermostat temperature – the maximum temperature that your thermostat can be set to (default 95C)
    • Door or window open delay time – the time for which a door or window can be open before the heating switches off. Default 3 minutes.
    • Room unoccupancy delay time – the time for which the zone can be unoccupied before the heating switches off, except during a warm-up period. Default 1 hour.
    • Warmup period – the period of time from the start of a new event for which room unoccupancy will be ignored. Default 2 hours.
    • Manual override period – the time period for which a manual intervention will override the schedule. Default 2 hours.
    • Repeat step 6 for each of your zones. If you change your mind about how to group devices to an zone, go back to step 4.

5. Design your dashboard

For testing, you might like to display a thermostat card and many or all of the helpers in an entities card, all grouped in a vertical stack.

For an end-user it suffices to see a thermostat card and a field that says what event is active (if any) and the reason for the current setting. My suggested design has a card for each zone that displays a half-sized thermostat card for each thermostat in the zone, followed by a half-sized Markdown card with the name (summary field) of the current event and the reason the thermostats have their current setting.

I use the custom “Better Thermostat” card, so if you want to try my card design, you need to install that first from HACS, or change the code to use a standard thermostat card.

  • On your dashboard, select ‘Edit dashboard’ and add a card. Select the ‘Manual’ card.
  • Delete any YAML that is already there, then copy and paste the code below.
type: vertical-stack
  - type: custom:mushroom-title-card
    title: Heating X
  - square: false
    columns: 2
    type: grid
      - type: custom:better-thermostat-ui-card
        entity: climate.radiator_x_thermostat
        eco_temperature: 5
        disable_window: true
        disable_summer: true
        disable_eco: true
        disable_heat: true
        disable_off: true
        set_current_as_main: false
      - type: markdown
        content: |-
          **Schedule**: {{states('input_text.radiator_x_event_name')}}
          **Status**: *{{states('input_text.radiator_x_setting_reason')}}*

  • Change the entity names ‘radiator_x’ to yours
  • Save the card.
  • Repeat step 5 for each zone in your home.

You’re done!



Just tried to use your blueprint that seems great thank you for your work but I’m stuck with status going “Turned off because a door or window is open” and temperature setpoint to Frost Temperature configured in the automation.

How ‘stuck’ exactly? This is the expected behavior if you have a door or window sensor that is reporting ‘open’ for more than the set period. It will return to the manual or scheduled setting when all doors and windows are closed. If you do not want this feature you can just leave the list of door and window sensors empty. If you are simulating a closure sensor with a template sensor, remember that ‘on’ means ‘open’ (whereas the switch in the sensor is off).

Actually, I have an idea what you might mean… I found a problem when I first created the automations for a new whole-house project (currently beta-testing). At start-up timers are at idle and that is taken to mean that a door or window is open. If you have a sensor, then opening it and closing it again makes this go away (so I did not find this in alpha-testing on the workbench).

That is not satisfactory and a problem if you do not have any sensors and probably after a restart. So I have just published a revision (dated 12-Feb) to GitHub that has an extra statement where the door and window open timer is found to be idle, to additionally check that there is in fact a door or window open. There is an equivalent statement for the occupancy sensors.

Thanks for your interest and do let me know how you get on.

I am currently working on a calendar problem: the details are only updated when a new event starts; if you have repeated all-day events (same temperature day and night) then apparently there is never a start event and it never gets updated. So if you make a short test event, when that finishes the correct event details become unknown.

Hi Andy,
Sorry I should have specified that I’m not using door or presence detector for now.

I saw the modification that you made and it seems OK for me I’ll update the blueprint and let you know tomorrow after some events pass.

Thank you for your script, I’m new to HA but as a programmer it’s pretty easy to understand (reading, not writing). YAML is a little strange to me and working with triggers, conditions, events is something that I’ll definitely have to learn.

This is my first published blueprint too :slightly_smiling_face: I have 40 years in IT but am new to HA and YAML … so feel free to criticise.
I have noted the known issues on the Github page.

thanks a lot for your work.
I just finished installing the first automation. I am a total beginner so it was a bit rough at first, but know I am quiet happy, as everything seems to work.(Still need to test around). But I have a question: Did you ever consider integration a “pre-heating” time? So if the event starts at 13:00, that the heating starts 1 hour earlier?

kind regards,

Why would that be useful? Just start the event an hour earlier.
I do have a ‘warmup period’ that ignores presence detection, so you can warm up a room before it is actually in use.

Hi Andy,
Thanks for your great work on putting this together. I have yet to try it out but it looks like this will save me a lot of work!

I’m implementing a heating control solution for a church building with multiple rooms that are let out, driven by room booking information coming from Hallmaster via an ical feed. We’re trialling an air-source heat pump in one room, that I want to drive from the bookings system.

So a ‘heat-up period’ would be useful as part of your solution for this situation. Alternatively, I will build that into my code that puts the room booking information into the HA calendar.

Any thoughts on the viability of this addition?

Many thanks,

Hi have followed your steps, and my Heating X is working manually, but a scheduled event on the calendar is failing with the start and end schedule and created an area but still no luck.

Not sure what you mean by a heatup period? I already have a ‘warm-up’ period during which presence sensors are ignored, so the heating is not shut off when no one is in the room. If the heat pump is controlled by an on/off switch then you can control is with generic thermostat and declare that to Heating X.

Do you get any error message? Not clear what you mean by ‘failing with the start and end schedule’. The main thing is to be sure you put the required temperature in the description field between two hashes e.g. 'Temperature #21.5# – and don’t use any hashes before that (the first two are used)

Hi Andy I have declared the generic thermostat, but for some reason, the X - Heating automation fails to switch on the geyser. I have set my #64# value in the calendar, and the Scrip goes through all the steps. I think I am missing something in your instructions. Can you look at a trace and check where I am missing some of your steps? Thx for your help.

Hi Andy I have checked the Geyser 2 guest bedroom control thermostat set temperature, and the state attribute is unknown.

The following are declared in my configuration.yaml:

  • platform: generic_thermostat
    name: geyser_2_guest_bedroom_control_thermostat
    heater: switch.sonoff_100178ffbb
    target_sensor: sensor.sonoff_100178ffbb_temperature
    “# min_temp: 15”
    “# max_temp: 21”
    “# ac_mode: false”
    “# target_temp: 17”
    “# cold_tolerance: 0.3”
    “# hot_tolerance: 0”
    “# min_cycle_duration:”
    “# seconds: 5”
    “# keep_alive:”
    “# minutes: 3”
    “# initial_hvac_mode: “off””
    “# away_temp: 16”
    “# precision: 0.1”

I’m not sure if I am declaring the thermostat correctly.

The sensor that controls the thermostat. “geyser_2_geyser_2_guest_bedroom_control_thermostat_measured_temperature” and “sensor.geyser_2_geyser_2_guest_bedroom_control_thermostat_set_temperature” - show an unknown status.

  1. Are you using Fahrenheit temperature? I use Centigrade so you might need to check that tha limits in the input variables are not exceeded.

  2. You should be able to see in the trace whether the automation goes through the branch that sets the temperature to 64. If not, then there is something wrong with the conditions, if so then there is something wrong with the control.

  3. My other debugging tip is to put all the input and control variables in a (test) dashboard so you can best se what is going on and trigger some of the values directly.

Good luck!

I use Centigrade.
I do see all the traces.
I have set up a test dashboard.

The following from AllexIT:

Sonoff TH

Support optional Climate entity that controls Thermostat. You can control low and high temperature values and hvac modes:

heat - lower temp enable switch, higher temp disable switch
cool - lower temp disable switch, higher temp enable switch
dry - change control by humidity with previous low/high switch settings
In dry mode, the Thermostat controls and displays Humidity. But the units are displayed as temperature (Hass limitation).

Thermostat can be controlled only with Cloud connection. Main switch and TH sensors support LAN and Cloud connections.

The thermostat can not switch on the sensor to turn on the heat for the geyser.

1 Like

I cannot comment on your particular thermostat, but with mine it is possible to read the temperature using a template sensor.

Hi Andy,
I’m relatively new to HA and wanted to use your blueprint to create some heating controls.
I have a very basic question which isn’t clear from the guidance here or in the code generator “ReadMe”: I’ve generated all the .yaml files as per the code generator but where do I put the resulting files. I’ve tried adding to the configuration.yaml but that generates multiple syntax errors.
Some guidance would be appreciated

Sorry I did not make this clear …

I use a separate .yaml file for each type of helper and the templates; put them in the /config folder then include them in configuration.yaml as follows:

input_datetime: !include input_datetimes.yaml 
input_number: !include input_numbers.yaml
input_text: !include input_texts.yaml
template: !include templates.yaml
timer: !include timers.yaml

Into each of the files input_datetimes.yaml etc., paste the output from the code generator for that type and the syntax should be correct.