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
- 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 toon. Holding the contact closed does not keep adding. A repeatedonwith nooffin between is not a new event. - Restore —
restoredefaults totrue. 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 toward0swrites more often and wears flash. Leave it at one minute for a door. The first boot, orrestore: false, starts atinitial_value(default 0). - Seed —
counter.set_valuesets 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. - 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_increasingtreats the fall as a meter reset. - 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
onall 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
ononly 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_onanddelayed_offof 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
- 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. - Bring the magnet to the reed. The contact goes
offand 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. - 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.
- If a mechanical register already shows a total, call
set_event_countonce with that integer. - 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: falseis set. Update, and leaverestoreon. - 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_onanddelayed_offtogether (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_countwith a smaller value) andtotal_increasingtreated 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
0sto 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.