ESPHomeESP32DIY

Direction-aware pet flap

Two staggered normally-open reed switches on a Seeed XIAO ESP32-C6 report which way a pet flap swung. Home Assistant keeps an inside / outside location that survives a restart of the board.

Difficulty · IntermediateSetup · 1–2 hoursHA · current stable (native API)ESPHome · 2026.10.0 or newer
Get the pack · from $19Have 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.

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

  1. 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.
  2. 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.
  3. 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.
  4. Event — a template event entity fires inside, outside, or unclear. A counter of openings since boot is diagnostic. Counters on the device start at zero after a restart.
  5. Location — an input_select (unknown, inside, outside) lives in Home Assistant. An automation writes inside or outside when the event says so, and leaves the helper alone on unclear. You can change the helper from the dashboard when the flap lied. The helper survives an ESP restart.
  6. Test mode — a switch that ignores the GPIO reeds and reads two dashboard toggles instead, so you can fire inside and outside before 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

  1. Flash over USB with both reeds open. Confirm the XIAO joins Wi-Fi and both reed entities exist.
  2. Turn on test mode. Toggle the inside test switch, wait for the event, then the outside one. The helper should follow inside and outside, and stay put on unclear.
  3. 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.
  4. 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.
  5. 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.

Changelog

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