Skip to content

Instantly share code, notes, and snippets.

@danieleparazza
Forked from raffy-ops/hvac_pause.yaml
Last active July 7, 2025 14:41
Show Gist options
  • Save danieleparazza/0342467484089d815152d274408bcab1 to your computer and use it in GitHub Desktop.
Save danieleparazza/0342467484089d815152d274408bcab1 to your computer and use it in GitHub Desktop.
HA HVAC Pause Blueprint V2
#ver. 2.0.0
blueprint:
name: HVAC Pause V2.0.0
description: Pauses HVAC when windows/doors open; resumes full previous state once closed (with wait-for-confirmed state)
domain: automation
input:
climate_device:
name: Climate Device
description: Climate entity used for climate control.
selector:
entity:
domain: [climate]
doors_windows:
name: Door and Window Sensors
description: Group of entities that trigger pause when opened. ('on' = open)
selector:
entity:
domain: [group, binary_sensor]
pause_delay:
name: Pause Delay
description: Time to wait before pausing the HVAC system.
default:
hours: 0
minutes: 5
seconds: 0
selector:
duration: {}
resume_delay:
name: Resume Delay
description: Time to wait before resuming the HVAC system.
default:
hours: 0
minutes: 0
seconds: 30
selector:
duration: {}
timeout:
name: Timeout (optional)
description: If set, HVAC will remain off if window/door stays open. Set to 0 to disable.
default:
hours: 0
minutes: 0
seconds: 0
selector:
duration: {}
action_first:
name: Action First
description: Run pause actions before pausing HVAC?
selector:
boolean: {}
pause_action:
name: Pause Action (Optional)
default: []
selector:
action: {}
resume_action:
name: Resume Action (Optional)
default: []
selector:
action: {}
timeout_action:
name: Timeout Action (Optional)
default: []
selector:
action: {}
mode: single
variables:
action_first: !input action_first
pause_action: !input pause_action
resume_action: !input resume_action
timeout_action: !input timeout_action
climate_device: !input climate_device
doors_windows: !input doors_windows
timeout: !input timeout
timeout_empty: {"hours": 0, "minutes": 0, "seconds": 0}
trigger:
- platform: state
entity_id: !input doors_windows
from: "off"
to: "on"
for: !input pause_delay
id: group_open
condition:
- condition: not
conditions:
- condition: state
entity_id: !input climate_device
state: "off"
action:
- variables:
hvac_mode_prev: "{{ states(climate_device) }}"
temperature_prev: "{{ state_attr(climate_device, 'temperature') }}"
preset_mode_prev: "{{ state_attr(climate_device, 'preset_mode') }}"
fan_mode_prev: "{{ state_attr(climate_device, 'fan_mode') }}"
swing_mode_prev: "{{ state_attr(climate_device, 'swing_mode') }}"
temp_low_prev: "{{ state_attr(climate_device, 'target_temp_low') }}"
temp_high_prev: "{{ state_attr(climate_device, 'target_temp_high') }}"
- choose:
- conditions: "{{ pause_action | length > 0 and action_first == true }}"
sequence: !input pause_action
- service: climate.set_hvac_mode
data:
hvac_mode: "off"
target:
entity_id: !input climate_device
- choose:
- conditions: "{{ pause_action | length > 0 and action_first != true }}"
sequence: !input pause_action
- if:
- condition: template
value_template: "{{ timeout != timeout_empty }}"
then:
- wait_for_trigger:
- platform: state
entity_id: !input doors_windows
from: "on"
to: "off"
for: !input resume_delay
continue_on_timeout: true
timeout: !input timeout
- if:
- condition: template
value_template: "{{ wait.trigger == none }}"
then:
- if:
- condition: template
value_template: "{{ timeout_action | length > 0 }}"
then:
- sequence: !input timeout_action
- stop: ""
else:
- wait_for_trigger:
- platform: state
entity_id: !input doors_windows
from: "on"
to: "off"
for: !input resume_delay
continue_on_timeout: false
- condition: state
entity_id: !input climate_device
state: "off"
- service: climate.set_hvac_mode
data:
hvac_mode: "{{ hvac_mode_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: state
entity_id: !input climate_device
from: "off"
timeout: "00:00:30"
continue_on_timeout: true
- choose:
- conditions: "{{ temperature_prev is number and temperature_prev != state_attr(climate_device, 'temperature') }}"
sequence:
- service: climate.set_temperature
data:
temperature: "{{ temperature_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: template
value_template: "{{ state_attr(climate_device, 'temperature') == temperature_prev }}"
timeout: "00:00:15"
continue_on_timeout: true
- choose:
- conditions: "{{ temp_low_prev is number and temp_high_prev is number and (temp_low_prev != state_attr(climate_device, 'target_temp_low') or temp_high_prev != state_attr(climate_device, 'target_temp_high')) }}"
sequence:
- service: climate.set_temperature
data:
target_temp_low: "{{ temp_low_prev }}"
target_temp_high: "{{ temp_high_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: template
value_template: >
{{ state_attr(climate_device, 'target_temp_low') == temp_low_prev and
state_attr(climate_device, 'target_temp_high') == temp_high_prev }}
timeout: "00:00:15"
continue_on_timeout: true
- choose:
- conditions: "{{ preset_mode_prev is string and preset_mode_prev != state_attr(climate_device, 'preset_mode') }}"
sequence:
- service: climate.set_preset_mode
data:
preset_mode: "{{ preset_mode_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: template
value_template: "{{ state_attr(climate_device, 'preset_mode') == preset_mode_prev }}"
timeout: "00:00:15"
continue_on_timeout: true
- choose:
- conditions: "{{ fan_mode_prev is string and fan_mode_prev != state_attr(climate_device, 'fan_mode') }}"
sequence:
- service: climate.set_fan_mode
data:
fan_mode: "{{ fan_mode_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: template
value_template: "{{ state_attr(climate_device, 'fan_mode') == fan_mode_prev }}"
timeout: "00:00:15"
continue_on_timeout: true
- choose:
- conditions: "{{ swing_mode_prev is string and swing_mode_prev != state_attr(climate_device, 'swing_mode') }}"
sequence:
- service: climate.set_swing_mode
data:
swing_mode: "{{ swing_mode_prev }}"
target:
entity_id: !input climate_device
- wait_for_trigger:
- platform: template
value_template: "{{ state_attr(climate_device, 'swing_mode') == swing_mode_prev }}"
timeout: "00:00:15"
continue_on_timeout: true
- choose:
- conditions: "{{ resume_action | length > 0 }}"
sequence: !input resume_action
@danieleparazza
Copy link
Author

🔄 HVAC Pause Blueprint – Changelog v1.7.1 → v2.0.0

This changelog explains the key improvements and structural changes introduced in version 2.0.0 of the HVAC Pause blueprint compared to version 1.7.1.


✅ Major Improvements in v2.0.0

1. Manual State Snapshot (No More scene.create)

  • v1.7.1 used scene.create to store HVAC state.
  • v2.0.0 saves state manually via Jinja variables:
    • hvac_mode (via states())
    • temperature
    • target_temp_low, target_temp_high
    • preset_mode, fan_mode, swing_mode
  • 🔄 Enables full state restoration on devices that don’t support scene.create well (e.g. Samsung SmartThings).

2. Conditional Restore Logic

  • Only updates a parameter if it differs from the current state.
  • Prevents redundant commands and avoids unnecessary device toggling.

3. Reliable State Change Confirmation

  • Each climate.set_* action is followed by a wait_for_trigger to ensure the change is reflected in the entity’s state.
  • Example: after setting preset_mode, waits for the preset_mode attribute to update.

4. Null-safe Compatibility

  • All parameters are checked with is number or is string before setting.
  • Prevents errors like:
    expected float for dictionary value @ data['target_temp_low']
    

5. Wait-for-Resumed HVAC State

  • After resuming HVAC via climate.set_hvac_mode, the automation waits until the climate entity’s state is no longer off before proceeding.

🛠 Best Use Cases

  • SmartThings devices (Samsung WindFree ACs)
  • MQTT or custom climate platforms
  • Any system where full state restoration is required and scene.create is insufficient

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment