Detect a pump running dry before it fails
A pump can be electrically on while moving little or no water. Home Assistant can detect that impossible relationship by comparing real power, measured flow, source level and how long the condition lasts.
The fault this project detects
The target fault is a commanded or powered pump that is not producing the expected flow. Depending on the pump, dry-running power may rise, fall or look deceptively normal, so a universal watt threshold is unsafe.
The worked system is a rainwater or bore transfer pump with a circuit power sensor, a flow sensor, a source tank-level sensor and Home Assistant. It observes only; the pump's manufacturer protection remains the primary safeguard.
The evidence model
A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:
- Running state. Measured power establishes that the motor is drawing meaningful power rather than trusting a relay command.
- Hydraulic result. Flow below the pump's measured normal minimum shows that the expected work is not happening.
- Source condition. A low source tank or bore header strengthens the dry-run interpretation.
- Power signature and time. Power outside the healthy running band or a sustained no-flow period filters brief priming and valve transitions.
The alarm describes the contradiction - powered pump, inadequate flow - not an unproven mechanical diagnosis. A blocked line, closed valve, lost prime, sensor fault or dry source can produce similar evidence.
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 representative stretch of normal operation and timing the biggest gap between genuine changes. 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: open History for the entity, pick two points recorded during normal operation, and divide the change in value between them by the number of seconds that elapsed to get the site-specific gradient to enter as min_gradient. A Derivative helper can instead display a friendlier per-hour unit when configured that way.
Collect this data before choosing a threshold
Capture several healthy starts, steady runs, stops and any manufacturer-approved priming period. Record power and flow on the same timestamps. Do not deliberately run a pump dry to create training data.
| Measurement | Why it matters | Required value |
|---|---|---|
sensor.bore_pump_power | Standby, start and stable running power | Read this from your own History: the W it settles at during standby, at start-up, and while running steadily. |
sensor.bore_flow_rate | Priming flow and healthy steady flow | Read this from your own History: the L/min during priming and once flow is steady. |
sensor.source_tank_level | Lowest normal operating level | Check this sensor's own History for the lowest level it reaches under normal use, in whichever unit it reports. |
run duration | Longest healthy prime and no-flow transition | Time it from your own History graph: how long your longest healthy prime and no-flow transition actually take. |
fault protection | Existing controller, float and thermal protection | Look up your own controller's make, model and built-in protection behaviour from its manual or nameplate. |
The experiment that makes this article defensible
- Capture multiple normal starts from command through stable flow, with power and flow aligned by timestamp.
- Close only a normal downstream outlet so the pump stops through its intended pressure control; do not dead-head it beyond manufacturer operation.
- Record a low-source event only if it occurs naturally or can be simulated through the controller's approved test function.
- Replay thresholds with monitoring disabled and confirm normal priming remains Advisory or clear.
Keep your own record as you calibrate: timestamped History readings, your Home Assistant version, the exact entity IDs you used, and a screenshot or two of the History or dashboard views that show the pattern. 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 |
|---|---|---|
input_number.pump_running_watts | Separates meaningful motor power from standby | Enter the running-power figure, in W, that you measured for this pump above. |
input_number.pump_minimum_flow_l_min | Lowest healthy flow after priming | Enter the lowest healthy post-priming flow, in L/min, that you measured above. |
input_number.pump_normal_power_low_watts | Lower edge of healthy running band | Enter the lower W edge of your own pump's healthy running band, from your measurements above. |
input_number.pump_normal_power_high_watts | Upper edge of healthy running band | Enter the upper W edge of your own pump's healthy running band, from your measurements above. |
input_number.pump_low_source_level | Source level that corroborates dry-running | Enter your tank's own low-level threshold, in % or litres to match your sensor, from its History. |
input_number.pump_dry_run_hold_minutes | No-flow persistence before Warning | Enter the no-flow duration, in minutes, you measured as normal priming above, plus a margin. |
input_number.pump_dry_run_critical_hold_minutes | Persistence before Critical | Enter a longer minutes value than the Warning hold, based on how much further you're willing to let the fault run before escalating. |
input_boolean.pump_dry_run_monitoring_enabled | Master notification gate | Leave this switched off until you've tested the automation and are ready to receive real alerts. |
input_number.pump_dry_run_critical_repeat_minutes | Critical repeat spacing | Enter a repeat interval, in minutes greater than zero, based on how often you want repeat critical alerts. |
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.
Compare electrical input with hydraulic output
The power band is deliberately two-sided because a fault can change motor load in either direction. All limits come from helpers populated with your measured pump data.
template:
- binary_sensor:
- name: "Bore pump running"
unique_id: bore_pump_running
default_entity_id: binary_sensor.bore_pump_running
device_class: running
availability: >
{{ has_value('sensor.bore_pump_power')
and has_value('input_number.pump_running_watts') }}
state: >
{{ (states('sensor.bore_pump_power') | float)
>= (states('input_number.pump_running_watts') | float) }}
- name: "Bore pump power abnormal"
unique_id: bore_pump_power_abnormal
default_entity_id: binary_sensor.bore_pump_power_abnormal
device_class: problem
availability: >
{{ has_value('sensor.bore_pump_power')
and has_value('input_number.pump_normal_power_low_watts')
and has_value('input_number.pump_normal_power_high_watts') }}
state: >
{% set watts = states('sensor.bore_pump_power') | float %}
{% set low = states('input_number.pump_normal_power_low_watts') | float %}
{% set high = states('input_number.pump_normal_power_high_watts') | float %}
{{ is_state('binary_sensor.bore_pump_running', 'on')
and (watts < low or watts > high) }}
- name: "Pump dry-run warning"
unique_id: pump_dry_run_warning
default_entity_id: binary_sensor.pump_dry_run_warning
device_class: problem
delay_on:
minutes: >
{{ states('input_number.pump_dry_run_hold_minutes') | int(0) }}
availability: >
{{ has_value('sensor.bore_flow_rate')
and has_value('input_number.pump_minimum_flow_l_min') }}
state: >
{{ is_state('binary_sensor.bore_pump_running', 'on')
and (states('sensor.bore_flow_rate') | float)
<= (states('input_number.pump_minimum_flow_l_min') | float) }}
attributes:
reason: >-
Pump power is {{ states('sensor.bore_pump_power') }} W
but measured flow is {{ states('sensor.bore_flow_rate') }} L/min.
- name: "Pump dry-run critical"
unique_id: pump_dry_run_critical
default_entity_id: binary_sensor.pump_dry_run_critical
device_class: problem
delay_on:
minutes: >
{{ states('input_number.pump_dry_run_critical_hold_minutes') | int(0) }}
state: >
{{ is_state('binary_sensor.pump_dry_run_warning', 'on')
and (is_state('binary_sensor.bore_pump_power_abnormal', 'on')
or ((states('sensor.source_tank_level') | float(999999))
<= (states('input_number.pump_low_source_level') | 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: pump_dry_run_warning_push
alias: "Pump dry-run - Warning push"
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.pump_dry_run_warning
to: "on"
conditions:
- condition: state
entity_id: input_boolean.pump_dry_run_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Pump dry-run warning"
message: >-
{{ state_attr('binary_sensor.pump_dry_run_warning', 'reason')
or 'The warning condition is active. Check Home Assistant for evidence.' }}
- id: pump_dry_run_critical_repeat
alias: "Pump dry-run - Critical repeat"
mode: restart
triggers:
- trigger: state
entity_id: binary_sensor.pump_dry_run_critical
to: "on"
conditions:
- condition: state
entity_id: input_boolean.pump_dry_run_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Pump dry-run 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.pump_dry_run_critical
state: "on"
- condition: template
value_template: >-
{{ states('input_number.pump_dry_run_critical_repeat_minutes') | int(0) > 0 }}
sequence:
- delay:
minutes: >-
{{ states('input_number.pump_dry_run_critical_repeat_minutes') | int(0) }}
- condition: state
entity_id: binary_sensor.pump_dry_run_critical
state: "on"
- action: notify.mobile_app_your_phone
data:
title: "Pump dry-run 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
Power says the motor is doing something; flow says whether the system is moving water. Source level and the healthy power band provide independent corroboration. The delay allows normal priming but must remain shorter than the equipment's safe no-flow period, which comes from the manufacturer or a pump technician - not from this page.
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 | Pump power is present while flow is still establishing. | Dashboard only |
| Warning | Powered operation continues without the measured minimum flow. | One push notification |
| Critical | No-flow operation persists and a low source or abnormal motor-load signature agrees. | 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 alert fires during priming
- Measure the longest healthy prime and set persistence from that evidence, within the pump's safe operating limit.
- Flow reads zero at low demand
- Confirm the flow sensor's starting threshold and orientation; some meters cannot resolve small flows.
- Power looks normal during a fault
- That is why flow is primary evidence. Do not force a power-signature rule that the pump does not exhibit.
- Variable-speed power moves constantly
- Compare power against commanded speed or pressure setpoint, or use a model-specific operating envelope.
- The tank sensor is unavailable
- Keep the no-flow Warning available, but do not claim source-level corroboration until the sensor is valid.
Safety and limits
Do not use a smart plug as motor protection unless the device and installation are specifically engineered for the load. Fixed wiring, current clamps and pump isolation are licensed-electrical work. Keep thermal, pressure, dry-run and float protection supplied by the manufacturer.
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 watts alone detect a pump running dry?
Not reliably across different pumps. Dry-running can raise, lower or barely change power. Flow, source level, runtime and the pump's own measured healthy power band make the conclusion stronger.
How long should no flow be allowed?
Use the longest measured healthy priming period while remaining inside the manufacturer's safe no-flow limit. If that limit is unknown, this is where a pump technician should set the protection.
What if the flow sensor fails?
Treat an unavailable or stale flow sensor as a monitoring fault. Do not convert missing flow data into proof of dry-running without independent evidence.
Can Home Assistant replace a dry-run controller?
No. Home Assistant is a useful monitoring and escalation layer, but dedicated pump protection operates closer to the equipment and should remain in place.
Should the automation turn the pump off?
Only after a site-specific electrical and hydraulic safety review. The article intentionally alerts without controlling the pump because a false stop can also cause damage or interrupt essential water.