# Climate Device Controller Blueprint # Controls climate devices based on environmental sensors, window/door states, # and schedules with hysteresis support. See README.md for detailed documentation. # # Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com) blueprint: name: "Custom: Climate Device Control" description: > Controls climate devices based on window/door sensors with decay duration and value threshold for temperature, humidity, or other environmental control. Supports any device type (switches, climate entities, smart remotes, etc.) through customizable turn on/off actions. domain: automation # =========================================================================== # INPUT CONFIGURATION # =========================================================================== input: # ------------------------------------------------------------------------- # General Settings # ------------------------------------------------------------------------- primary_group: name: "General" collapsed: false input: device_entity: name: Device Entity description: > Entity used to monitor the device state (on/off). Can be a switch, climate entity, or any entity with on/off states. This is used for power monitoring and state checks. selector: entity: domain: - switch - climate - fan - light - input_boolean turn_on_action: name: Turn On Action description: > Action to execute when the device should be turned ON. Examples: - For switch: switch.turn_on with entity_id - For climate: climate.turn_on or climate.set_hvac_mode - For smart remote: remote.send_command with your IR/RF code - For script: script.turn_on selector: action: {} turn_off_action: name: Turn Off Action description: > Action to execute when the device should be turned OFF. Examples: - For switch: switch.turn_off with entity_id - For climate: climate.turn_off or climate.set_hvac_mode to 'off' - For smart remote: remote.send_command with your IR/RF code - For script: script.turn_on (for off script) selector: action: {} control_switch: name: Control Switch description: > Master switch that enables/disables this automation. When OFF, the device will be turned off regardless of other conditions. selector: entity: domain: - binary_sensor - input_boolean force_on_entity: name: Force ON Switch (optional) description: > When this entity is ON, the device will be FORCED ON regardless of all other conditions (except being higher priority than emergency). Useful for manual override when you want the device always running. Leave empty to disable this feature. default: [] selector: entity: domain: - input_boolean - switch - binary_sensor multiple: true # ------------------------------------------------------------------------- # Doors & Windows Configuration # ------------------------------------------------------------------------- doors_group: name: "Doors & Windows" collapsed: false input: house_windows: name: House Window Sensors description: > Window sensors for the entire house. If ANY is open, device may turn off. Leave empty if not using house-wide window monitoring. default: [] selector: entity: domain: binary_sensor multiple: true room_windows: name: Room Window Sensors description: > Window sensors for the room where the device is located. Device can run when room is sealed (all closed). default: [] selector: entity: domain: binary_sensor multiple: true room_doors: name: Room Door Sensors description: > Door sensors for the room where the device is located. Combined with room windows to determine if room is sealed. default: [] selector: entity: domain: binary_sensor multiple: true decay_duration: name: Decay Duration (seconds) description: > Time to wait after a door/window state change before reacting. Prevents toggling when briefly opening doors. default: 3 selector: number: min: 0 max: 600 unit_of_measurement: seconds mode: slider # ------------------------------------------------------------------------- # Environment Sensors & Target # ------------------------------------------------------------------------- env_group: name: "Environment" collapsed: false input: env_sensors: name: Environment Sensors description: > Sensors that measure the current environmental value (temperature, humidity, etc.) in the room. default: [] selector: entity: domain: sensor multiple: true target_value_entity: name: Target Value Entity description: > Entity (e.g., input_number) that defines the target value. Device turns OFF when sensor value reaches this target. selector: entity: domain: input_number hysteresis_window: name: Hysteresis Window description: > Buffer zone to prevent rapid on/off cycling. Device turns ON when value drops below (target - window). Example: Target 30%, window 5% → turns OFF at 30%, turns ON at 25%. Set to 0 to disable hysteresis (device toggles exactly at target). default: 0 selector: number: min: 0 max: 50 step: 0.5 mode: slider value_threshold: name: Emergency Threshold description: > If ANY sensor value falls below this threshold, device will be FORCED ON regardless of other conditions (emergency override). Set to 0 to disable this feature. default: 0 selector: number: min: 0 max: 100 mode: slider value_is_low_entity: name: "Low Value Indicator Entity (optional)" description: > Optional input_boolean that will be turned ON when value is below the emergency threshold. Useful for dashboard indicators. default: null selector: entity: domain: - input_boolean # ------------------------------------------------------------------------- # Schedule Configuration # ------------------------------------------------------------------------- schedule_group: name: "Schedules" collapsed: false input: schedule_entities: name: Schedules (optional) description: > Schedule entities that define when the device may run. Device only operates when ANY schedule is active (ON). Leave empty to allow running at any time. default: [] selector: entity: domain: schedule multiple: true # ------------------------------------------------------------------------- # Power Monitoring # ------------------------------------------------------------------------- power_group: name: "Power" collapsed: false input: power_sensor: name: Power Sensor (optional) description: > Sensor reporting device power consumption (W). Leave empty to disable power monitoring. default: selector: entity: domain: sensor power_threshold: name: Power Threshold (W) description: > If device is ON but power consumption is below this value, it may indicate a malfunction (e.g., heater element failure). default: 10 selector: number: min: 0 max: 50 unit_of_measurement: "W" power_decay_duration: name: Power Decay Duration (s) description: > Time to wait after power reading changes before flagging as problematic. Prevents false alarms during startup. default: 10 selector: number: min: 1 max: 50 unit_of_measurement: "s" power_problematic_indicator_entity: name: Power Problem Indicator Entity (optional) description: > Optional input_boolean that will be turned ON when the device appears to be malfunctioning (ON but low power consumption). default: null selector: entity: domain: input_boolean # ------------------------------------------------------------------------- # Debug # ------------------------------------------------------------------------- debug_group: name: "Debug" collapsed: true input: enable_debug_notifications: name: Enable Debug Notifications description: > Send persistent notifications for debugging automation behavior. Shows current state of all variables and filtering decisions. default: false selector: boolean: # ============================================================================= # AUTOMATION MODE # ============================================================================= # Single mode prevents multiple simultaneous executions mode: single # ============================================================================= # TRIGGERS # ============================================================================= trigger: # Control switch state changed - platform: state entity_id: !input control_switch # Force ON switch state changed - platform: state entity_id: !input force_on_entity # House window sensor changed (with decay delay) - platform: state entity_id: !input house_windows for: seconds: !input decay_duration # Room window sensor changed (with decay delay) - platform: state entity_id: !input room_windows for: seconds: !input decay_duration # Room door sensor changed (with decay delay) - platform: state entity_id: !input room_doors for: seconds: !input decay_duration # Target value changed - platform: state entity_id: !input target_value_entity # Environment sensor value changed - platform: state entity_id: !input env_sensors # ============================================================================= # CONDITIONS # ============================================================================= condition: [] # ============================================================================= # ACTIONS # ============================================================================= action: - variables: # ----------------------------------------------------------------------- # Input Variables # ----------------------------------------------------------------------- env_sensors: !input env_sensors control_switch: !input control_switch device_entity: !input device_entity threshold: !input value_threshold value_is_low_entity: !input value_is_low_entity force_on_entity: !input force_on_entity hysteresis_window: !input hysteresis_window # ----------------------------------------------------------------------- # Device State # ----------------------------------------------------------------------- is_device_on: "{{ states(device_entity) not in ['off', 'unavailable', 'unknown'] }}" # ----------------------------------------------------------------------- # Force ON Check # ----------------------------------------------------------------------- # Check if any force ON entity is enabled (list may be empty) is_force_on: > {{ force_on_entity | length > 0 and force_on_entity | select('is_state', 'on') | list | length > 0 }} # ----------------------------------------------------------------------- # Target Value & Hysteresis # ----------------------------------------------------------------------- target_value_entity: !input target_value_entity target_value: "{{ states(target_value_entity) | float(0) }}" # Turn-on threshold accounts for hysteresis window turn_on_threshold: "{{ target_value - hysteresis_window }}" # ----------------------------------------------------------------------- # Value Statistics # ----------------------------------------------------------------------- # Calculate sensor counts for different thresholds # Returns: [count_below_emergency, count_below_turn_on, count_at_or_above_target] value_stats: > {% set result = [] %} {% if env_sensors | length > 0 %} {% set values = expand(env_sensors) | map(attribute='state') | map('float', default=0) | list %} {# Count sensors below emergency threshold #} {% if threshold != 0 %} {% set result = result + [values | select('lt', threshold) | list | count] %} {% else %} {% set result = result + [0] %} {% endif %} {# Count sensors below turn-on threshold (target - hysteresis) #} {% if turn_on_threshold != 0 %} {% set result = result + [values | select('lt', turn_on_threshold) | list | count] %} {% else %} {% set result = result + [0] %} {% endif %} {# Count sensors at or above target value (for turn-off decision) #} {% if target_value != 0 %} {% set result = result + [values | select('ge', target_value) | list | count] %} {% else %} {% set result = result + [0] %} {% endif %} {% else %} {% set result = [0, 0, 0] %} {% endif %} {{ result }} # True if ANY sensor is below the emergency threshold is_value_below_threshold: "{{ (value_stats[0] | int) > 0 }}" # True if ANY sensor is below the turn-on threshold (target - hysteresis) is_value_below_turn_on_threshold: "{{ (value_stats[1] | int) > 0 }}" # True if ALL sensors are at or above the target value is_value_at_or_above_target: "{{ (value_stats[2] | int) == (env_sensors | length) and (env_sensors | length) > 0 }}" # ----------------------------------------------------------------------- # Power Monitoring # ----------------------------------------------------------------------- power_sensor: !input power_sensor power_threshold: !input power_threshold power_problematic_indicator_entity: !input power_problematic_indicator_entity power_decay_duration: !input power_decay_duration power: "{{ states(power_sensor) | float(0) if power_sensor is not none else 0 }}" # Device is problematic if it's consuming power but below threshold is_power_not_ok: "{{ (power > 0 and power < power_threshold) if power_sensor is not none and power_threshold != 0 else false }}" # ----------------------------------------------------------------------- # Window/Door State # ----------------------------------------------------------------------- house_windows: !input house_windows room_windows: !input room_windows room_doors: !input room_doors decay: !input decay_duration # Check if ALL house windows are closed (for decay duration) house_closed: > {% if house_windows | length == 0 %} {{ false }} {% else %} {% set ns = namespace(res = true) %} {% for it in house_windows %} {% set time_closed = (now() - states[it].last_changed).total_seconds() %} {% set ns.res = ns.res and (is_state(it, 'off') and time_closed > decay) %} {% endfor %} {{ ns.res }} {% endif %} # Check if ALL room windows AND doors are closed (for decay duration) # BUG FIX: Original had wrong operator precedence in condition room_closed: > {% set window_count = room_windows | length %} {% set door_count = room_doors | length %} {% if window_count == 0 and door_count == 0 %} {{ false }} {% else %} {% set ns = namespace(res = true) %} {# Check all room windows #} {% for it in room_windows %} {% set time_closed = (now() - states[it].last_changed).total_seconds() %} {% set ns.res = ns.res and (is_state(it, 'off') and time_closed > decay) %} {% endfor %} {# Check all room doors #} {% for it in room_doors %} {% set time_closed = (now() - states[it].last_changed).total_seconds() %} {% set ns.res = ns.res and (is_state(it, 'off') and time_closed > decay) %} {% endfor %} {{ ns.res }} {% endif %} # ----------------------------------------------------------------------- # Schedule Check # ----------------------------------------------------------------------- schedule_entities: !input schedule_entities # True if any schedule is active, or if no schedules are configured schedule_active: > {% if schedule_entities | length > 0 %} {{ schedule_entities | select('is_state', 'on') | list | length > 0 }} {% else %} {{ true }} {% endif %} # ----------------------------------------------------------------------- # Debug Flag # ----------------------------------------------------------------------- is_debug: !input enable_debug_notifications # --------------------------------------------------------------------------- # DEBUG: Log current state # --------------------------------------------------------------------------- - choose: - conditions: "{{ is_debug }}" sequence: - service: persistent_notification.create data: title: "Climate Device Controller Debug" message: > **Device State:** - Device: {{ states(device_entity) }} (on: {{ is_device_on }}) - Control Switch: {{ states(control_switch) }} - Force ON: {{ is_force_on }} - Schedule Active: {{ schedule_active }} **Environment:** - Target Value: {{ target_value }} - Turn-On Threshold: {{ turn_on_threshold }} (hysteresis: {{ hysteresis_window }}) - Below Emergency Threshold: {{ is_value_below_threshold }} - Below Turn-On Threshold: {{ is_value_below_turn_on_threshold }} - At/Above Target: {{ is_value_at_or_above_target }} **Doors & Windows:** - House Closed: {{ house_closed }} - Room Closed: {{ room_closed }} **Power Monitoring:** - Power Sensor: {{ power_sensor | default('not configured') }} - Power: {{ power }} W - Power OK: {{ not is_power_not_ok }} # --------------------------------------------------------------------------- # POWER MONITORING: Flag device if malfunctioning # --------------------------------------------------------------------------- # Note: For climate entities, 'on' state check works for most HVAC modes. # For entities that don't use 'on' state, power monitoring may need adjustment. - choose: - conditions: - condition: template value_template: "{{ power_sensor is not none and states(device_entity) not in ['off', 'unavailable', 'unknown'] and power_problematic_indicator_entity is not none }}" sequence: - variables: # Check if enough time has passed since last power reading timeout_elapsed: > {% set last = as_timestamp(states[power_sensor].last_changed) %} {{ (as_timestamp(now()) - last) > power_decay_duration }} - condition: template value_template: "{{ timeout_elapsed }}" - choose: # Power is problematic - flag the indicator - conditions: "{{ is_power_not_ok }}" sequence: - service: input_boolean.turn_on target: entity_id: "{{ power_problematic_indicator_entity }}" # Power is OK - clear the indicator default: - service: input_boolean.turn_off target: entity_id: "{{ power_problematic_indicator_entity }}" # --------------------------------------------------------------------------- # LOW VALUE INDICATOR: Update the low value indicator entity # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ value_is_low_entity is not none }}" sequence: - choose: # Value is below threshold - turn on indicator - conditions: - condition: template value_template: "{{ is_value_below_threshold }}" sequence: - service: input_boolean.turn_on target: entity_id: !input value_is_low_entity # Value is OK - turn off indicator default: - service: input_boolean.turn_off target: entity_id: !input value_is_low_entity # =========================================================================== # MAIN CONTROL LOGIC (Priority Order) # =========================================================================== - choose: # ----------------------------------------------------------------------- # PRIORITY 1: Force ON Override (Manual) # ----------------------------------------------------------------------- # If force ON switch is enabled, FORCE device ON regardless of all else - conditions: - condition: template value_template: "{{ is_force_on }}" sequence: - if: - condition: template value_template: "{{ not is_device_on }}" then: !input turn_on_action # ----------------------------------------------------------------------- # PRIORITY 2: Emergency Override (Safety) # ----------------------------------------------------------------------- # If value is below emergency threshold, FORCE device ON - conditions: - condition: template value_template: "{{ is_value_below_threshold }}" sequence: - if: - condition: template value_template: "{{ not is_device_on }}" then: !input turn_on_action # ----------------------------------------------------------------------- # PRIORITY 3: Control Switch Off # ----------------------------------------------------------------------- # If master control is disabled, FORCE device OFF - conditions: - condition: template value_template: "{{ is_state(control_switch, 'off') }}" sequence: - if: - condition: template value_template: "{{ is_device_on }}" then: !input turn_off_action # ----------------------------------------------------------------------- # PRIORITY 4: Environment Not Ready # ----------------------------------------------------------------------- # If environment not sealed OR schedule not active, turn OFF - conditions: - condition: template value_template: "{{ not (house_closed or room_closed) or not schedule_active }}" sequence: - if: - condition: template value_template: "{{ is_device_on }}" then: !input turn_off_action # ----------------------------------------------------------------------- # PRIORITY 5: Target Reached # ----------------------------------------------------------------------- # If all sensors at or above target value, turn OFF - conditions: - condition: template value_template: "{{ is_value_at_or_above_target }}" sequence: - if: - condition: template value_template: "{{ is_device_on }}" then: !input turn_off_action # ----------------------------------------------------------------------- # PRIORITY 6: Below Turn-On Threshold # ----------------------------------------------------------------------- # If any sensor below turn-on threshold (target - hysteresis), turn ON - conditions: - condition: template value_template: "{{ is_value_below_turn_on_threshold }}" sequence: - if: - condition: template value_template: "{{ not is_device_on }}" then: !input turn_on_action # ------------------------------------------------------------------------- # DEFAULT: Maintain Current State (Hysteresis Zone) # ------------------------------------------------------------------------- # Value is between turn-on threshold and target - don't change device state # This prevents rapid on/off cycling when value is near the target default: []