Last active
January 20, 2026 21:41
-
-
Save hansharhoff/0c8f5e6b94b7e26d27f8d1675fd38b23 to your computer and use it in GitHub Desktop.
boost heat for X hours on Danfoss ally
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| blueprint: | |
| name: Danfoss Ally 2 - Heat boost (no-default choose) | |
| description: > | |
| Press an input_button to boost selected Danfoss Ally thermostats for X hours. | |
| If pressed again while the timer is active/paused, adds +extend hours. | |
| When timer ends/cancels, restores previous preset + temperature (and hvac_mode). | |
| domain: automation | |
| input: | |
| activation_button: | |
| name: Activation input_button | |
| selector: | |
| entity: | |
| domain: input_button | |
| thermostats: | |
| name: Danfoss Ally thermostats (climate entities) | |
| selector: | |
| entity: | |
| domain: climate | |
| multiple: true | |
| boost_timer: | |
| name: Timer helper | |
| selector: | |
| entity: | |
| domain: timer | |
| snapshot_store: | |
| name: Snapshot store (input_text helper) | |
| description: Stores thermostat states (JSON) so they can be restored after boost. | |
| selector: | |
| entity: | |
| domain: input_text | |
| boost_hours: | |
| name: Boost duration (hours) | |
| default: 2 | |
| selector: | |
| number: | |
| min: 0.25 | |
| max: 24 | |
| step: 0.25 | |
| unit_of_measurement: h | |
| mode: box | |
| extend_hours: | |
| name: Extend by (hours) when pressed again | |
| default: 1 | |
| selector: | |
| number: | |
| min: 0.25 | |
| max: 12 | |
| step: 0.25 | |
| unit_of_measurement: h | |
| mode: box | |
| boost_preset: | |
| name: Boost preset | |
| default: "Manual" | |
| selector: | |
| select: | |
| options: | |
| - "home" | |
| - "away" | |
| - "Pause" | |
| - "Manual" | |
| - "Holiday (Home)" | |
| - "Holiday (Away)" | |
| boost_temperature: | |
| name: Boost target temperature (°C) - optional | |
| description: > | |
| If > 0 and boost_preset is Manual, sets this target temperature during boost. If 0, does not change temperature. | |
| default: 0 | |
| selector: | |
| number: | |
| min: 0 | |
| max: 30 | |
| step: 0.5 | |
| unit_of_measurement: "°C" | |
| mode: box | |
| mode: queued | |
| max: 10 | |
| trigger: | |
| - platform: event | |
| event_type: input_button.press | |
| event_data: | |
| entity_id: !input activation_button | |
| id: activate | |
| - platform: event | |
| event_type: timer.finished | |
| event_data: | |
| entity_id: !input boost_timer | |
| id: restore | |
| - platform: event | |
| event_type: timer.cancelled | |
| event_data: | |
| entity_id: !input boost_timer | |
| id: restore | |
| - platform: state | |
| entity_id: !input boost_timer | |
| from: "active" | |
| to: "idle" | |
| id: restore | |
| - platform: state | |
| entity_id: !input boost_timer | |
| from: "paused" | |
| to: "idle" | |
| id: restore | |
| - platform: homeassistant | |
| event: start | |
| id: restore | |
| variables: | |
| timer_entity: !input boost_timer | |
| thermostats: !input thermostats | |
| store_entity: !input snapshot_store | |
| boost_hours: !input boost_hours | |
| extend_hours: !input extend_hours | |
| boost_preset: !input boost_preset | |
| boost_temperature: !input boost_temperature | |
| action: | |
| - choose: | |
| # ========================================================= | |
| # ACTIVATE: timer already running -> EXTEND | |
| # ========================================================= | |
| - conditions: > | |
| {{ trigger.id == 'activate' | |
| and (is_state(timer_entity, 'active') or is_state(timer_entity, 'paused')) }} | |
| sequence: | |
| - service: timer.start | |
| target: | |
| entity_id: !input boost_timer | |
| data: | |
| duration: > | |
| {% set finish = state_attr(timer_entity, 'finishes_at') %} | |
| {% if finish %} | |
| {% set remaining = as_timestamp(finish) - as_timestamp(now()) %} | |
| {% else %} | |
| {% set rem_str = state_attr(timer_entity, 'remaining') %} | |
| {% set remaining = (as_timedelta(rem_str).total_seconds() if rem_str else 0) %} | |
| {% endif %} | |
| {% if remaining < 0 %}{% set remaining = 0 %}{% endif %} | |
| {% set sec = (remaining + (extend_hours | float * 3600)) | int %} | |
| {{ '%02d:%02d:%02d'|format(sec//3600, (sec%3600)//60, sec%60) }} | |
| - service: climate.set_hvac_mode | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| hvac_mode: "heat" | |
| - service: climate.set_preset_mode | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| preset_mode: !input boost_preset | |
| - choose: | |
| - conditions: "{{ (boost_temperature | float > 0) and (boost_preset == 'Manual') }}" | |
| sequence: | |
| - service: climate.set_temperature | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| temperature: !input boost_temperature | |
| # ========================================================= | |
| # ACTIVATE: timer idle -> SNAPSHOT + START | |
| # ========================================================= | |
| - conditions: > | |
| {{ trigger.id == 'activate' and is_state(timer_entity, 'idle') }} | |
| sequence: | |
| - service: input_text.set_value | |
| target: | |
| entity_id: !input snapshot_store | |
| data: | |
| value: > | |
| {% set ns = namespace(items=[]) %} | |
| {% for eid in thermostats %} | |
| {% set ns.items = ns.items + [{ | |
| "entity_id": eid, | |
| "hvac_mode": states(eid), | |
| "preset_mode": state_attr(eid, "preset_mode"), | |
| "temperature": state_attr(eid, "temperature") | |
| }] %} | |
| {% endfor %} | |
| {{ ns.items | to_json }} | |
| - service: climate.set_hvac_mode | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| hvac_mode: "heat" | |
| - service: climate.set_preset_mode | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| preset_mode: !input boost_preset | |
| - choose: | |
| - conditions: "{{ (boost_temperature | float > 0) and (boost_preset == 'Manual') }}" | |
| sequence: | |
| - service: climate.set_temperature | |
| target: | |
| entity_id: !input thermostats | |
| data: | |
| temperature: !input boost_temperature | |
| - service: timer.start | |
| target: | |
| entity_id: !input boost_timer | |
| data: | |
| duration: > | |
| {% set sec = (boost_hours | float * 3600) | int %} | |
| {{ '%02d:%02d:%02d'|format(sec//3600, (sec%3600)//60, sec%60) }} | |
| # ========================================================= | |
| # RESTORE: timer ended/cancelled -> RESTORE snapshot | |
| # ========================================================= | |
| - conditions: "{{ trigger.id == 'restore' }}" | |
| sequence: | |
| - variables: | |
| saved: > | |
| {% set raw = states(store_entity) %} | |
| {% if raw in ['unknown', 'unavailable', ''] %} | |
| [] | |
| {% else %} | |
| {{ raw | from_json }} | |
| {% endif %} | |
| - condition: template | |
| value_template: "{{ (saved | length) > 0 and is_state(timer_entity, 'idle') }}" | |
| - repeat: | |
| for_each: "{{ saved }}" | |
| sequence: | |
| - variables: | |
| eid: "{{ repeat.item.entity_id }}" | |
| hvac: "{{ repeat.item.hvac_mode }}" | |
| preset: "{{ repeat.item.preset_mode }}" | |
| temp: "{{ repeat.item.temperature }}" | |
| - choose: | |
| - conditions: "{{ hvac is not none and hvac not in ['unknown','unavailable',''] }}" | |
| sequence: | |
| - service: climate.set_hvac_mode | |
| target: | |
| entity_id: "{{ eid }}" | |
| data: | |
| hvac_mode: "{{ hvac }}" | |
| - choose: | |
| - conditions: "{{ preset is not none and preset not in ['unknown','unavailable',''] }}" | |
| sequence: | |
| - service: climate.set_preset_mode | |
| target: | |
| entity_id: "{{ eid }}" | |
| data: | |
| preset_mode: "{{ preset }}" | |
| # Restore temperature only if it was Manual before (avoid overriding schedule-like presets) | |
| - choose: | |
| - conditions: > | |
| {{ (preset == 'Manual') | |
| and temp is not none | |
| and temp not in ['unknown','unavailable',''] }} | |
| sequence: | |
| - service: climate.set_temperature | |
| target: | |
| entity_id: "{{ eid }}" | |
| data: | |
| temperature: "{{ temp }}" | |
| - service: input_text.set_value | |
| target: | |
| entity_id: !input snapshot_store | |
| data: | |
| value: "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment