Update ha-smart-thermostat-control.yaml

manual adjustments are out
This commit is contained in:
Andreas Gammelgaard Damsbo 2025-12-05 12:19:01 +01:00
parent 284e428e0c
commit fc3a572171

View file

@ -2,11 +2,9 @@ blueprint:
name: "Smart Thermostat Controller" name: "Smart Thermostat Controller"
author: Andreas Gammelgaard Damsbo author: Andreas Gammelgaard Damsbo
description: | description: |
Advanced thermostat automation with window/door sensors, presence detection, time schedules, and holiday calendar integration. Smart thermostat automation based on window/door sensors, outside temperature (winter mode), time schedules, and holiday calendar integration.
SETUP REQUIRED: * **Day Temperature is now controlled by a separate Input Number Helper.**
1. Create an input_boolean helper (Settings > Devices & Services > Helpers > Toggle) for manual override tracking
2. Create a timer helper (Settings > Devices & Services > Helpers > Timer) for override duration
domain: automation domain: automation
source_url: https://gdamsbo.dk/forgejo/andreas/ha-smart-thermostat-control/raw/branch/main/ha-smart-thermostat-control.yaml source_url: https://gdamsbo.dk/forgejo/andreas/ha-smart-thermostat-control/raw/branch/main/ha-smart-thermostat-control.yaml
input: input:
@ -18,33 +16,6 @@ blueprint:
domain: domain:
- climate - climate
multiple: true multiple: true
manual_override_helper:
name: Manual Override Helper
description: Select an input_boolean helper to track manual temperature overrides. Create one in Settings > Devices & Services > Helpers.
selector:
entity:
domain:
- input_boolean
multiple: false
override_timer:
name: Override Timer
description: Select a timer helper to manage override duration. Create one in Settings > Devices & Services > Helpers > Timer.
selector:
entity:
domain:
- timer
multiple: false
override_duration:
name: Manual Override Duration
description: 'How long manual temperature changes should override the schedule. This sets the timer duration. (Default = 120 minutes)'
default: 120
selector:
number:
mode: box
min: 30.0
max: 480.0
unit_of_measurement: minutes
step: 30.0
window_sensor: window_sensor:
name: Window / Door Sensor Group name: Window / Door Sensor Group
description: Select your grouped or single window / door sensor. description: Select your grouped or single window / door sensor.
@ -102,16 +73,14 @@ blueprint:
domain: domain:
- schedule - schedule
multiple: false multiple: false
day_temp: day_temp_helper:
name: Day Time Temperature Target name: Day Time Temperature Input Number
default: 21 description: Select an input_number helper that stores the desired Day Time temperature. (Create one in Settings > Devices & Services > Helpers).
selector: selector:
number: entity:
step: 0.5 domain:
min: 10.0 - input_number
max: 30.0 multiple: false
unit_of_measurement: °C or °F
mode: slider
night_temp: night_temp:
name: Night Time Temperature Target name: Night Time Temperature Target
default: 18 default: 18
@ -175,17 +144,8 @@ trigger:
entity_id: !input schedule_helper entity_id: !input schedule_helper
id: schedule_change id: schedule_change
- platform: state - platform: state
entity_id: !input thermostat entity_id: !input day_temp_helper # Trigger when the input number changes
attribute: temperature id: day_temp_change
id: manual_adjustment
- platform: state
entity_id: !input manual_override_helper
to: 'off'
id: override_expired
- platform: state
entity_id: !input override_timer
to: 'idle'
id: timer_finished
- platform: calendar - platform: calendar
event: end event: end
entity_id: !input away_calendar entity_id: !input away_calendar
@ -205,86 +165,23 @@ trigger:
variables: variables:
thermostat_entities: !input thermostat thermostat_entities: !input thermostat
triggered_thermostat: "{{ trigger.entity_id | default('none') }}" triggered_thermostat: "{{ trigger.entity_id | default('none') }}"
# Access temperature safely:
# 1. Get the 'to_state', defaulting to a simple object/dict if missing. # Entity ID variables for safe templating in action conditions
# 2. Get the 'attributes' from that object, defaulting to a dict if missing.
# 3. Get the 'temperature' from the attributes, defaulting to 0, then convert to float.
new_temperature: >
{{ trigger.to_state.attributes.temperature | default(0) | float(0)
if trigger.to_state is defined and trigger.to_state.attributes is defined
else 0 }}
old_temperature: >
{{ trigger.from_state.attributes.temperature | default(0) | float(0)
if trigger.from_state is defined and trigger.from_state.attributes is defined
else 0 }}
override_helper: !input manual_override_helper
override_duration_id: !input override_duration
day_temp_input: !input day_temp
night_temp_input: !input night_temp
away_temp_input: !input away_temp
schedule_helper_id: !input schedule_helper schedule_helper_id: !input schedule_helper
away_calendar_id: !input away_calendar away_calendar_id: !input away_calendar
# --- REVISED VARIABLE: Get state value from helper ---
day_temp_input: "{{ states(!input day_temp_helper) | float(21) }}"
# --- END REVISED VARIABLE ---
night_temp_input: !input night_temp
away_temp_input: !input away_temp
condition: [] condition: []
action: action:
- choose: - choose:
# Priority 0: Manual Adjustment - Detect and enable override # Priority 1: Windows/Doors Open - Turn OFF heating
- conditions:
- condition: trigger
id: manual_adjustment
# Only if temperature actually changed
- condition: template
value_template: "{{ (new_temperature - old_temperature) | abs > 0.1 }}"
# Only if the new temp is different from current schedule targets
- condition: template
value_template: >
{% if is_state(away_calendar_id, 'on') %}
{{ (new_temperature - away_temp_input) | abs > 0.1 }}
{% elif is_state(schedule_helper_id, 'on') %}
{{ (new_temperature - day_temp_input) | abs > 0.1 }}
{% else %}
{{ (new_temperature - night_temp_input) | abs > 0.1 }}
{% endif %}
sequence:
- service: logbook.log
data:
name: Smart Thermostat
message: 'Manual Override: {{ triggered_thermostat }} changed to {{ new_temperature }}°, syncing others'
# Sync temperature to all other thermostats
- repeat:
for_each: "{{ thermostat_entities | reject('eq', triggered_thermostat) | list }}"
sequence:
- service: climate.set_temperature
target:
entity_id: "{{ repeat.item }}"
data:
temperature: "{{ new_temperature }}"
# Enable manual override mode
- service: input_boolean.turn_on
target:
entity_id: !input manual_override_helper
# Start timer
- service: timer.start
target:
entity_id: !input override_timer
data:
duration: "{{ (override_duration_id * 60) | int }}"
# Priority 0b: Timer Finished - Disable override and resume schedule
- conditions:
- condition: trigger
id: timer_finished
sequence:
- service: input_boolean.turn_off
target:
entity_id: !input manual_override_helper
- service: logbook.log
data:
name: Smart Thermostat
message: 'Manual Override: Timer expired, resuming schedule'
# Priority 1: Windows/Doors Open - Turn OFF heating (overrides manual)
- conditions: - conditions:
- condition: state - condition: state
entity_id: !input window_sensor entity_id: !input window_sensor
@ -294,18 +191,12 @@ action:
- service: climate.turn_off - service: climate.turn_off
target: target:
entity_id: !input thermostat entity_id: !input thermostat
- service: input_boolean.turn_off
target:
entity_id: !input manual_override_helper
- service: timer.cancel
target:
entity_id: !input override_timer
- service: logbook.log - service: logbook.log
data: data:
name: Smart Thermostat name: Smart Thermostat
message: 'Heating OFF: Window or door is open' message: 'Heating OFF: Window or door is open'
# Priority 2: Too Warm Outside - Turn OFF heating (overrides manual) # Priority 2: Too Warm Outside - Turn OFF heating
- conditions: - conditions:
- condition: numeric_state - condition: numeric_state
entity_id: !input outdoor entity_id: !input outdoor
@ -314,29 +205,12 @@ action:
- service: climate.turn_off - service: climate.turn_off
target: target:
entity_id: !input thermostat entity_id: !input thermostat
- service: input_boolean.turn_off
target:
entity_id: !input manual_override_helper
- service: timer.cancel
target:
entity_id: !input override_timer
- service: logbook.log - service: logbook.log
data: data:
name: Smart Thermostat name: Smart Thermostat
message: 'Heating OFF: Outside temperature above threshold' message: 'Heating OFF: Outside temperature above threshold'
# Priority 3: Manual Override Active - Do nothing, keep manual temperature # Priority 3: Away/Holiday Mode - Set to away temperature
- conditions:
- condition: state
entity_id: !input manual_override_helper
state: 'on'
sequence:
- service: logbook.log
data:
name: Smart Thermostat
message: 'Manual override active - maintaining user-set temperature'
# Priority 4: Away/Holiday Mode - Set to away temperature
- conditions: - conditions:
- condition: state - condition: state
entity_id: !input away_calendar entity_id: !input away_calendar
@ -360,7 +234,7 @@ action:
name: Smart Thermostat name: Smart Thermostat
message: 'Away Mode: Heating set to away temperature' message: 'Away Mode: Heating set to away temperature'
# Priority 5: Schedule Day Time - Set to day temperature # Priority 4: Schedule Day Time - Set to day temperature
- conditions: - conditions:
- condition: state - condition: state
entity_id: !input schedule_helper entity_id: !input schedule_helper
@ -380,14 +254,16 @@ action:
target: target:
entity_id: !input thermostat entity_id: !input thermostat
data: data:
temperature: !input day_temp # --- REVISED ACTION: Use the variable which holds the helper state ---
temperature: "{{ day_temp_input }}"
# --- END REVISED ACTION ---
hvac_mode: heat hvac_mode: heat
- service: logbook.log - service: logbook.log
data: data:
name: Smart Thermostat name: Smart Thermostat
message: 'Day Mode: Heating set to day temperature' message: 'Day Mode: Heating set to dynamic day temperature'
# Priority 6: Schedule Night Time - Set to night temperature # Priority 5: Schedule Night Time - Set to night temperature
- conditions: - conditions:
- condition: state - condition: state
entity_id: !input schedule_helper entity_id: !input schedule_helper
@ -424,5 +300,5 @@ action:
name: Smart Thermostat name: Smart Thermostat
message: 'Heating OFF: No active heating conditions met' message: 'Heating OFF: No active heating conditions met'
mode: single mode: restart
max_exceeded: silent max_exceeded: silent