Smart Home Fix

Detect pump short cycling from start count and runtime

A pump may still deliver water while starting far too often. Counting starts and comparing them with total runtime can expose a leaking line, failed pressure vessel, bad non-return valve or unstable control before the pump simply stops.

The fault this project detects

The target is an abnormal concentration of starts: many separate on periods within a measured window, usually with low total runtime. Continuous high demand is a different fault and should not be labelled short cycling.

The example monitors a pressure pump through a reliable running binary sensor. History Stats calculates how many on periods occurred and how many hours the pump ran inside the same rolling window.

Pump short-cycle evidence flow Multiple measurements are combined into advisory, warning and critical fault states. Start countTotal runtimeRolling windowWater-use context Evidence model direction + context agreement + duration Advisory dashboard only Warning one push Critical repeat + audible
Start count becomes meaningful only when the observation window and total runtime are kept alongside it.

The evidence model

A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:

  1. Starts in window. History Stats type count measures how many on periods occur in the chosen interval.
  2. Runtime in window. A second History Stats entity distinguishes rapid short runs from a legitimate long draw.
  3. Expected use. Irrigation, filling or heavy household demand provides context for a busy pump.
  4. Persistence. The alert is based on a completed pattern, not one bounce or a Home Assistant restart.

Home Assistant identifies the duty pattern; it does not declare the failed component. The pressure vessel, check valve, pressure switch, pipework and water demand still need inspection.

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 quiet overnight periods, normal household demand and the busiest legitimate use. Verify that the running binary sensor has a clean state and does not chatter around standby power.

MeasurementWhy it mattersRequired value
binary_sensor.pressure_pump_runningClean on periods and transition timestampsPull this entity's own History for a representative period and check its on/off transitions line up cleanly with real pump runs.
sensor.pump_starts_in_windowNormal start count by use patternRead this sensor's own History over a normal-use period and note the start count that's typical for your property.
sensor.pump_runtime_in_windowNormal total on-time in the same windowRead this sensor's own History over the same period and note your normal total on-time, in hours.
water-use scheduleKnown irrigation or transfer activityList your own known irrigation, transfer or heavy-use periods, from a timer, controller log or memory, so they can be told apart from a fault.
pump controllerMinimum run or anti-cycle behaviourCheck your own pump controller's manual or nameplate for any built-in minimum-run or anti-cycle setting it already applies.

The experiment that makes this article defensible

  1. Record a quiet period with no deliberate water use and note spontaneous starts.
  2. Run one normal tap, irrigation zone or transfer cycle and capture start count plus total runtime.
  3. Confirm that one long run does not look like many short runs in History Stats.
  4. If a known short-cycling event already exists, preserve its trace before repairs; do not create pressure faults deliberately.

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 entityPurposeSetting
input_number.pump_cycle_window_hoursRolling observation windowEnter a window, in hours, long enough to smooth over one normal demand episode on your own property, based on what you saw in History.
input_number.pump_short_cycle_warning_startsStart count that deserves reviewEnter a start count, within that window, clearly above your own quiet-baseline pattern from History.
input_number.pump_short_cycle_critical_startsSevere start countEnter a higher start count than the Warning figure, reflecting a genuinely severe pattern for your own pump.
input_number.pump_short_cycle_max_runtime_hoursMaximum total runtime still considered short cyclingEnter the maximum total runtime, in hours, within the window that's still consistent with short runs rather than legitimate demand on your own property.
input_boolean.pump_short_cycle_monitoring_enabledMaster notification gateLeave this switched off until you've tested the automation and are ready to receive real alerts.
input_number.pump_short_cycle_critical_repeat_minutesCritical repeat spacingEnter 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.

Count on periods and measure their total time

History Stats requires exactly two of start, end and duration. These sensors use a templated rolling start and now as the end. Its count includes a pump already on at the start boundary, which is acceptable when the threshold is calibrated with the same method.

sensor:
  - platform: history_stats
    name: "Pump starts in window"
    unique_id: pump_starts_in_window
    entity_id: binary_sensor.pressure_pump_running
    state: "on"
    type: count
    start: >-
      {{ now() - timedelta(
         hours=states('input_number.pump_cycle_window_hours') | int(0)) }}
    end: "{{ now() }}"

  - platform: history_stats
    name: "Pump runtime in window"
    unique_id: pump_runtime_in_window
    entity_id: binary_sensor.pressure_pump_running
    state: "on"
    type: time
    start: >-
      {{ now() - timedelta(
         hours=states('input_number.pump_cycle_window_hours') | int(0)) }}
    end: "{{ now() }}"

template:
  - binary_sensor:
      - name: "Pump short-cycle warning"
        unique_id: pump_short_cycle_warning
        default_entity_id: binary_sensor.pump_short_cycle_warning
        device_class: problem
        availability: >
          {{ has_value('sensor.pump_starts_in_window')
             and has_value('input_number.pump_short_cycle_warning_starts') }}
        state: >
          {{ (states('sensor.pump_starts_in_window') | float)
             >= (states('input_number.pump_short_cycle_warning_starts') | float) }}
        attributes:
          reason: >-
            {{ states('sensor.pump_starts_in_window') }} starts and
            {{ states('sensor.pump_runtime_in_window') }} running hours
            inside the configured window.

      - name: "Pump short-cycle critical"
        unique_id: pump_short_cycle_critical
        default_entity_id: binary_sensor.pump_short_cycle_critical
        device_class: problem
        state: >
          {{ (states('sensor.pump_starts_in_window') | float(0))
                >= (states('input_number.pump_short_cycle_critical_starts') | float(999999))
             and (states('sensor.pump_runtime_in_window') | float(999999))
                <= (states('input_number.pump_short_cycle_max_runtime_hours') | 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_short_cycle_warning_push
    alias: "Pump cycling - Warning push"
    mode: single
    triggers:
      - trigger: state
        entity_id: binary_sensor.pump_short_cycle_warning
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.pump_short_cycle_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Pump cycling warning"
          message: >-
            {{ state_attr('binary_sensor.pump_short_cycle_warning', 'reason')
                or 'The warning condition is active. Check Home Assistant for evidence.' }}

  - id: pump_short_cycle_critical_repeat
    alias: "Pump cycling - Critical repeat"
    mode: restart
    triggers:
      - trigger: state
        entity_id: binary_sensor.pump_short_cycle_critical
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.pump_short_cycle_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Pump cycling 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_short_cycle_critical
              state: "on"
            - condition: template
              value_template: >-
                {{ states('input_number.pump_short_cycle_critical_repeat_minutes') | int(0) > 0 }}
          sequence:
            - delay:
                minutes: >-
                  {{ states('input_number.pump_short_cycle_critical_repeat_minutes') | int(0) }}
            - condition: state
              entity_id: binary_sensor.pump_short_cycle_critical
              state: "on"
            - action: notify.mobile_app_your_phone
              data:
                title: "Pump cycling 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

Start count alone confuses one long draw with repeated starts. Total on-time inside the identical window separates the two patterns. The rolling window must be longer than a normal demand episode but short enough to expose the damaging cluster you care about.

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

TierMeaningAction
AdvisoryPump starts are accumulating faster than the quiet baseline.Dashboard only
WarningStart count reaches the measured abnormal level in the rolling window.One push notification
CriticalThe start count is severe while total runtime remains characteristic of short runs.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 count is one higher than expected
History Stats count can include an on state already active at the window start. Calibrate and interpret the sensor using that documented behaviour.
The running sensor flickers
Add hysteresis or delay to the power-derived running entity before counting it.
Irrigation always triggers it
Use a separate operating profile or suppress Warning during known irrigation while leaving raw counts visible.
History is incomplete after a restart
Confirm Recorder retention covers the full rolling window and allow History Stats to repopulate.
A continuous leak does not short-cycle
That is a different pattern: long runtime or continuous flow. Create a separate long-run fault instead of stretching this rule.

Safety and limits

Do not alter pressure-switch settings, accumulator pre-charge or pump controls without the correct procedure. Stored pressure and electrical supply can injure. Use a pump technician, plumber or licensed electrician as the equipment requires.

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

FAQ

What counts as pump short cycling?

It is a site-specific pattern of repeated short runs rather than one universal number of starts. Pump type, pressure vessel, demand and controller settings all matter.

Why measure total runtime as well as starts?

Many starts with low total runtime describes short cycling. A similar start count with long total runtime may reflect legitimate demand or a different fault.

Does History Stats count transitions?

Its count type counts periods in which the entity matched the requested state and may include an entity already in that state at the start boundary. Calibrate with that documented behaviour.

Can this diagnose a failed pressure tank?

It can reveal a duty pattern consistent with several faults, including a pressure-vessel problem. It cannot identify the component without physical testing.

Will the rolling count survive limited Recorder history?

Only if Recorder retains enough detailed history for the selected window. Verify retention and entity history before relying on the result.