blueprint: name: Adjust HVAC temperature based on doors/windows description: Send notifications or take action based on whether a door/window is needlessly open in the area you're trying to heat/cool domain: automation input: hvac: name: HVAC thermostat description: The climate component which controls the HVAC in the room/area selector: entity: domain: climate external_temperature: name: External temperature description: A sensor referencing the temperature outside the room/area, in the area which the door/window opens to. This could be the next room temperature for doors or the outside temperature for windows. selector: entity: domain: - sensor - input_number window_sensor: name: Window/door sensor description: The window or door sensor to be monitored selector: entity: domain: - binary_sensor - input_boolean on_debounce_time: name: Acceptable open duration description: The amount of time a door/window is allowed to be open before any action is taken default: hours: 0 minutes: 5 seconds: 0 selector: duration: off_debounce_time: name: Closed lag duration description: The amount of time for which a door/window needes to be closed before any action is taken default: hours: 0 minutes: 0 seconds: 30 selector: duration: allow_user_override: name: Allow user override description: If this is set and a user or another automation alters the set temperature of the already-turned-down HVAC, then closing the window no longer resets its state to that from before the turning down took place default: false selector: boolean: override_boolean: name: Override indicator (optional) description: An input_boolean where to store whether the HVAC temperature was overridden while the door/window was open. Useful only if user overrides are allowed selector: entity: domain: input_boolean default: "a.b" # something that doesn't exist user_override_cooldown: name: Respect user overrides for description: If you have overridden the automation (i.e. turned the HVAC back on or set the temperature manually after it was turned down), don't let environmental changes restart it for an amount of time. This is so that when you "turn the HVAC back up", you don't get it turned back down in a minute because the external temperature changed. Ignored if user overrides are not allowed default: hours: 6 minutes: 0 seconds: 0 selector: duration: script_turn_down: name: Script for turning down HVAC description: Script to run when the NVAC needs to be turned down as a result of an open window/door selector: entity: domain: - script default: "a.b" # something that doesn't exist script_turn_back_up: name: Script for turning HVAC back up description: Script to run in order to turn the HVAC back to its initial state as a result of the window/door closing selector: entity: domain: - script default: "a.b" # something that doesn't exist script_turn_back_up_override: name: Script for turning HVAC back up (override) description: Script to run to turn the HVAC back up as a result of a user clicking an action in the notification (user override) selector: entity: domain: - script default: "a.b" # something that doesn't exist script_notify: name: Script for sending actionable notifications description: Script to run in order to send notifications that the HVAC was turned down as a result of an open window/door selector: entity: domain: - script default: "a.b" # something that doesn't exist script_clear_notification: name: Script for clearing notifications description: Script for clearing notifications on mobiles in case the user hasn't interacted with them selector: entity: domain: - script default: "a.b" # something that doesn't exist mode: parallel # needed for multiple rooms variables: hvac: !input hvac snapshot_scene: > # climate.some_thermostat -> scene.some_thermostat_snapshot scene.{{hvac[8:]}}_snapshot external_temperature: !input external_temperature window_sensor: !input window_sensor allow_user_override: !input allow_user_override override_boolean: !input override_boolean user_override_cooldown: !input user_override_cooldown # whether the set temperature was lowered by the automation in order to save energy energy_saving_active: > {# last_changed gets updated upon scene activation, as the scene state gets updated. hence, to test if a scene was activated, the state and last_changed attribute need to be almost equal ("almost" due to race conditions) #} {# the manual override is present if the scene was activated. the override is present if the scene has been activated #} {# the energy saving is active if the snapshot scene exists, but has not been activated after creation. activation (as the last action) signifies override #} {{ (states[snapshot_scene] is not none) and (is_state(snapshot_scene, 'unknown') or (states[snapshot_scene].last_changed.timestamp()|float(as_timestamp(now())) - as_timestamp(states(snapshot_scene))|float(as_timestamp(now())) > 0.1)) and not (is_state(override_boolean, 'on')) }} user_override_active: > {{ allow_user_override and is_state(override_boolean, 'on') }} user_override_cooldown_seconds: > {{ (user_override_cooldown.seconds + 60*user_override_cooldown.minutes + 3600*user_override_cooldown.hours)|float(0) }} user_override_cooldown_active: > {{ user_override_active and (as_timestamp(now()) - states[override_boolean].last_updated.timestamp())|float(0) < user_override_cooldown_seconds }} script_turn_down: !input script_turn_down script_turn_back_up: !input script_turn_back_up script_turn_back_up_override: !input script_turn_back_up_override script_notify: !input script_notify script_clear_notification: !input script_clear_notification trigger: - platform: state entity_id: !input window_sensor to: "on" for: !input on_debounce_time id: "window_open" - platform: state entity_id: !input window_sensor to: "off" for: !input off_debounce_time id: "window_closed" - platform: state entity_id: !input hvac attribute: temperature id: "set_temperature_changed" - platform: state entity_id: !input external_temperature id: "ext_temperature_change" - platform: homeassistant event: shutdown id: ha_restart condition: - condition: state entity_id: !input hvac state: - heat - cool action: - alias: "Reset override cooldown" if: - condition: template value_template: "{{user_override_active and not user_override_cooldown_active}}" then: - service: input_boolean.turn_off target: entity_id: !input override_boolean - choose: - conditions: - condition: trigger id: ha_restart sequence: - service: "{{script_turn_back_up}}" data: hvac: !input hvac snapshot_scene: "{{snapshot_scene}}" override_boolean: "{{override_boolean}}" - conditions: - condition: trigger id: set_temperature_changed - condition: template value_template: "{{states.script|selectattr('context.id', 'eq', context.parent_id)|selectattr('entity_id', 'in', script_turn_down)|list|length == 0}}" # temperature change was not done by this automation - condition: template value_template: "{{energy_saving_active}}" - condition: template value_template: "{{ (not allow_user_override) or states[override_boolean].state is defined }}" # if it exists, not if it's on sequence: - alias: "Reset override" service: scene.create data: scene_id: "{{snapshot_scene[6:]}}" # exclude 'scene.' snapshot_entities: "{{hvac}}" - service: input_boolean.turn_on target: entity_id: !input override_boolean - conditions: - alias: "If window is open" condition: state entity_id: !input window_sensor state: "on" - alias: "Energy saving is not active (temp was not lowered)" condition: template value_template: "{{not energy_saving_active}}" # includes user_override_active condition - alias: "No override was set or override cooldown has passed" condition: template value_template: "{{not user_override_cooldown_active}}" # includes user_override_active condition - condition: template value_template: > {% set direction = {"heat": 1, "cool": -1}[states(hvac)] or 0 %} {{ direction*(states(external_temperature)|float(0) - state_attr(hvac, 'temperature')|float(0)) < 0 }} - condition: template value_template: "{{trigger.id != 'set_temperature_changed'}}" sequence: - service: "{{script_turn_down}}" data: hvac: !input hvac snapshot_scene: "{{snapshot_scene}}" override_boolean: "{{override_boolean}}" - service: "{{script_notify}}" data: window_name: "{{ state_attr(window_sensor, 'friendly_name')|lower }}" action_script: "{{script_turn_back_up_override}}" hvac: !input hvac snapshot_scene: "{{snapshot_scene}}" allow_user_override: "{{allow_user_override}}" override_boolean: "{{override_boolean}}" - conditions: - alias: "If window is closed" condition: state entity_id: !input window_sensor state: "off" - condition: template value_template: "{{trigger.id != 'set_temperature_changed'}}" sequence: - if: - alias: "Energy saving enabled" condition: template value_template: "{{energy_saving_active}}" then: - service: "{{script_turn_back_up}}" data: hvac: !input hvac snapshot_scene: "{{snapshot_scene}}" override_boolean: "{{override_boolean}}" - if: - condition: template value_template: "{{ states[script_clear_notification] is defined }}" then: - service: "{{ script_clear_notification }}" data: notification_tag: "open_window_{{hvac|replace('.', '_')}}"