blueprint: name: "Smart Thermostat Controller" author: Andreas Gammelgaard Damsbo description: "Advanced thermostat automation with window/door sensors, presence detection, time schedules, and holiday calendar integration" domain: automation source_url: https://gdamsbo.dk/forgejo/andreas/ha-smart-thermostat-control/raw/branch/main/ha-smart-thermostat-control.yaml input: thermostat: name: Thermostat description: Select the thermostat(s) to control. selector: entity: 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. selector: entity: domain: - binary_sensor multiple: true window_delay: name: Window / Door Sensor Delay description: 'Time the sensor needs to stay the same after change to trigger the automation. (Default = 5s)' default: 5 selector: number: mode: box min: 0.0 max: 600.0 unit_of_measurement: seconds step: 5.0 outdoor: name: Outdoor Temperature Sensor description: Select your outdoor temperature sensor. selector: entity: domain: - sensor multiple: false wintermode: name: Winter Mode Threshold description: 'The outside temperature needs to be below this to activate winter mode. (Default = 16°C)' default: 16 selector: number: step: 1 min: 10.0 max: 25.0 unit_of_measurement: °C or °F mode: box wintermode_delay: name: Winter Mode Delay description: 'Time the outside temperature needs to stay above the winter mode temperature to turn the heating off.' default: 30 selector: number: mode: box min: 1.0 max: 1440.0 unit_of_measurement: minutes step: 5.0 schedule_helper: name: Schedule Helper description: Select the schedule helper entity to determine day/night heating times. selector: entity: domain: - schedule multiple: false day_temp: name: Day Time Temperature Target default: 21 selector: number: step: 0.5 min: 10.0 max: 30.0 unit_of_measurement: °C or °F mode: box night_temp: name: Night Time Temperature Target default: 18 selector: number: step: 0.5 min: 10.0 max: 30.0 unit_of_measurement: °C or °F mode: box away_temp: name: Away Temperature Target description: Temperature when away/on holiday. default: 16 selector: number: step: 0.5 min: 10.0 max: 30.0 unit_of_measurement: °C or °F mode: box away_calendar: name: Holiday or Away Calendar description: Calendar entity that indicates away/holiday periods. selector: entity: domain: - calendar multiple: false return_offset: name: Return Offset description: 'Time offset to start heating before returning from away. Negative value = minutes before return.' default: -120 selector: number: mode: box min: -1440.0 max: 0.0 unit_of_measurement: minutes step: 30.0 trigger: - platform: state entity_id: !input window_sensor for: seconds: !input window_delay id: window_change - platform: numeric_state entity_id: !input outdoor below: !input wintermode for: minutes: !input wintermode_delay id: cold_weather - platform: numeric_state entity_id: !input outdoor above: !input wintermode for: minutes: !input wintermode_delay id: warm_weather - platform: state entity_id: !input schedule_helper id: schedule_change - platform: state entity_id: !input thermostat attribute: temperature id: manual_adjustment - platform: state entity_id: !input manual_override_helper to: 'off' id: override_expired - platform: calendar event: end entity_id: !input away_calendar offset: !input return_offset id: return_soon - platform: calendar event: start entity_id: !input away_calendar id: leaving - platform: homeassistant event: start id: ha_start - platform: event event_type: automation_reloaded id: reload variables: thermostat_entities: !input thermostat 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 - Detect and enable override - 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(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 %} {{ (new_temperature - night_temp_input) | abs > 0.1 }} {% endif %} sequence: # 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: - service: climate.set_temperature target: 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 Override: Temperature set to {{ new_temperature }}° for {{ input_override_duration }} minutes' # Priority 1: Windows/Doors Open - Turn OFF heating (overrides manual) - conditions: - condition: state entity_id: !input window_sensor state: 'on' match: any sequence: - 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 (overrides manual) - conditions: - condition: numeric_state entity_id: !input outdoor above: !input wintermode sequence: - 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: 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 state: 'on' - condition: numeric_state entity_id: !input outdoor below: !input wintermode - condition: state entity_id: !input window_sensor state: 'off' match: all sequence: - service: climate.set_temperature target: entity_id: !input thermostat data: temperature: !input away_temp hvac_mode: heat - service: logbook.log data: name: Smart Thermostat message: 'Away Mode: Heating set to away temperature' # Priority 5: Schedule Day Time - Set to day temperature - conditions: - condition: state entity_id: !input schedule_helper state: 'on' - condition: numeric_state entity_id: !input outdoor below: !input wintermode - condition: state entity_id: !input window_sensor state: 'off' match: all - condition: state entity_id: !input away_calendar state: 'off' sequence: - service: climate.set_temperature target: entity_id: !input thermostat data: temperature: !input day_temp hvac_mode: heat - service: logbook.log data: name: Smart Thermostat message: 'Day Mode: Heating set to day temperature' # Priority 6: Schedule Night Time - Set to night temperature - conditions: - condition: state entity_id: !input schedule_helper state: 'off' - condition: numeric_state entity_id: !input outdoor below: !input wintermode - condition: state entity_id: !input window_sensor state: 'off' match: all - condition: state entity_id: !input away_calendar state: 'off' sequence: - service: climate.set_temperature target: entity_id: !input thermostat data: temperature: !input night_temp hvac_mode: heat - service: logbook.log data: name: Smart Thermostat message: 'Night Mode: Heating set to night temperature' # Default: Turn off if no conditions match default: - service: climate.turn_off target: entity_id: !input thermostat - service: logbook.log data: name: Smart Thermostat message: 'Heating OFF: No active heating conditions met' mode: restart max_exceeded: silent