Please help with climate control automation (window status plus timed temperature)

Hi everybody,

I am using the automation below to control my SPZB0001 zigbee thermostats via Home Assistant depending on window statuses; the code below includes three different rooms (office, kitchen, living room).

As you can see, office (“Arbeitszimmer”) and kitchen (“Küche”) are identical except for the sensors they depend on (obviously, office depends on the office window while kitchen depends on the kitchen sensor, but the rest is the same code). Then, however, there is the living room, which has multiple windows as well as multiple thermostats to set. My code there works as well, but here are my questions:

  1. is there some code / template I can use for all rooms (or, if not, at least all rooms except the living room? All other rooms only have one single window and one single climate entity to control)
  2. how can I improve this code to constantly monitor the windows? Explanation below

I also have timers for my climate entities, for example, turn office to 18.5°C at 09:00. This works fine as long as the office window is shut. However, when the office window is open at 09:00, the climate entity will still be set to 18.5°C, because the trigger.to_state entity in this case is not binary_sensor.arbeitszimmer_fenster_contact (which would be the window sensor), but rather climate.arbeitszimmer_heizung_climate, as my automation triggers that entity directly (example below as well).

So currently, I can successfully open or close windows, and my climate entities will act accordingly; however, when a climate entity is controlled by another automation (or manually), it will not “double-check” whether or not the corresponding window is still shut and might turn on the heat while the window is not shut.

So, my initial idea was to create an automation with time_pattern to check, for example, every 30 seconds whether or not any windows are open, and, if so, turn off the corresponding climate entity. HOWEVER, this would require me to write (almost) identical code for each room. So I was hoping there might be a better way to do this without so much copy and paste.

All my window sensors are titled binary_sensor.<room>_fenster_contact and all my climate entities are titled climate.<room>_heizung_climate. There are three exceptions: living room (three window sensors, two climate entities) as well as both bathrooms, which are heated by infrared radiators and are therefore controlled by different automations (if temperature in bathroom1 <= valuex, turn on switch.heat_bathroom1, else turn off switch.heat_bathroom1), so those two bathrooms are not effected by this. The living room, however, ought to be included, if possible; I have binary_sensor.wohnzimmer_vorne_fenster_contact, binary_sensor.wohnzimmer_mitte_fenster_contact, and binary_sensor.wohnzimmer_strasse_fenster_contact here (vorne, mitte, and strasse being the locations of those windows, which makes the pattern a bit different than the pattern I use for all other rooms).

How can I improve this?

Thank you for your ideas :slight_smile:


Automation to control climate entities based on window sensor

Will be triggered when window sensor state changes and regulate climate entity accordingly; works fine, but has to be hand-written for each room.

automation:
# Arbeitszimmer #####################################################
  - id: "heizung_az"
    alias: "[Heizung] AZ"
    trigger:
      - platform: state
        entity_id: binary_sensor.arbeitszimmer_fenster_contact
    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: climate.arbeitszimmer_heizung_climate
          hvac_mode: >
            {% if trigger.to_state.state == "on" %}
              off
            {% elif trigger.to_state.state == "off" %}
              heat
            {% endif %}

# Küche #####################################################
  - id: "heizung_ku"
    alias: "[Heizung] KU"
    trigger:
      - platform: state
        entity_id: binary_sensor.kueche_fenster_contact
    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: climate.kueche_heizung_climate
          hvac_mode: >
            {% if trigger.to_state.state == "on" %}
              off
            {% elif trigger.to_state.state == "off" %}
              heat
            {% endif %}

# Wohnzimmer #####################################################
  - id: "heizung_wz"
    alias: "[Heizung] WZ"
    trigger:
      - platform: state
        entity_id: binary_sensor.wohnzimmer_vorne_fenster_contact, binary_sensor.wohnzimmer_mitte_fenster_contact, binary_sensor.wohnzimmer_strasse_fenster_contact
    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: climate.wohnzimmer_strasse_heizung_climate
          hvac_mode: >
            {% if trigger.to_state.state == "on" %}
              off
            {% elif trigger.to_state.state == "off" %}
              heat
            {% endif %}
      - service: climate.set_hvac_mode
        data_template:
          entity_id: climate.wohnzimmer_vorne_heizung_climate
          hvac_mode: >
            {% if trigger.to_state.state == "on" %}
              off
            {% elif trigger.to_state.state == "off" %}
              heat
            {% endif %}


Automations to control climate entities based on time (currently still turning on climate entities even when windows are open, which needs to be fixed)

This gives me input sliders so that I can individually set the temperature for each room in the morning and at night (some rooms have multiple of those, for example, kitchen will be heated at 08:00, then turned down at 10:00, heated again at 17:30, turned down again at 18:30, and turned down more at 22:15, etc. etc.; but all rooms have at least one individually setable value for morning and evening)

# Zeitsteuerung für Heizungen
input_number:
  az_morgens:
    name: "AZ 08:30"
    initial: 18.5
    <<: &input_template
      min: 5
      max: 30
      step: 0.5
      icon: mdi:radiator
      unit_of_measurement: "°C"
  az_abends:
    name: "AZ 20:15"
    initial: 16.5
    <<: *input_template

automation:
  # Arbeitszimmer {{{
  - id: "heizung_az_morgens"
    alias: "[Heizung] AZ 08:30"
    trigger:
      - platform: time
        at: "08:30:00"
    action:
      - service: climate.set_temperature
        data_template:
          entity_id: climate.arbeitszimmer_heizung_climate
          temperature: "{{ states('input_number.az_morgens') | float }}"
  - id: "heizung_az_abends"
    alias: "[Heizung] AZ 20:15"
    trigger:
      - platform: time
        at: "20:15:00"
    action:
      - service: climate.set_temperature
        data_template:
          entity_id: climate.arbeitszimmer_heizung_climate
          temperature: "{{ states('input_number.az_abends') | float }}"

Combining the three window-sensor automations into one is challenging because wohnzimmer is handled differently from kueche and arbeitzimmer.

For kueche and arbeitzimmer, it’s simply a matter of extracting their name from their entity_id (split the string at each _ character and then take the zeroth item) and using it to specify the correct climate entity_id.

  - alias: 'Example 1'
    trigger:
      - platform: state
        entity_id:
          - binary_sensor.arbeitszimmer_fenster_contact
          - binary_sensor.kueche_fenster_contact
    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: >
            {% set zimmer = trigger.to_state.object_id.split('_')[0] %}
            climate.{{zimmer}}_heizung_climate
          hvac_mode: "{{ 'heat' if trigger.to_state.state == 'off' else 'off' }}"

However, this simple approach fails for wohnzimmer because its three window sensors have compound names (wohnzimmer_vorne_fenster_contact), which defeat our simple parsing algorithm, and a change to any one of the three window sensors causes a change to two climate components.


EDIT
I know of a way to fix this but I’m not convinced it’s worth the effort.

If wohnzimmer_vorne_fenster_contact is renamed to wohnzimmervorne_fenster_contact, then it can be handled by the simple parsing algorithm (and you also have rename its associated climate entity to climate.wohnzimmervorne_heizung_climate).

You also have to add a condition within the automation’s action. If the window sensor is in wohnzimmervorne then it allows a second service to be executed. This second service is for the second climate entity in wohnzimmervorne.

1 Like

Thank you so much!

I had used similar techniques when I was using node-red, but fever quite understood how this worked in Home Assistant. Now I do.

This makes it so much easier to write automations in general. I will have to recreate a bunch of those notes :slight_smile:

Btw. I will keep the wohnzimmer ones as they are and write separate automations for that room.

How should I handle the process of not changing temperature when a window is already open? My initial thought is to remove the window open condition from the initial automation and instead create a time_pattern running svg 30s or so (using your template example) to check whether any windows are open, and, if so, turn off the corresponding climate entity.

This would mean if I control any climate entity while a window is open, it will still be set to whatever I set it to, but within 29 seconds tops it will be switched off as this automation runs every 30 seconds.

I don’t understand what you mean by ‘already open’. The automation triggers whenever the window’s sensor changes state. So the automation knows when the window is opened (and handles that situation).

Please look at the “Automations to control climate entities based on time” code I pasted in my initial post.

This will turn my office heat to 18.5 at 08:30:00 each morning. I have similar automations for during the day and nighttime as well. However, if my window is open before 08:30, it will still change the temperature (and turn on heat mode) even though the window is open.

I have tried the automation below for this, but got the following error

2019-11-26 14:39:24 ERROR (MainThread) [homeassistant.components.automation] Error while executing automation automation.heizungen_fenstercheck. Invalid data for call_service at pos 1: not a valid value for dictionary value @ data['entity_id']

  - alias: "[Heizungen Fenstercheck]"
    trigger:
      - platform: state
        entity_id: 
          - climate.arbeitszimmer_heizung_climate
          - climate.jonna_heizung_climate
          - climate.kueche_heizung_climate
          - climate.schlafzimmer_heizung_climate
          - climate.zwischenzimmer_heizung_climate
    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: >
            {% set raum = trigger.to_state.object_id.split('_')[0] %}
            climate.{{zimmer}}_heizung_climate
          hvac_mode: >
            {% if states("binary_sensor({{raum}}_fenster_contact") == "on" %}
              off
            {% elif states("binary_sensor({{raum}}_fenster_contact") == "off" %}
              heat
            {% endif %}

I assumed that this would be trigger any time one of those climate entities would change their temperature, then check whether or not the corresponding window was open or shut, and then turn off hvac mode if this was the case. However, my office window is currently open and if I change the temperature in Home Assistant, it will actually change it on the radiator module (even though it should not).

This makes things very difficult because if any window is open while one of those temperature automations is triggered, it will heat for nothing…

1 Like

OK, now I understand. If the window is open and you increase the thermostat’s temperature, the climate entity will request heating. The solution is to not only monitor the state of the windows but also the state of the thermostat. Any requests for heating must first be checked if there are any open windows.

The automation you presented is attempting to do that but it has three little mistakes:

  1. You’ve defined a variable called raum in entity_id but don’t use it (you refer to a non-existent variable called zimmer).
  2. In hvac_mode you refer to raum but it was defined in the previous option (entity_id) so it is undefined with the hvac_mode option.
  3. In hvac_mode you are using the {{raum}} expression within the {% ... %} statement and that’s not permitted.

Try this version:

    action:
      - service: climate.set_hvac_mode
        data_template:
          entity_id: >
            {% set raum = trigger.to_state.object_id.split('_')[0] %}
            climate.{{raum}}_heizung_climate
          hvac_mode: >
            {% set fenster = 'binary_sensor.' ~ trigger.to_state.object_id.split('_')[0] ~ '_fenster_contact' %}
            {{ 'off' if is_state(fenster, 'on') else 'heat' }}

EDIT
Fixed incorrect use of terminology; ‘expression’ and ‘statement’.

3 Likes

Brilliant, thank you so much. I cannot test this on site at the moment, but from what developer tools tells me, this seems to work :pray: