Detect a hot-water heating fault before the shower
A cylinder can still be warm when an element, contactor or control path stops doing useful work. Check whether temperature rises when measured power says heating is active, whether two probes agree, and whether the expected heating window has elapsed.
The fault this project detects
This project detects electrical input without the expected tank response, an overdue heating cycle and contradictory top and lower tank probes. It does not diagnose the element or thermostat directly; it identifies evidence that heating performance is abnormal.
The example assumes a storage electric hot-water system with non-invasive temperature probes, whole-circuit power monitoring and a Home Assistant Schedule helper representing when heating is normally available. Controlled-load supply may not follow your own schedule, so model the real installation.
The evidence model
A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:
- Value: tank temperature remains the comfort and safety backstop.
- Direction: the temperature derivative shows whether stored water is actually heating.
- Agreement: top and lower probes expose stratification, probe movement and broader tank response.
- Context and time: circuit power and the expected heating window determine when a rise should occur, with persistence for thermal lag.
The strongest early clue is a contradiction: sustained element-circuit power but no calibrated positive temperature response. That can appear before the upper tank probe reaches a cold threshold.
Freshness is separate evidence
An entity can keep a plausible old value without becoming unavailable. Inspect last_changed on a source that should genuinely change, and work out your own longest healthy interval between meaningful state changes by scrolling back through that entity's History over a normal week and timing the longest gap between real updates. If the measured process can legitimately stay flat, add a changing source heartbeat or timestamp; do not treat an unchanged temperature, level or power value as proof that the transport is alive.
Rate maths: never copy a gradient
The Trend integration expresses min_gradient in units per second. The arithmetic example from the project brief is 4 °C/hour ÷ 3600 = 0.00111 °C/s. That is an example conversion, not a threshold for this equipment. Calculate the rate from your own timestamped readings: pick two points from that entity's History, subtract the earlier value from the later one for your measured change, divide by the number of seconds between their timestamps for the elapsed seconds, and that division gives you the site-specific gradient. A Derivative helper can instead display a friendlier per-hour unit when configured that way.
Collect this data before choosing a threshold
Record complete healthy heating cycles with normal household draw, including power start and stop, both temperature curves and recovery after showers or overnight use.
| Measurement | Why it matters | Required value |
|---|---|---|
sensor.hot_water_top_temperature | Stored-water result | Watch this sensor through a full normal heating cycle and note the minimum and maximum °C it reaches, with timestamps, so you know what healthy looks like. |
sensor.hot_water_lower_temperature | Second thermal view | Compare this sensor's curve against the top sensor over the same cycle and note the normal temperature difference, or stratification, between them. |
sensor.hot_water_temperature_rate | Heating response | Watch this derivative sensor for a few minutes after heating power switches on and note the °C/h rise rate you would call healthy. |
sensor.hot_water_circuit_power | Electrical input proxy | Check this sensor's History and note the typical wattage range when the circuit is idle, or standby, versus actively heating. |
schedule.hot_water_heating_window | Expected availability | Enter the actual heating window from your own tariff or controlled-load schedule. |
The experiment that makes this article defensible
- Capture at least three ordinary healthy heating cycles, including one after meaningful hot-water use.
- Align circuit power, top and lower temperatures, derivative and schedule on one timeline.
- Measure the normal lag between power starting and a reliable positive temperature rate.
- Use a genuine fault or technician-supervised service event for failure evidence; never disconnect, bypass or expose the element to stage one.
Keep your own record as you go: timestamped readings, your Home Assistant version, the exact entity IDs you used, and one or two History or dashboard screenshots. Do not stage an unsafe electrical, refrigeration, pressure, battery or water fault merely to make a graph.
Create the helpers
Use Settings → Devices & services → Helpers. Create the named Number or Toggle helpers before loading the templates. Where this guide uses a Derivative, Statistics, History Stats, Integral, Schedule or Utility Meter helper, the current Home Assistant interface can create it; the source links below describe the current options.
| Helper or derived entity | Purpose | Setting |
|---|---|---|
sensor.hot_water_temperature_rate | Derivative helper for the selected tank probe in °C/h | Try a time window against your own History and pick the one that reliably shows the healthy rise rate you measured earlier. |
schedule.hot_water_heating_window | Expected heating availability | Enter the actual heating periods from your own tariff or timer schedule. |
input_number.hot_water_heating_power_w | Separates active heating from standby | Set this wattage threshold between the standby and active ranges you measured on the circuit-power sensor. |
input_number.hot_water_min_rise_c_per_hour | Minimum healthy positive response | Set this a little below the healthy rise rate you measured after power switches on. |
input_number.hot_water_fault_hold_minutes | Thermal-lag allowance before Warning | Set a hold time that allows for the normal thermal lag you observed before a real fault should be reported. |
input_number.hot_water_critical_temperature_c | Site-specific last-line low temperature | Set this to a low temperature that would genuinely concern you for your own household's hot-water needs, based on the healthy range you measured. |
input_boolean.hot_water_monitoring_enabled | Master notification gate | Leave this switched off until you have tested the automation end to end, then turn it on. |
input_number.hot_water_critical_repeat_minutes | Critical repeat spacing | Set a repeat interval greater than zero that matches how often you actually want to be reminded of a critical fault. |
Current Home Assistant YAML
Merge top-level keys with your existing configuration instead of duplicating them. Replace the example entity IDs consistently. If you keep automations in automations.yaml, paste only the automation list items there, without a top-level automation: wrapper.
Ask whether electrical input produces useful heat
The power threshold must come from the measured circuit, and the derivative threshold must come from complete healthy cycles. Do not use the template to control a contactor.
template:
- sensor:
- name: "Hot water probe difference"
unique_id: hot_water_probe_difference
default_entity_id: sensor.hot_water_probe_difference
device_class: temperature
state_class: measurement
unit_of_measurement: "°C"
availability: >
{{ has_value('sensor.hot_water_top_temperature')
and has_value('sensor.hot_water_lower_temperature') }}
state: >
{{ ((states('sensor.hot_water_top_temperature') | float)
- (states('sensor.hot_water_lower_temperature') | float)) | round(2) }}
- binary_sensor:
- name: "Hot water heating warning"
unique_id: hot_water_heating_warning
default_entity_id: binary_sensor.hot_water_heating_warning
device_class: problem
delay_on:
minutes: >
{{ states('input_number.hot_water_fault_hold_minutes') | int(0) }}
availability: >
{{ has_value('sensor.hot_water_circuit_power')
and has_value('sensor.hot_water_temperature_rate') }}
state: >
{% set powered = (states('sensor.hot_water_circuit_power') | float)
>= (states('input_number.hot_water_heating_power_w') | float) %}
{% set slow = (states('sensor.hot_water_temperature_rate') | float)
< (states('input_number.hot_water_min_rise_c_per_hour') | float) %}
{{ powered and slow and is_state('schedule.hot_water_heating_window', 'on') }}
attributes:
reason: >-
Circuit power is {{ states('sensor.hot_water_circuit_power') }} W,
top temperature is {{ states('sensor.hot_water_top_temperature') }} °C,
rate is {{ states('sensor.hot_water_temperature_rate') }} °C/h and
top-lower difference is {{ states('sensor.hot_water_probe_difference') }} °C.
- name: "Hot water heating critical"
unique_id: hot_water_heating_critical
default_entity_id: binary_sensor.hot_water_heating_critical
device_class: problem
state: >
{{ is_state('binary_sensor.hot_water_heating_warning', 'on')
and (states('sensor.hot_water_top_temperature') | float(999999))
<= (states('input_number.hot_water_critical_temperature_c') | float(-999999)) }}Warning and Critical notification actions
Replace the mobile notification action and media-player entity with your own. Put critical-monitoring-alert.mp3 in /media, test it manually, and leave the monitoring Toggle off until calibration is complete.
automation:
- id: hot_water_warning_push
alias: "Hot water - Warning push"
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.hot_water_heating_warning
to: "on"
conditions:
- condition: state
entity_id: input_boolean.hot_water_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Hot water warning"
message: >-
{{ state_attr('binary_sensor.hot_water_heating_warning', 'reason')
or 'The warning condition is active. Check Home Assistant for evidence.' }}
- id: hot_water_critical_repeat
alias: "Hot water - Critical repeat"
mode: restart
triggers:
- trigger: state
entity_id: binary_sensor.hot_water_heating_critical
to: "on"
conditions:
- condition: state
entity_id: input_boolean.hot_water_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Hot water critical"
message: "The critical condition is active. Check the equipment and evidence now."
- action: media_player.play_media
target:
entity_id: media_player.alert_speaker
data:
media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
media_content_type: "audio/mpeg"
- repeat:
while:
- condition: state
entity_id: binary_sensor.hot_water_heating_critical
state: "on"
- condition: template
value_template: >-
{{ states('input_number.hot_water_critical_repeat_minutes') | int(0) > 0 }}
sequence:
- delay:
minutes: >-
{{ states('input_number.hot_water_critical_repeat_minutes') | int(0) }}
- condition: state
entity_id: binary_sensor.hot_water_heating_critical
state: "on"
- action: notify.mobile_app_your_phone
data:
title: "Hot water still critical"
message: "The critical condition remains active."
- action: media_player.play_media
target:
entity_id: media_player.alert_speaker
data:
media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
media_content_type: "audio/mpeg"
Why the logic works
Circuit power establishes expected heating, the derivative tests delivered thermal response, the second probe reveals stratification or probe disagreement, and the schedule stops the system demanding heat when none should be available.
The design is intentionally diagnostic rather than controlling. It reports an impossible or abnormal relationship; it does not bypass equipment protection or decide that a single suspected cause is proven.
Severity tiers without alert fatigue
| Tier | Meaning | Action |
|---|---|---|
| Advisory | Heating response or probe relationship has moved outside the recent healthy envelope. | Dashboard only |
| Warning | Measured heating power persists during the expected window without the calibrated temperature rise. | One push notification |
| Critical | The corroborated performance fault remains active and the measured tank temperature reaches the site-specific low limit. | Push, repeat and audible alert |
A weak clue remains visible without interrupting you. A Warning requires persistence or corroboration. Critical is reserved for direct danger or several independent signals. That separation is what stops a useful monitoring system becoming notification wallpaper.
Troubleshooting
- The derivative lags power
- Measure normal thermal lag and select the helper time window and Warning persistence from that evidence.
- Top and lower probes disagree
- Stratification can be normal. Fix probe positions and calibrate the relationship across whole cycles.
- Controlled load ignores the schedule
- Model actual supply availability or a contactor-state input rather than a guessed clock window.
- Power is present but the circuit is not heating
- That contradiction is the alarm evidence, not proof of a specific failed component. A qualified technician must diagnose it.
- Solar diversion changes the pattern
- Include diverter state and calibrate separate operating profiles if power and heating occur in variable bursts.
Safety and limits
Stored hot water can scald and poorly managed temperatures can create health risks. Do not alter thermostats, tempering valves, elements or fixed wiring from Home Assistant. Electrical and plumbing work must be completed by appropriately licensed trades.
If a webhook is added later, treat its webhook_id like a password, keep local_only: true unless a reviewed design requires otherwise, and never use an unauthenticated webhook for locks, garage doors, power isolation or anything destructive.
Official sources
These are the primary documentation pages used for the design. Check them against the Home Assistant or ESPHome version you are actually running.
Related Smart Home Fix guides
- Fix unreliable entities before trusting alarms
- Separate smart-home devices without breaking discovery
- Reduce false alarms without hiding real faults
FAQ
Can power monitoring prove the hot-water element works?
No. It proves electrical input at the measurement point. Temperature response is required to show that useful heat reached the tank.
Why use two temperature probes?
They reveal stratification, probe movement and whether a response is local or visible through more of the tank. They still need stable, safe placement.
What temperature should I set for hot water?
This guide does not prescribe one. Follow current Australian requirements and advice from a licensed plumber for storage, delivery and tempering.
Can Home Assistant control the element?
This diagnostic design deliberately does not. Fixed high-current switching, safety interlocks and water-temperature control require a compliant engineered installation.
What if the system is on controlled load?
Use actual supply or contactor evidence where available. A guessed schedule can misclassify a normal retailer-controlled period as a fault.