Garage door controller.

ESP32 + ESPHome controlling two garage doors with relay pulses, reed switches reporting true state, and a Bluetooth proxy thrown in for free. Talks directly to Home Assistant.

A small board, about the size of a deck of cards, that opens and closes two garage doors and reports whether each one is up. It runs ESPHome and talks to Home Assistant over the native API.

LilyGO TTGO T-Relay: ESP32, four onboard relays, 4 MB flash, WiFi and Bluetooth, $15 from AliExpress.

Architecture

home assistantcover entities · automationslilygo TTGO T-RelayESP32 · ESPHome · BLE proxy2× pulse_relay scripts · 20ms reed debounceleft doorrelay (out) + reed (in)right doorrelay (out) + reed (in)native APISolid arrows out: relay pulses (open/close commands).Dashed arrows in: reed switch state (ground truth: the door's actual position).Same board doubles as a Bluetooth proxy for BLE temp sensors the rest of the network couldn't reach.
Two relays out, two reed switches in. Home Assistant sees one cover entity per door whose state reflects the actual door, not what was last commanded.

Reed switches

Without state detection, a controller only knows the last command it sent. Reed switches screwed to the doors and into the garage floor give the ESP32 the real position, so Home Assistant shows whether each door is actually open right now.

ESPHome

Each door pairs a relay (output, presses the opener button) with a reed switch (input, reads the position). A template cover: joins them into one entity per door:

cover:
  - platform: template
    name: "Left Garage Door"
    device_class: garage
    lambda: 'return id(garage_door_sensor_left).state ? COVER_OPEN : COVER_CLOSED;'
    open_action:  { script.execute: pulse_relay_left }
    close_action: { script.execute: pulse_relay_left }
    stop_action:  { script.execute: pulse_relay_left }

Open, close and stop all run the same script, because a real opener has one button and it toggles. The script turns the relay on for 500 ms:

script:
  - id: pulse_relay_left
    then:
      - switch.turn_on: garage_door_relay_left
      - delay: 500ms
      - switch.turn_off: garage_door_relay_left

Each reed switch has a 20 ms delayed_on_off filter. As a door starts moving the magnet doesn't pass the sensor cleanly, and without the filter the state would flicker for a fraction of a second.

Bluetooth proxy

The board sits in a small cabinet in the garage, wired up with reused CAT5 scraps, and it's in range of two BLE temperature sensors. ESPHome's built-in Bluetooth proxy lets any ESP32 forward BLE advertisements to Home Assistant, so the same board extends BLE coverage into a corner of the house that had none before:

esp32_ble_tracker:
  scan_parameters:
    active: true

bluetooth_proxy:
  active: true

It's pretty cool how cheap and easy it is to hook a microcontroller up to things these days.