diff --git a/ha-smart-thermostat-control.yaml b/ha-smart-thermostat-control.yaml index 1a9f3df..883765e 100644 --- a/ha-smart-thermostat-control.yaml +++ b/ha-smart-thermostat-control.yaml @@ -13,6 +13,25 @@ blueprint: domain: - climate 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_duration: + name: Manual Override Duration + description: 'How long manual temperature changes should override the schedule before returning to automatic control. (Default = 120 minutes)' + default: 120 + selector: + number: + mode: box + min: 30.0 + max: 480.0 + unit_of_measurement: minutes + step: 30.0 window_sensor: name: Window / Door Sensor Group description: Select your grouped or single window / door sensor. @@ -120,17 +139,6 @@ blueprint: max: 0.0 unit_of_measurement: minutes step: 30.0 - sync_delay: - name: Manual Sync Delay - description: 'Delay before syncing manual temperature changes to prevent cascade loops. (Default = 3s)' - default: 3 - selector: - number: - mode: box - min: 1.0 - max: 10.0 - unit_of_measurement: seconds - step: 1.0 trigger: - platform: state @@ -157,12 +165,10 @@ trigger: entity_id: !input thermostat attribute: temperature id: manual_adjustment - for: - seconds: !input sync_delay - platform: state - entity_id: !input thermostat - to: - id: manual_state_change + entity_id: !input manual_override_helper + to: 'off' + id: override_expired - platform: calendar event: end entity_id: !input away_calendar @@ -184,40 +190,40 @@ variables: triggered_thermostat: "{{ trigger.entity_id }}" new_temperature: "{{ trigger.to_state.attributes.temperature | float(0) }}" old_temperature: "{{ trigger.from_state.attributes.temperature | float(0) }}" + override_helper: !input manual_override_helper + day_temp_input: !input day_temp + night_temp_input: !input night_temp + away_temp_input: !input away_temp + schedule_on: "{{ is_state(input_schedule_helper, 'on') }}" + away_active: "{{ is_state(input_away_calendar, 'on') }}" condition: [] action: - choose: - # Priority 0: Manual Adjustment - Sync all thermostats + # Priority 0: Manual Adjustment - Detect and enable override - conditions: - - condition: or - conditions: - - condition: trigger - id: manual_adjustment - - condition: trigger - id: manual_state_change - # Only sync if the temperature actually changed + - condition: trigger + id: manual_adjustment + # Only if temperature actually changed - condition: template value_template: "{{ (new_temperature - old_temperature) | abs > 0.1 }}" - # Check that other thermostats need syncing (have different temps) + # Only if the new temp is different from current schedule targets - condition: template value_template: > - {% set other_thermostats = thermostat_entities | reject('eq', triggered_thermostat) | list %} - {% if other_thermostats | length > 0 %} - {% set needs_sync = namespace(value=false) %} - {% for thermo in other_thermostats %} - {% set thermo_temp = state_attr(thermo, 'temperature') | float(0) %} - {% if (thermo_temp - new_temperature) | abs > 0.1 %} - {% set needs_sync.value = true %} - {% endif %} - {% endfor %} - {{ needs_sync.value }} + {% if is_state(input_away_calendar, 'on') %} + {{ (new_temperature - away_temp_input) | abs > 0.1 }} + {% elif is_state(input_schedule_helper, 'on') %} + {{ (new_temperature - day_temp_input) | abs > 0.1 }} {% else %} - false + {{ (new_temperature - night_temp_input) | abs > 0.1 }} {% endif %} sequence: - # Only sync to OTHER thermostats (not the one that triggered) + # Enable manual override mode + - service: input_boolean.turn_on + target: + entity_id: !input manual_override_helper + # Sync temperature to all other thermostats - repeat: for_each: "{{ thermostat_entities | reject('eq', triggered_thermostat) | list }}" sequence: @@ -226,12 +232,18 @@ action: entity_id: "{{ repeat.item }}" data: temperature: "{{ new_temperature }}" + # Set timer to automatically disable override + - delay: + minutes: !input override_duration + - service: input_boolean.turn_off + target: + entity_id: !input manual_override_helper - service: logbook.log data: name: Smart Thermostat - message: 'Manual Adjustment: {{ triggered_thermostat }} changed to {{ new_temperature }}°, syncing to other thermostats' + message: 'Manual Override: Temperature set to {{ new_temperature }}° for {{ input_override_duration }} minutes' - # Priority 1: Windows/Doors Open - Turn OFF heating + # Priority 1: Windows/Doors Open - Turn OFF heating (overrides manual) - conditions: - condition: state entity_id: !input window_sensor @@ -241,12 +253,15 @@ action: - service: climate.turn_off target: entity_id: !input thermostat + - service: input_boolean.turn_off + target: + entity_id: !input manual_override_helper - service: logbook.log data: name: Smart Thermostat message: 'Heating OFF: Window or door is open' - # Priority 2: Too Warm Outside - Turn OFF heating + # Priority 2: Too Warm Outside - Turn OFF heating (overrides manual) - conditions: - condition: numeric_state entity_id: !input outdoor @@ -255,12 +270,26 @@ action: - service: climate.turn_off target: entity_id: !input thermostat + - service: input_boolean.turn_off + target: + entity_id: !input manual_override_helper - service: logbook.log data: name: Smart Thermostat message: 'Heating OFF: Outside temperature above threshold' - # Priority 3: Away/Holiday Mode - Set to away temperature + # Priority 3: Manual Override Active - Do nothing, keep manual 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: - condition: state entity_id: !input away_calendar @@ -284,7 +313,7 @@ action: name: Smart Thermostat message: 'Away Mode: Heating set to away temperature' - # Priority 4: Schedule Day Time - Set to day temperature + # Priority 5: Schedule Day Time - Set to day temperature - conditions: - condition: state entity_id: !input schedule_helper @@ -311,7 +340,7 @@ action: name: Smart Thermostat message: 'Day Mode: Heating set to day temperature' - # Priority 5: Schedule Night Time - Set to night temperature + # Priority 6: Schedule Night Time - Set to night temperature - conditions: - condition: state entity_id: !input schedule_helper