Counting sump pump cycles

I’m working on creating a sump pump sensor, and one of the features I’d like is to count the number of times the pump runs. Below is a graph of the water level in my sump, and the pump events are obvious every ~10 minutes. I’m working on eliminating the measurement noise, but that’s a separate problem.
image

While the events are very obvious to a human, I’m having trouble coming up with a robust way to detect them. This would admittedly be easier if I fixed the noise issue first, but I believe there should be a way to accomplish this in a robust manner with data as it exists currently.

Ideally what I’d like to do is:

  1. Take the average of the sensor readings from between 15 seconds and 10 seconds in the past
  2. Take the average of the sensor readings from between 5 seconds and 0 seconds in the past
  3. Calculate the difference between the two. If the difference is > 0.05 meters, then increment a counter.

But I can’t come up with a way to do item #1; there doesn’t seem to be a way to get a buffer of stale data without also including the fresh data.

For now I’m using two max filters, one with a 20sec window and one with a 10 sec window, and calculating the difference between them every 10 seconds. But I’m finding the noise is causing this to not be robust at all.

Any tips appreciated.

Here is a similar question:

Measure time between peaks in a graph - Configuration - Home Assistant Community (home-assistant.io)

and here

How to keep track of time between state changes? - Configuration - Home Assistant Community (home-assistant.io)

Maybe a “hacky” solution, but I could think of an average sensor for the last 15 seconds for example, an input_number as helper entity and an automation that runs every minute.
The input_number stores the value of the average sensor from a minute ago.
The automation does:

  • Check if the current value of the average sensor is 0.05 lower than that of the input_number, then increase the counter
  • set input_number = current value

History Stats - Home Assistant tells you.

you can use the Derivative sensor to check whether the tank level is increasing or decreasing (positive vs negative derivative) and how fast.

I use something similar for my fish tank temperature to ensure the heater is working as expected.

sensor:
  - platform: derivative
    source: sensor.tank_sensor_temperature
    name: Tank Temperature Derivative
    round: 3
    unit_time: h 
    time_window: "00:30:00" 
    unit: '°F/Hr'

here is how it looks relative to the tank temp:

you could use that derivative sensor in an automation so that if the derivative is positive (>0) for a period of time then increase the pump counter.

you would just need to set the derivative config to smooth out the variations and I think it could work

Or there is a Trend sensor you might be able to use but I really don’t have any experience with that.

I should have mentioned I was hoping to contain all the code within the ESP32 device I’m using, so that the data is captured and calculated even if there is a HA shutdown or WiFi issue. The derivative would be great if there was a similar function within ESPHome.

OK, here’s a solution for dummies like me who aren’t good at statistical analysis:

Just plug the pump into a smart plug. Here’s one of my templates to catch cycles:

template:
  - binary_sensor:
      - name: "East Pump Cycle On"
        unique_id: 'east_pump_on'
        state: "{{ states('sensor.east_sump_pump_current')|float > 1 }}"

And a sensor to count them over the past hour:

sensor:
  - platform: history_stats
    name: East Pump On 1H Count
    entity_id: binary_sensor.east_pump_cycle_on
    state: "on"
    type: count
    end: "{{ now() }}"
    duration:
      hours: 1

As I was typing this you posted that you want it in an ESP32. So this probably won’t work for you. I’ll post anyway, in case someone else like me stumbles on this thread.

Yeah, that changes everything…

Sorry I can’t help with that.

  1. Take the average of the sensor readings from between 15 seconds and 10 seconds in the past

sensor filter

  1. Take the average of the sensor readings from between 5 seconds and 0 seconds in the past
  2. Calculate the difference between the two. If the difference is > 0.05 meters, then increment a counter.

To do this on an esp you could try this approach

Create a analog_threshold binary sensor

Then create a number component

Under the analog_threshold sensor use something along these lines

binary_sensor:
  - platform: analog_threshold
    # ...
    on_state:
      then:
        - lambda: |-
            auto call = id(my_number).make_call();
            call.set_value( x + 1 );
            call.perform();

edit this will update on it turning on and off, so it’s not going to work as is

Add the condition

  if:
    condition:
      binary_sensor.is_on: my_binary_sensor

I wasn’t aware of the analog_threshold platform; I think this is exactly what I needed. I’m using global variables elsewhere in my code so I’ll use those to store the ‘stale’ readings.

I’ll come back and post my solution when I have it working but I’m confident I can get there now. Thanks!

Turns out that the comparion of past (“stale”) data to current data wasn’t necessary; the analog_threshold platform was all I really needed. Here’s the snippets I ended up with:

substitutions:
  friendly_name: Sump Pump Sensor
  pumped_out_below_height: '0.100' #when the level falls below this value, turn on the pump counter trigger
  pumped_out_reset_height: '0.140' #when the level rises above this value, reset (turn off) the pump counter trigger

globals:
  - id: sump_pump_counter_int
    type: int
    restore_value: true
    initial_value: '0'

binary_sensor:
  - platform: analog_threshold
    name: "${friendly_name} Pumpout Event"
    id: pumpout_event
    sensor_id: level_actual
    threshold:
      upper: $pumped_out_below_height
      lower: $pumped_out_reset_height
    filters:
      - invert:
      - delayed_on: 10s #debounce after falling below_height
      - delayed_off: 10s #debounce after rising above reset_height
    on_press:
      lambda: |-
        // Add one to the global integer
        id(sump_pump_counter_int) += 1;
        // Force the sensor to publish a new state
        id(sump_pump_counter).publish_state(id(sump_pump_counter_int));

sensor:
  - platform: template
    name: "${friendly_name} Pump Counter"
    id: sump_pump_counter
    update_interval: never
    accuracy_decimals: 0
    state_class: total_increasing
    lambda: return id(sump_pump_counter_int);
  - platform: ads1115
    id: level_actual
    ... (rest of code for setting up and filtering the sensor)
2 Likes

Turns out that code didn’t work as I expected. When there was noise around the 0.140 level, it would register multiple triggers. I realized I could get what I wanted easier by using a template sensor instead. Below is my new sensor code if anyone is following along, although I haven’t tested to confirm if the debounces work as expected. But I can confirm the hysteresis (and everything else) does.

  # Hysteresis sensor to trip when sump pump is emptied. When tripped, increment the counter and calculate time between pumpouts
  - platform: template
    name: "${friendly_name} Pumpout Event"
    id: pumpout_event
    lambda: |-
      if (id(level_actual).state < $pumped_out_below_height) {
        return true;
      } else if (id(level_actual).state > $pumped_out_reset_height) {
        return false;
      } else {
        return {};
      }
    filters:
      - delayed_on: 2s #debounce after falling below_height
      - delayed_off: 5s #debounce after rising above reset_height
    on_press:
      # when state transisions from false to true, a pumpout event occurred. 
      lambda: |-
        // Add one to the global integer
        id(sump_pump_counter_int) += 1;

        // Force the sensor to publish a new state
        id(sump_pump_counter).publish_state(id(sump_pump_counter_int));
1 Like

I’m wanting to do this exact this.

  1. Is this still working well for you? Any updates or upgrades?
  2. Is this the code on the ESP32 or in Home Assistant?

I’m still running that code. It’s running on the ESP32 itself. Here’s the whole build if you’re interested:
https://community.home-assistant.io/t/monitor-for-sump-pump-with-battery-backup/

edit: this is the code I’m using, with the specific code highlighted:

1 Like