Files

670 lines
26 KiB
YAML

# 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: []