Smart heating scheduler for Home Assistant (extra: multi-zones version)

Hi Robert,

Thanks for sharing your setup, much appreciated!!

I’ve setup my heating in HA based on your automations. I have a total of 6 zones, each zone controls the corresponding TRVs and a main heating valve that controls the hot water flow for the heating in my apartment.

I’m having an issue with the temperature sliders as those automations don’t run because I get following error messages in the logs:

* Template variable error: 'str object' has no attribute 'timestamp' when rendering '{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %} {% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %} {% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %} {{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}'
* Template variable error: 'str object' has no attribute 'timestamp' when rendering '{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %}'

The problem seems to be with the template condition in the adjusting temperature automations:

{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %}
{% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %}
{% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %}
{{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}

Sorry but I am no expert these complex conditions so I am not sure how to fix it.
Maybe someone can put me in the right direction.

Thanks!!!

Update:

I found a solution that seems to work for now from an other post. Maybe it is useful for somebody else.

Instead of:

{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %}
{% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %}
{% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %}
{{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}

I use now:

{{ now().fromtimestamp(state_attr('input_datetime.heating_weekday_morning_start', 'timestamp')).time() <= now().time() <= now().fromtimestamp(state_attr('input_datetime.heating_weekday_day_start', 'timestamp')).time() and is_state('binary_sensor.workday_sensor', 'on') }}

Changed all my automations accordingly and so far all working without errors.

1 Like

Hi Robi,

First of all I’m impressed by your work and the fact of sharing it! As a starter with HA, but have experience with other domotica systems (domoticz) I would like to use your work and migrate my current heating system to HA. Maybe you’re willing to help migrating.

A little explanation;

  • I’ve got one central heating system with integrated pump, and is turned on or off with an zwave switch.
  • The ground floor is heated with heattubes in the floor and the first floor with convectors.
  • The ground floor can be isolated from the system with the help of a valve (open or close). It works with an zwave switch and needs to be ON for 20 seconds for opening or OFF 20 seconds for closing the valve.
    With help of motion- and temperature sensors on the 1st floor the system decides to heat the whole house or only the first floor.
  • The groundfloor heating pump works with a zwave switch and is activated when central heating is activated and needs to be running as long as the system heats (ciculates the water in the ground floor)
  • I’ve got two motion sensor on the first floor; when the first is triggered the heating is turned on for half hour and when the second one is triggered the heating is turned on for one hour,
  • I’ve got an temperature sensor on the ground and first floor,
  • Off course all the above is depending on the master schedule ;-).

I’m seeking for some clarifications whitin you’re solution and hopefully your willing to help me out?

  • The best choice should probably be the 2-zone system, wright?
  • How can I best setup the climate.yaml file:
    • Heater1: switch.--------
    • target_sensor1: sensor.-------
    • Heater2: switch.--------
    • target_sensor2: sensor.-------
  • I’m confused how to configure zz_switch/switch_heater.yaml and switch_rpi.yaml due the fact I haven’t got an RPI and/or MQTT sensors.
  • Is there a way to put the presence on the 1st floor integrated?
  • How can I add activation time to the valve switch?
  • How can I disable presence of people?
  • What is the difference or why should I configure openweather and outside temp sensors?

Thanks in advance, John

Quite complex indeed, let me think about it, I’ll get back in a few days.

Hi Robi,

Already thanks!

Hi Robert,
I use your brilliant heating scheduler code in my Home Assistant installation. I’ve recently updated to 2021.12.4 and noticed errors in the log when starting up. I confess to not knowing enough about YAML to fix this myself. I was wondering if you may be able to help me please. Here is one of the errors:

> 2021-12-22 11:27:12 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'strptime' got invalid input '2021-12-22T11:27:12.027556+00:00' when rendering template '{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %} {% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %} {% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %} {{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}' but no default was specified. Currently 'strptime' will return '2021-12-22T11:27:12.027556+00:00', however this template will fail to render in Home Assistant core 2022.1
> 2021-12-22 11:27:12 ERROR (MainThread) [homeassistant.helpers.template] Template variable error: 'str object' has no attribute 'timestamp' when rendering '{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %} {% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %} {% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %} {{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}'
> 2021-12-22 11:27:12 WARNING (MainThread) [homeassistant.helpers.script] Error in 'choose[0]' evaluation: In 'template' condition: UndefinedError: 'str object' has no attribute 'timestamp'

I have searched the HA forums and there are examples of how to fix “Template warning: ‘strptime’ got invalid input” but none apply directly and my knowledge is not there (yet).
There is a block for each template that has this error. Once I know how to fix one block I can fix the others but need some help please.
Kind regards
Richard

Hi Robert,
I fixed this issue using time conditions:

It’s similar solution than @armins2k. Instead of:

{% set t = strptime((now().timestamp() | timestamp_local), "%Y-%m-%d %H:%M:%S").timestamp()-strptime(now().strftime("%Y-%m-%d 00:00:00"),"%Y-%m-%d %H:%M:%S").timestamp() %}
{% set time_start = state_attr('input_datetime.heating_weekday_morning_start','timestamp') %}
{% set time_end = state_attr('input_datetime.heating_weekday_day_start','timestamp') %}
{{ time_start <= t < time_end and is_state('binary_sensor.workday_sensor', 'on')}}

I use:

    - conditions:
      - condition: state
        entity_id: binary_sensor.workday_sensor
        state: 'off'      
      - condition: time
        after: input_datetime.heating_weekend_day_start
        before: input_datetime.heating_weekend_evening_start

For the nightime condition, I need to change a little bit:

    - conditions:
      - condition: state
        entity_id: binary_sensor.workday_sensor
        state: 'off'      
      - condition: or
        conditions:          
        - condition: time
          after: input_datetime.heating_weekend_night_start
        - condition: time
          before: input_datetime.heating_weekend_morning_start

My personal opinion is that this way is cleaner for a guy not used to be programmer.

Best regards,
Arkharim

Nice… lemme check…

Don’t know why I went this route (building templates) when I forked this project from the original author. I guess when the very first version was created triggering or conditioning on time based on an input_datetime was not possible, you had to use templates anyway.

But it’s certainly true that nowdays this is the good way to go.

I will rewrite the whole thing. Thanks for pointing it out.

Why do you use off state for the workday sensor while it should be on, based on the template above?

A question: the switch valve ground/first floor is a 2-way valve, right? It means that the heating system can work towards only one floor at a time, right?

Also you want heat to go on the first floor only when PIR detects motion over there… That kind of defeats the thermostat… (the thermostat would be asking for heat upstairs, but since there’s no motion it doesn’t get any - are you sure that’s what you want?)

I’m not sure if that’s an ideal solution… because you don’t have the freedom to choose any heat in any direction at any time… this kind of setup makes assumptions that you can’t nicely conrtol from HA.

Hi Robi,
First of all, thank you very much for sharing your project. Very interesting and I reused quite a lot in comination wiht my HMIP thermostats.

I think that some info went lost from @arkharim as he copied a snipet of weekday as example for the template he replaced and then use all weekend data for the new condition example.
I replaced with is example the different templates and changed entities or variable to adapt to the “old” template and it seems to work.

Another question from my side. In the main timer, you use:

    - condition: template
      value_template: "{{ not is_state_attr('climate.generic_thermostat_guest_wc', 'preset_mode', 'away') }}"

but the template output a string “True”, to get a boolean true, you need to remove the quotes around the template. Is there something that I missed? I copied it for the moment and are not getting errors but didnt had the time to try to trigger this condition to see if it can be entered/evaluated
Let me know if there is more behind it :slight_smile:

Thank you!

Didn’t test! No support! Use at own risk, as usual:

Yes I think that would be appropriate.

switch_heater.yaml and switch_rpi.yaml are just examples on how to integrate a few kinds of relays into the system. If you use ZWave, ignore these. Most probably you already have your switches integrated in HA through some other component.

I assume your PIRs appear in HA as binary_sensors. You don’t use them directly as they are short in time. I’d suggest this:

Add some input_booleans:

heating_motion_upstairs_1:
  name: Heating upstairs on for 0.5 hour
heating_motion_upstairs_2:
  name: Heating upstairs on for 1 hour
heating_thermostat_request:
  name: Heating upstairs thermostat request
heating_motion_upstairs_override:
  name: Heating upstairs override PIR

Add automations to extend the time of the PIRs:

- id: motion_upstairs_1
  alias: 'motion_upstairs_1'
  trigger:
    - platform: state
      entity_id: binary_sensor.pir_1
      from: 'off'
      to: 'on'
  condition:
    - condition: state
      entity_id: 'input_boolean.heating_motion_upstairs_override'
      state: 'off'
  action:
    - service: input_boolean.turn_on
      entity_id: input_boolean.heating_motion_upstairs_1
    - delay: '00:30:00'
    - service: input_boolean.turn_off
      entity_id: input_boolean.heating_motion_upstairs_1

- id: motion_upstairs_2
  alias: 'motion_upstairs_2'
  trigger:
    - platform: state
      entity_id: binary_sensor.pir_2
      from: 'off'
      to: 'on'
  condition:
    - condition: state
      entity_id: 'input_boolean.heating_motion_upstairs_override'
      state: 'off'
  action:
    - service: input_boolean.turn_on
      entity_id: input_boolean.heating_motion_upstairs_2
    - delay: '01:00:00'
    - service: input_boolean.turn_off
      entity_id: input_boolean.heating_motion_upstairs_2

- id: motion_upstairs_override_pirs
  alias: 'motion_upstairs_override_pirs'
  trigger:
    - platform: state
      entity_id: input_boolean.heating_motion_upstairs_override
      from: 'off'
      to: 'on'
  action:
    - service: input_boolean.turn_on
      entity_id: input_boolean.heating_motion_upstairs_1
    - service: input_boolean.turn_on
      entity_id: input_boolean.heating_motion_upstairs_2

From now on you don’t use binary_sensor.pir_X but input_boolean.heating_motion_upstairs_X to have the time. If you turn on the input_boolean.heating_motion_upstairs_override your heating_motion_upstairs_X will always be triggered so it will look like the PIR sensors are not taken into account.

Add an automation to control the upstairs level heating:

- id: heating_upstairs_start
  alias: 'Heating Upstairs Start'
  trigger:
    - platform: state
      entity_id: input_boolean.heating_motion_upstairs_1
      from: 'off'
      to: 'on'
    - platform: state
      entity_id: input_boolean.heating_motion_upstairs_2
      from: 'off'
      to: 'on'
    - platform: state
      entity_id: input_boolean.heating_thermostat_request
      from: 'off'
      to: 'on'
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.heating_motion_upstairs_1
        state: 'on'
      - condition: state
        entity_id: input_boolean.heating_motion_upstairs_2
        state: 'on'
      - condition: state
        entity_id: input_boolean.heating_thermostat_request
        state: 'on'
  action:
    - service: switch.turn_on
      entity_id: switch.heater_2

- id: heating_upstairs_stop
  alias: 'Heating Upstairs Stop'
  trigger:
    - platform: state
      entity_id: input_boolean.heating_motion_upstairs_1
      from: 'on'
      to: 'off'
    - platform: state
      entity_id: input_boolean.heating_motion_upstairs_2
      from: 'on'
      to: 'off'
    - platform: state
      entity_id: input_boolean.heating_thermostat_request
      from: 'on'
      to: 'off'
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: input_boolean.heating_motion_upstairs_1
        state: 'off'
      - condition: state
        entity_id: input_boolean.heating_motion_upstairs_2
        state: 'off'
      - condition: state
        entity_id: input_boolean.heating_thermostat_request
        state: 'off'
  action:
    - service: switch.turn_off
      entity_id: switch.heater_2

For the ground floor I’d set up a climate controlling the floor heating pumps and the central heating. So I’d keep the pump-relation.yaml exactly for this. You either replace the entity names in the automation, or you rename your existing entity_ids to these:

switch.main_pump - this should be the Switch CH on/off on your drawing.
switch.heater_1 - this should be the Switch groundfloor heaters
switch.heater_2 - this should be Switch valve ground/first floor

In the climate I’d replace this one with an input_boolean which could be monitoring the PIRs and the upper thermostat. It would only turn on when PIR1 + PIR2 + climate on the first floor all want heating ON. See below.

Climates

- platform: generic_thermostat
  name: Heating ground floor
  heater: switch.heater_1
  target_sensor: sensor.temp_groundfloor
  min_temp: 16
  max_temp: 26
  cold_tolerance: 0.2
  hot_tolerance: 0.2
  away_temp: 20.5  # has to be set from here
  precision: 0.1
  initial_hvac_mode: "heat"
  min_cycle_duration:
    minutes: 10

- platform: generic_thermostat
  name: Heating upstairs (first floor)
  heater: input_boolean.heating_thermostat_request
  target_sensor: sensor.temp_firstfloor
  min_temp: 16
  max_temp: 26
  cold_tolerance: 0.2
  hot_tolerance: 0.2
  away_temp: 20.5  # has to be set from here
  precision: 0.1
  initial_hvac_mode: "heat"
  min_cycle_duration:
    minutes: 10

It’s practical so your heating system only operates when it’s cold enough outside. This is good in autumn and in spring, where there can be some warmer days when you don’t need heating in the house at all, you can save some energy.

I think the quotes around booleans are treated automatically. I assume there can be yaml parsing errors if quotes are missing from around the template.

hi robi, first of all thanks for replying and take time for me! Let me try to answer the first questions. " the switch valve ground/first floor is a 2-way valve, right?" No, it isn’t the valve is an open and close valve.

The second one concerning the PIR and thermostat, let me try to clarify iit with two use-cases:.

  1. The timer is thru, and the temperature on the first floor is below the preset or thermostat value. When PIR detects movement on the first floor AND the ground floor has reached its temperature; heating should go ON and Valve is closed. In case when the ground floor is below temperature the Valve should be open (ground floor and first floor is heated).
  2. In the situation there is no PIR detection, and timer of themostat on the groundfloor is requesting to heat the groundfloor, the Valve needs to be open and all floors are heated. For overheating I’ve got thermostatic valves on my first floor radiators.

The thought behind this: the ground floor is beter isolated than the first floor. So the systems needs to heat the first floor more frequently.

Sorry it ssems that in the mean time you answered again, maybe the above is still helpful…

My answer above might not be exactly what you described, but you get the idea, good luck!

hi robi, you’re answer is very detailed and I really have to study on it. I’ll let you know what the progress will be. Do I understand that you rewrite your heating program? Best regards, John

I will rewrite some of the timeslot detecting templates as suggested by @arkharim some posts above.
(already done here, testing)

The thermostat could simply take care of that. No need for extra fiddling.

hi robi, can you explain more about above feedback and especially this line: “switch.heater_1` - this should be the *Switch groundfloor heaters”.
Do you mean that I’ve to put “climate.heater_1” in here or something else?

I use ‘off’ to detect when is holiday so I use weekend information. It’s to replace the part after and condition.