Home Assistant's template engine has no abs() function, so every run of
Stove On Detection failed at its first variable ("'abs' is undefined") and
the HVAC failure monitor would fail the same way. Deployment note now points
at labctl blueprints:pull ha.
Claude-Session: https://claude.ai/code/session_01JBPyi1vyBbme5DKykW3Z25
166 lines
5.4 KiB
YAML
166 lines
5.4 KiB
YAML
blueprint:
|
|
name: HVAC Failure Monitor
|
|
description: >
|
|
Detects HVAC underperformance and hard failure by monitoring temperature trends when heating or cooling is active.
|
|
Uses differential between setpoint and outdoor temp to dynamically adjust expectations.
|
|
domain: automation
|
|
input:
|
|
thermostat:
|
|
name: Thermostat
|
|
description: The climate entity to monitor (must support hvac_action and setpoint attributes).
|
|
selector:
|
|
entity:
|
|
domain: climate
|
|
|
|
heating_temp_sensors:
|
|
name: Heating Temperature Sensors
|
|
description: One or more temperature sensors to monitor during heating (e.g. upstairs).
|
|
default: []
|
|
selector:
|
|
target:
|
|
entity:
|
|
domain: sensor
|
|
device_class: temperature
|
|
|
|
cooling_temp_sensors:
|
|
name: Cooling Temperature Sensors
|
|
description: One or more temperature sensors to monitor during cooling (e.g. downstairs).
|
|
default: []
|
|
selector:
|
|
target:
|
|
entity:
|
|
domain: sensor
|
|
device_class: temperature
|
|
|
|
outdoor_sensor:
|
|
name: Outdoor Temperature Source
|
|
description: Outdoor temperature source (e.g. from a weather entity).
|
|
selector:
|
|
entity:
|
|
domain: weather
|
|
|
|
monitoring_interval:
|
|
name: Monitoring Interval (minutes)
|
|
description: How long after HVAC starts to evaluate temperature change.
|
|
default: 20
|
|
selector:
|
|
number:
|
|
min: 5
|
|
max: 120
|
|
unit_of_measurement: minutes
|
|
mode: slider
|
|
|
|
notify_target:
|
|
name: Notification Target
|
|
description: Who to notify on potential HVAC failure (e.g. mobile app).
|
|
default: []
|
|
selector:
|
|
target:
|
|
entity:
|
|
domain: notify
|
|
|
|
mode: single
|
|
|
|
trigger:
|
|
- platform: state
|
|
entity_id: !input thermostat
|
|
attribute: hvac_action
|
|
to:
|
|
- heating
|
|
- cooling
|
|
|
|
variables:
|
|
thermostat: !input thermostat
|
|
heating_temp_sensors: !input heating_temp_sensors
|
|
cooling_temp_sensors: !input cooling_temp_sensors
|
|
outdoor_sensor: !input outdoor_sensor
|
|
interval: !input monitoring_interval
|
|
hvac_action: "{{ state_attr(thermostat, 'hvac_action') }}"
|
|
direction: >
|
|
{{ 'rising' if hvac_action == 'heating' else 'falling' }}
|
|
selected_sensors: >
|
|
{{ heating_temp_sensors if hvac_action == 'heating' else cooling_temp_sensors }}
|
|
|
|
condition: []
|
|
|
|
action:
|
|
- variables:
|
|
indoor_start: >
|
|
{{ expand(selected_sensors) | map(attribute='state') | map('float') | select('defined') | list | average }}
|
|
|
|
- delay:
|
|
minutes: "{{ interval }}"
|
|
|
|
- variables:
|
|
indoor_now: >
|
|
{{ expand(selected_sensors) | map(attribute='state') | map('float') | select('defined') | list | average }}
|
|
outdoor_now: >
|
|
{{ state_attr(outdoor_sensor, 'temperature') | float }}
|
|
setpoint: >
|
|
{{ state_attr(thermostat, 'temperature') | float }}
|
|
delta: >
|
|
{{ (setpoint - outdoor_now) | abs }}
|
|
required_rate: >
|
|
{% set d = delta %}
|
|
{{ [0.01, [0.10, 0.10 - (d * 0.004)] | min] | max }}
|
|
observed_rate: >
|
|
{{ ((indoor_now - indoor_start) / interval) | round(3) }}
|
|
trend_ok: >
|
|
{% if direction == 'rising' %}
|
|
{{ observed_rate >= required_rate }}
|
|
{% else %}
|
|
{{ observed_rate <= -required_rate }}
|
|
{% endif %}
|
|
hard_failure: >
|
|
{% if direction == 'rising' %}
|
|
{{ observed_rate < 0 }}
|
|
{% else %}
|
|
{{ observed_rate > 0 }}
|
|
{% endif %}
|
|
hvac_state_icon: >
|
|
{% if hard_failure %}
|
|
mdi:close-circle-outline
|
|
{% elif not trend_ok %}
|
|
mdi:alert-circle-outline
|
|
{% else %}
|
|
mdi:check-circle-outline
|
|
{% endif %}
|
|
hvac_state_text: >
|
|
{% if hard_failure %}
|
|
HVAC Failure
|
|
{% elif not trend_ok %}
|
|
Possible HVAC underperformance
|
|
{% else %}
|
|
Normal operation
|
|
{% endif %}
|
|
|
|
- choose:
|
|
- conditions: "{{ not trend_ok }}"
|
|
sequence:
|
|
- choose:
|
|
- conditions: "{{ notify_target | length > 0 }}"
|
|
sequence:
|
|
- service: notify.{{ notify_target }}
|
|
data:
|
|
message: >
|
|
HVAC '{{ hvac_action }}' action for {{ interval }} minutes showed ineffective operation.
|
|
Indoor temp started at: {{ indoor_start }}°, now: {{ indoor_now }}°
|
|
Outdoor temp: {{ outdoor_now }}°
|
|
Setpoint: {{ setpoint }}°
|
|
Required rate: {{ required_rate }}°/min
|
|
Observed rate: {{ observed_rate }}°/min
|
|
Status: {{ hvac_state_text }} {{ hvac_state_icon }}
|
|
default:
|
|
- service: persistent_notification.create
|
|
data:
|
|
title: HVAC Alert
|
|
message: >
|
|
HVAC '{{ hvac_action }}' action for {{ interval }} minutes showed ineffective operation.
|
|
Indoor temp started at: {{ indoor_start }}°, now: {{ indoor_now }}°
|
|
Outdoor temp: {{ outdoor_now }}°
|
|
Setpoint: {{ setpoint }}°
|
|
Required rate: {{ required_rate }}°/min
|
|
Observed rate: {{ observed_rate }}°/min
|
|
Status: {{ hvac_state_text }} {{ hvac_state_icon }}
|
|
|