Putting a driveway gate on Home Assistant, and trusting what it says
Code and schematic on GitHub: github.com/nickcupo/esphome-gate-controller
The gate at the end of the driveway came with a key fob and nothing else. I wanted it to open when we get home, open when we leave, tell us who opened it, and never lie about whether it is closed. The first three took an evening. The last one took the rest of the summer.
Hardware
An ESP32 dev board running ESPHome, wired to the gate’s TOPENS control board. One output drives a relay whose contacts sit across the board’s O/S/C dry-contact input, terminals 4 and 5, the same thing the key fob does. The operator is a single-button toggle: from closed, a pulse opens; from open, a pulse closes.
Knowing which way the gate is moving comes from the motor itself. The arm runs on 24 volts DC and the board reverses polarity to reverse direction. Two optocoupler modules, the PC817 kind with the input resistor already on the board, sit across the MOTOR1 terminals wired in opposite polarity. Polarity picks which one conducts: one while the gate opens, the other while it closes. Their isolated outputs feed two ESP inputs. The motor drive is chopped, so each run shows up on the ESP side as a burst of pulses, and the firmware counts pulses per minute on each line rather than reading a level. The first version used limit switches on plain GPIO inputs; the pulse counting replaced it and turned out to matter.
ESPHome exposes the whole thing to Home Assistant as a cover entity, plus a pulse counter per line for diagnostics. The config, the wiring diagram, and every Home Assistant automation below are in the repository.
What it does
- Opens when someone genuinely arrives. An automation watches every person entity for a transition from away to home, so anyone added to Home Assistant is covered without editing anything. Single-run mode means two phones in the same car send one open command.
- Opens when we leave. Connecting to CarPlay while at home, with the gate closed, opens it after a five-second delay so the car has cleared the swing path.
- Scheduled open. Pick a date and time on the dashboard and it arms itself. Handy for deliveries.
- Says who did it. Every open or close sends a push notification naming the person. Identification walks the Home Assistant context chain: the cover’s own state change, then the parent service call that caused it, then a last-user field written by the arrival automation. If nothing resolves, it says “Key fob”.
- Offline and back-online alerts, debounced so a millisecond API blip does not produce a “reconnected” with no matching “disconnected”.
The bug that opened the gate with nobody there
One morning the gate opened at 8:27 with nobody around and sat open for two hours. The cause: when a phone’s battery dies, its device tracker keeps its last value, “not home”, indefinitely. When the phone is charged and reports again, it emits a textbook not-home-to-home transition that looks exactly like an arrival.
The fix is a staleness guard. A phone actually driving home reports every few minutes. A phone that was off has been silent for hours. The automation now rejects any arrival whose previous position report is more than fifteen minutes old. A real arrival that night had a 38-second gap; the phantom had a gap of over ten hours.
Knowing when it is really closed
The close-detect wire is flaky. A healthy close produces well over a hundred thousand pulses. Some closes produce two or three thousand, above the crosstalk floor from the gate moving but below the firmware’s threshold, so the ESP discards them as noise and Home Assistant thinks the gate is still open. The open-detect wire has never missed.
Rather than pretend the hardware is reliable, a separate “verified state” is kept alongside the raw cover, and four automations maintain it:
- Track verified state. Any open or close the ESP reports is trusted. A confirmed close also clears the fault flag and resets the missed-close counter, because the wire demonstrably worked.
- Weak signal warning. If the close line reports pulses in the too-weak band while the gate is sitting idle at open, record the gate as closed anyway, raise a fault flag, and send a notification saying the wire is degrading. The band is bounded on both sides so a healthy close and movement crosstalk cannot trigger it.
- Missed close proven by a later open. A gate can only start opening from closed. So if the ESP reports “opening” while the verified state still says open, that is hard proof a close happened unobserved. Retroactive, so its value is detection, not correction.
- Infer close after a commanded toggle. If Home Assistant, Apple Home, or a voice assistant sent a toggle from a resting open and the ESP reports nothing within 45 seconds, past the firmware’s 30-second travel timeout, commit the close and raise the fault flag. A physical fob press never touches Home Assistant, so this cannot cover it.
The result is a dashboard that says “closed” when the gate is closed, a counter of consecutive missed closes, and a notification that tells me to go check the terminals before the wire fails completely. I would rather it tell me the wire is going than pretend everything is fine.
Everything above is in the repository: github.com/nickcupo/esphome-gate-controller
Tagged home-assistant, esphome, esp32, automation.