ESPHomeESP32Home Assistant

Reboot-safe ESPHome event counter

An ESPHome counter sensor stores door openings or slow reed pulses on the ESP32 and restores them after a restart, so a Utility Meter helper is not starting from zero every boot.

Difficulty · IntermediateSetup · 30–60 minutesHA · current stable (native API)ESPHome · 2026.10.0 or newer
Get the pack · from $9Have us set it up

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.

pulse_meter and pulse_counter are the right tools for a flow rate, and their on-device total starts over at 0 after a reboot. Home Assistant can paper over that with total_increasing, which is what the ESP32-C6 water meter does. A door, a gate, or a meter that only clicks a few times a minute is a different job: you want the number on the chip itself to still be there after a restart. ESPHome 2026.10.0 added a counter sensor that stores a 64-bit integer and restores it.

Wi-Fi credentials stay in secrets.yaml. Do not publish a live meter total.

What problem it solves

You want a running count of openings or slow pulses that survives a reboot, and a daily Utility Meter in Home Assistant that keeps counting instead of treating every boot as a new meter.

How it works

  1. One GPIO, one edge — a normally-open reed sits between the pin and ground. The pin’s pull-up holds it high. Each closure takes a GPIO binary sensor to on. The counter adds one when that sensor changes to on. Holding the contact closed does not keep adding. A repeated on with no off in between is not a new event.
  2. Restore — restore defaults to true. The value is kept in RAM and flushed to flash on the preferences interval, which defaults to one minute. A restart after that flush comes back at the stored count. A power cut can drop the pulses from the current interval, because they may not have been written yet. Shortening the interval toward 0s writes more often and wears flash. Leave it at one minute for a door. The first boot, or restore: false, starts at initial_value (default 0).
  3. Seed — counter.set_value sets the integer. The pack exposes that as a native-API action so you can type the number you already read on a mechanical register. The published sensor state is a float, so it prints whole numbers exactly only up to 16,777,216. The stored integer is exact past that. Door openings never get there.
  4. Utility Meter — in Home Assistant, add a Utility Meter helper on this sensor with state_class: total_increasing, cycled daily. Because a reboot no longer resets the ESP number to 0, the helper’s daily delta is “pulses since midnight,” not “pulses since boot.” If restore ever does fail and the number falls, total_increasing treats the fall as a meter reset.
  5. Not a flow rate — this component does not report liters per minute. A spinning dial that must become a flow entity still wants pulse_meter, as in the water-meter pack. Use the counter when each edge is an event you can afford to debounce in tens of milliseconds.

On ESP8266 the same component keeps the value in RTC memory unless that chip is told to use flash. This pack is an ESP32-C6. ESP32 writes the preferences store to flash on the interval above.

Hardware notes

  • Board. ESP32-C6 Super Mini, USB-powered. Leave GPIO8 (LED and strap), GPIO9 (BOOT and strap), GPIO12 and GPIO13 (USB), and GPIO15 (strap) alone. The pack uses GPIO6, with the internal pull-up. Same pin choice as the water-meter pack, so do not put both a water pulse and this contact on GPIO6 of one board.
  • Reed. Normally open, closed only while the magnet is near. A normally-closed door contact sits on all day and then counts once, backwards from the story you wanted. The linked set is 2×14 mm normally-open glass reeds plus small magnets.
  • Door versus meter. On a door, stick the magnet on the moving leaf and the reed on the frame so the sensor is on only while the leaf is open. The counter then counts openings, one per swing. On a meter, the reed clicks once per revolution and must release between clicks.
  • Debounce. A glass reed bounces. delayed_on and delayed_off of a few tens of milliseconds collapse a bounce into one edge. Raise them if one swing counts as two. Lower them if a fast meter pulse disappears.

Wiring

Reed ESP32-C6 Super Mini
Contact A GPIO6
Contact B GND

The internal pull-up is enabled in YAML. An external resistor is optional.

Snippet

The pin and the debounce are substitutions.

substitutions:
  contact_pin: GPIO6
  debounce: 30ms

esp32:
  board: esp32-c6-devkitc-1
  framework:
    type: esp-idf

preferences:
  flash_write_interval: 1min

binary_sensor:
  - platform: gpio
    id: contact_edge
    name: "Contact"
    pin:
      number: ${contact_pin}
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on: ${debounce}
      - delayed_off: ${debounce}

sensor:
  - platform: counter
    id: event_count
    name: "Event count"
    binary_sensor: contact_edge
    restore: true
    accuracy_decimals: 0
    state_class: total_increasing
    unit_of_measurement: "events"

api:
  actions:
    - action: set_event_count
      variables:
        new_total: int
      then:
        - counter.set_value:
            id: event_count
            value: !lambda 'return new_total;'

The pull-up holds the pin high while the reed is open, so the binary sensor rests at on only when nothing is across it. A magnet that closes the reed while a door is shut takes the sensor off. The counter adds one when the sensor next changes to on, which is the opening. A meter pulse does the same: one count when the reed releases after the click. Leave inverted off unless you have checked the edge with a magnet. A door contact you already trust in Home Assistant is a different entity. This GPIO exists to feed the counter.

Wi-Fi and the API encryption key stay in secrets.yaml. The native API action shows up under Developer Tools → Actions after the node is adopted.

Setup outline

  1. Flash the YAML. With the reed open, the contact entity should be on (the pull-up) and the count should sit at 0, or at a restored value on a later flash.
  2. Bring the magnet to the reed. The contact goes off and the count stays put. Take the magnet away. The count increases by one. Hold the magnet on the reed. The count must not climb. Take it away again and the count increases by one more.
  3. Restart the ESPHome device from the button or a power cycle that lasts past a minute of quiet. The count should return at the same number, not 0. A power cut in the same minute as a pulse can still miss that pulse.
  4. If a mechanical register already shows a total, call set_event_count once with that integer.
  5. Add a Utility Meter helper. Source: the event-count entity. Cycle: daily. Confirm tomorrow’s helper starts at 0 while the ESP entity keeps the lifetime total.

Troubleshooting

  • Count returns to 0 after a reboot. ESPHome is older than 2026.10.0, so this platform does not exist, or restore: false is set. Update, and leave restore on.
  • Count climbs while the magnet sits still. The binary sensor is oscillating. Increase debounce. A normally-closed switch will also look noisy if it is wired where a normally-open reed belongs.
  • One swing counts two or three. Bounce shorter than the filter. Raise delayed_on and delayed_off together (try 80 ms) and test again.
  • Missed meter pulses. The pulse is shorter than delayed_on, or the reed never releases. This counter is for edges you can separate. A dial that must become liters per minute belongs on ESP32-C6 water meter.
  • Daily helper jumps by the lifetime total. The ESP number fell (restore missed, or you called set_event_count with a smaller value) and total_increasing treated it as a reset. Seed upward, or reset the helper’s cycle after a deliberate seed.
  • Last pulse missing after a power cut. It was still in RAM. The default flush is one minute. That is the window you can lose. Do not set the interval to 0s to chase it. Flash wears out.
  • Board will not boot. The reed landed on a strap pin and holds it during reset. Move it to GPIO6.

Credits

The component is the ESPHome counter sensor, added in ESPHome 2026.10.0. Flash flushes follow preferences. This write-up is an original ESP32-C6 recipe: a normally-open reed, a restored event count, a seed action, and a Utility Meter on top. It does not replace a pulse_meter flow total.

Changelog

v1.0 · Oct 27, 2026: First publish.