Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.
A thermostat that says heating is not the same as a room that is getting warmer. A dirty flame sensor, a frozen coil, or a filter that has gone solid will run the fan for an hour while the temperature sits still — or walks the wrong way. This workflow starts a clock when hvac_action becomes heating or cooling, snapshots the room temperature, waits a dwell you pick, and pages critically if the number did not move far enough in the right direction.
Entity ids are blueprint inputs. Do not publish a live thermostat name.
If an open sash is the more likely reason heat is “on” and the room is not warming, pause the system first with window-open climate pause so this monitor does not treat a propped door as failed equipment.
What problem it solves
You want to know the equipment is doing work — not that the setpoint is armed — before a space sits at the wrong temperature for a whole afternoon.
How it works
- Call starts —
climatehvac_actiongoes toheatingorcooling(notfanoridle). The pack records start temperature in aninput_numberand, for cool, a start timestamp. - Dwell — wait long enough that a healthy system would have moved the room. Defaults in the pack: about 30 minutes for heat, about 90 minutes for cool. Short cycles that hit setpoint and stop before the dwell are a success, not a miss.
- Direction + magnitude — after the wait, if the thermostat is still calling, the room must have dropped (cool) or risen (heat) by at least the tolerance (default about 0.4 °C / 0.7 °F). The tolerance must sit under the thermostat’s deadband or every normal cycle looks like a failure.
- Failure counting — cool: increment a consecutive-failure counter; reset on a good cycle; alert when it hits the limit (pack default 2). Heat: increment a weekly counter (flame sensors fail intermittently); reset Sunday; alert when the week’s count crosses the limit.
- Runaway cool — if cooling has been continuous past a long-runtime helper (hours, not minutes), fire a separate critical notify. That is the dirty-filter / low-refrigerant shape.
- Critical notify — Companion interruption-level
critical(iOS) orhigh(Android), plus a persistent card. Separate actions for heat, cool, and long runtime so the message names the failure mode.
This is a performance check, not a service manual. A page means “look at the equipment,” not a diagnosis of which part failed.
Hardware notes
- Climate entity. Must expose
hvac_actionofheating/cooling/idle. Confirm in Developer Tools → States before you import. Some Wi-Fi stats only reporthvac_mode(what you asked for), not what the equipment is doing — those will not trigger. - Temperature. The thermostat’s current-temperature attribute is enough if it tracks the room. A separate sensor in the same space is better when the stat sits in a hallway.
- No extra hub. Helpers plus a notify service. Power-draw clamps are useful later; they are not required for this pack.
- Smart stats. Nest-class predictive deadbands confuse a naive “did it move?” check. The pack can disable deadband warnings for those.
Snippet
The pack ships a blueprint. Inputs replace real entity ids:
trigger:
- platform: state
entity_id: !input climate_entity
attribute: hvac_action
to: "cooling"
id: cool_start
- platform: state
entity_id: !input climate_entity
attribute: hvac_action
to: "heating"
id: heat_start
action:
- service: input_number.set_value
target:
entity_id: !input start_temp
data:
value: "{{ state_attr(climate_entity, 'current_temperature') }}"
- delay:
minutes: !input cool_runtime
- condition: state
entity_id: !input climate_entity
attribute: hvac_action
state: "cooling"
- condition: template
value_template: >
{{ (start | float - current | float) < (tolerance | float) }}
- service: notify.notify
data:
title: "HVAC — cooling not moving the room"
data:
push:
interruption-level: critical
# heat branch + counters: full blueprint in the pack
Setup outline
- Confirm
hvac_actionon the climate entity. Create the pack helpers (start temp, two failure counters, cooling start datetime, long-runtime threshold). - Import the blueprint. Point it at the climate entity (and optional room sensor). Leave defaults for the first week.
- Watch traces on a normal heat and a normal cool cycle. If a healthy run increments a failure counter, drop the tolerance or raise the dwell — do not chase the notify yet.
- Test the alert path without touching the equipment. For one cycle, set the tolerance higher than a healthy system can reach in the dwell (for example 5 °C / 9 °F) and shorten the dwell to about 10 minutes. Then raise the setpoint a few degrees (heat) or lower it (cool) so the thermostat calls. Confirm one critical push, then put the tolerance, dwell, and setpoint back.
Troubleshooting
- Never triggers. The integration does not publish
hvac_action, or it usesheat/coolinstead ofheating/cooling. Fix the attribute mapping; do not trigger onhvac_modealone. - False alarm every cycle. Tolerance is larger than the deadband, or the dwell is longer than a short winter cycle. Lower tolerance first; then shorten dwell.
- Missed a real dead unit. Dwell is shorter than the room’s thermal mass, or you are reading a sensor in a different space than the stat. Move the sensor or lengthen cool dwell toward 90 minutes.
- Heat pages on an open window. The room is dumping heat on purpose. Use window-open climate pause so
hvac_actiongoes idle while the opening is actually open.
Credits
Based on the community blueprint HVAC Health Monitor — detect system performance issues. This write-up is an original overview of the call-then-measure pattern.