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.
A single reed on a pet flap tells you the flap moved. It does not tell you whether the pet went into the house or out of it. This pack puts two normally-open reeds on a Seeed XIAO ESP32-C6, staggered so one closes first when the flap swings in and the other closes first when it swings out. ESPHome publishes an opening event (inside, outside, or unclear). Home Assistant stores the last clear direction in an input_select, which is the location you show on a dashboard.
Wi-Fi credentials stay in secrets.yaml. The page does not name a pet or a doorway.
A sleepy single contact is the battery reed door sensor. This one stays on USB so both edges are awake to be timed against each other.
What problem it solves
You want “the pet is inside” or “the pet is outside” from the flap you already have, with a manual correction when a swing was the wind, and without guessing from one magnet.
How it works
- Two reeds — inside reed on XIAO D0 / GPIO0, outside reed on D1 / GPIO1. Internal pull-ups,
inverted: true, so a contact closing to ground reads as on. Both are debounced at 50 ms. - Rest is open — with the flap centered, both reeds are open. The first reed that closes sets a pending direction. If the other reed also closes within 100 ms, the result is
unclear. - Quiet period — after both reeds are open again for 500 ms, the node arms for the next swing. That gap swallows the bounce of a single pass. A second real pass inside that half-second can merge into the first.
- Event — a template event entity fires
inside,outside, orunclear. A counter of openings since boot is diagnostic. Counters on the device start at zero after a restart. - Location — an
input_select(unknown,inside,outside) lives in Home Assistant. An automation writesinsideoroutsidewhen the event says so, and leaves the helper alone onunclear. You can change the helper from the dashboard when the flap lied. The helper survives an ESP restart. - Test mode — a switch that ignores the GPIO reeds and reads two dashboard toggles instead, so you can fire
insideandoutsidebefore the magnets are glued down.
A swing is evidence the flap moved that way. It is not proof the pet finished the trip. Treat unclear as “look, don’t guess.”
Hardware notes
- Board. Seeed XIAO ESP32-C6. The pack includes the XIAO antenna-select snippet so the onboard antenna is active before Wi-Fi starts. That snippet is specific to this board. D0 is GPIO0: if a reed holds it low at boot, the chip stays in the bootloader. The resting flap must leave both reeds open.
- Reeds. Two normally-open magnetic contacts. On the linked listing, choose the NO color; the NC color reads backwards and can hold GPIO0 low. One magnet rides on the flap, placed so the inside reed sees it first on an inward swing and the outside reed sees it first on an outward swing. The flap’s own latch magnet can false-trigger a reed — check placement with the log before you stick anything permanently.
- Power. USB-C, left plugged in. Both reeds have to be observed together, so the node stays awake.
- Flap. Whatever flap you already have. The reference build was aimed at a screen-door flap; the YAML only cares that the two reeds see the swing in order.
Wiring
Internal pull-ups are on. A closed contact is a short to ground. Do not feed a supply voltage into these pins.
| Reed | XIAO ESP32-C6 |
|---|---|
| Inside, normally open | D0 / GPIO0 and GND |
| Outside, normally open | D1 / GPIO1 and GND |
Snippet
Pins and names are substitutions:
substitutions:
device_name: pet-flap
friendly_name: Pet flap
inside_pin: GPIO0
outside_pin: GPIO1
esp32:
variant: esp32c6
framework:
type: esp-idf
binary_sensor:
- platform: gpio
name: Inside reed
id: inside_reed
pin:
number: ${inside_pin}
mode:
input: true
pullup: true
inverted: true
filters:
- delayed_on_off: 50ms
- platform: gpio
name: Outside reed
id: outside_reed
pin:
number: ${outside_pin}
mode:
input: true
pullup: true
inverted: true
filters:
- delayed_on_off: 50ms
event:
- platform: template
name: Opening
id: opening
event_types:
- inside
- outside
- unclear
# Direction timing, test mode, Wi-Fi, and API: full YAML in the pack
The location helper is Home Assistant, so a reboot of the flap does not forget the last clear answer:
trigger:
- platform: state
entity_id: !input opening_event
action:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.to_state.attributes.event_type == 'inside' }}"
sequence:
- service: input_select.select_option
target:
entity_id: !input location_helper
data:
option: inside
- conditions:
- condition: template
value_template: "{{ trigger.to_state.attributes.event_type == 'outside' }}"
sequence:
- service: input_select.select_option
target:
entity_id: !input location_helper
data:
option: outside
Setup outline
- Flash over USB with both reeds open. Confirm the XIAO joins Wi-Fi and both reed entities exist.
- Turn on test mode. Toggle the inside test switch, wait for the event, then the outside one. The helper should follow
insideandoutside, and stay put onunclear. - Close each reed to ground with a jumper. The matching binary sensor should turn on, and a single close should produce one event after the quiet period.
- Mount the magnet and the two reeds. Swing the flap by hand in each direction ten times. Swap the reeds in YAML if the labels come out backwards.
- Leave test mode off. Correct the helper from the dashboard the first time, so the starting location matches the house.
Troubleshooting
- The board will not boot or will not flash. A reed is holding GPIO0 low. Prop the flap so both reeds are open, then flash again. The rest position has to be “both open” after you mount it, too.
- Every swing is
unclear. The reeds are so close that both close inside 100 ms, or one reed is stuck on. Spread them along the swing, and watch the two binary sensors while you move the flap slowly. - One direction never fires. That reed is normally-closed, wired to 3.3 V, or the magnet passes it on both swings. Continuity-test the NO pair, and move the magnet.
- Location flips back after a restart. The automation is writing from a device sensor that reset, or the helper is a template on the ESP. Use the
input_select. Device counters are allowed to reset. - Wi-Fi is deaf on a XIAO. The antenna switch is still on the external pad. Flash the pack’s antenna snippet and use the onboard antenna.
- Two swings become one. They landed inside the 500 ms quiet window. That window is what rejects the return bounce; a pet who darts in and straight back out can look like one event.
Credits
Based on xerexexe/smart-cat-flap. This write-up is an original USB-powered, two-reed direction sensor plus a Home Assistant location helper, not a paste of that project’s firmware.