Skip to content

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Garage Door Monitor

A Home Assistant custom integration that monitors and controls a garage door which has only a single button (open, close, and stop are all the same press) plus two contact sensors — one at the bottom of the travel and one at the top. It infers the full door state (open / closed / opening / closing / stopped), drives the button, and learns how long the door takes to open and close so it can report a live position percentage.

This is a rebuild of the original monitorgarage AppDaemon app as a proper UI-configurable integration: no input_select helper and no template cover are needed — the integration provides the cover entity itself.

How the sensors are read

Bottom sensor Top sensor Meaning Position
off off Fully closed 0%
on off Mid-travel inferred
on on Fully open 100%
off on invalid (ignored)
  • Bottom sensor turns on as soon as the door leaves the fully-closed position.
  • Top sensor turns on once the door reaches fully-open.

The sensors can't reveal direction while the door is mid-travel, so direction is inferred from the endpoint the door most recently left (leaving closed → opening, leaving open → closing), exactly as the AppDaemon app did. The single button is what stops a moving door or reverses a stopped one — something the contacts can never show on their own.

Position learning

The integration times each clean run and averages it into a learned open-duration and close-duration, then interpolates the position while the door moves.

A run only counts if it goes endpoint → the opposite endpoint with no button press in between. Any press mid-run is a stop or a reverse, so that run is marked dirty and its timing is discarded — partial or interrupted travels never corrupt the estimate. Until a direction has been learned, a default travel time is used for the interpolation.

The learned durations persist across restarts and adapt slowly over time (a capped running average), so a door whose timing drifts is tracked rather than frozen.

Remote / wall-button operation

The door can also be operated by a remote or the wall button, which never touch the toggle switch — so those presses are invisible to the integration and are tracked purely from the two contact sensors. Normal opens and closes by any means are handled fully (state, position, and learning all update).

The one thing that can't be seen directly is a mid-travel stop by a remote or wall button (no switch event, and the contacts don't change while the door sits between them). Two guards handle it:

  • Learning guard — a travel that takes more than twice the already-learned time is discarded, so a paused-then-resumed run can't inflate the average.
  • Stuck guard — once a move overruns ~2.5× its expected duration, the door is assumed to have been stopped mid-travel and is reported stopped, with the direction remembered. That hidden stop also advances the opener's own single-button toggle, so remembering the direction keeps the integration in sync: the next press it can see (the HA switch) then reverses correctly — a paused open becomes a close. The sensors remain the source of truth: if the door actually reaches an endpoint, that supersedes the guess.

Entities on the device

  • Cover — the garage door: open / close / stop, is_opening/is_closing, a live position percentage, and best-effort set position (see below).
  • Binary sensor ×2 — Top sensor and Bottom sensor, mirroring the two contacts (diagnostic).
  • Sensor: State — the inferred door state as an enum.
  • Sensor: Position — the door's open percentage (0–100%). A device_class: garage cover is shown as open/closed in the HA frontend and doesn't render the percentage, so the learned position is exposed here as its own value for dashboards and templates. (The cover still carries it as current_position.)
  • Sensor: Learned open time / Learned close time — the learned durations in seconds, with a samples attribute (diagnostic).
  • Sensor: Sensor status — health of the two battery contacts as text: OK, or a warning such as "Top door sensor is not reporting - check its battery".
  • Binary sensor: Sensor problemon (device class problem) whenever a contact isn't reporting or reads an impossible state; the clean trigger for a notification (detail in its message attribute).
  • Button: Press — the raw single-button action: one pulse, whatever the door is doing. Handy when you want the literal wall-button behaviour.

Battery / offline sensor guards

These are battery contact sensors, so the integration is defensive about one of them going quiet:

  • A contact reporting unavailable/unknown (dead battery) is never acted on — it can't move the door state or corrupt the learned timings. The door simply holds its last known state until the contact reports again.
  • The impossible reading top open while bottom closed is treated as a faulty sensor and ignored the same way.
  • Both conditions raise Sensor problem and set Sensor status to a warning, so you can be told the moment a sensor stops reporting.

Example notification automation:

automation:
  - alias: Garage sensor not reporting
    trigger:
      - platform: state
        entity_id: binary_sensor.garage_door_sensor_problem
        to: "on"
        for: "00:05:00"
    action:
      - service: notify.mobile_app_your_phone
        data:
          title: Garage door
          message: "{{ state_attr('binary_sensor.garage_door_sensor_problem', 'message') }}"

Cover control behaviour

Because the opener has a single button, the cover's open/close/stop each issue a single guarded press:

  • Open — pressed unless the door is already open or opening.
  • Close — pressed unless the door is already closed or closing.
  • Stop — pressed only while the door is moving.

Each press pulses the configured switch (on, briefly, then off) so a momentary relay re-arms for the next press.

Set position (best-effort)

The cover also supports set position, so the HA frontend shows and controls the open percentage. Since there's only one button, the position is reached by timing: the door is started in the right direction and the button is pressed again to stop it once the door's interpolated position — derived from the learned travel time — reaches the target. For example, on a door that takes 14 s to open, asking for 50 % from closed stops it about 7 s in.

This is deliberately best-effort: the stop is judged from the moment the sensors confirm the door actually moved (so the press-to-move delay doesn't skew it), but the timing won't be perfectly accurate, and it needs a learned open/close duration to be meaningful (until then it uses a default estimate). Full open (100 %) and full close (0 %) skip the timing and let the opener's own limit switch stop the door.

Setup

Settings → Devices & Services → Add IntegrationGarage Door Monitor, then choose:

  • Top sensor (binary_sensor, on when fully open)
  • Bottom sensor (binary_sensor, on once the door leaves closed)
  • Toggle switch (the switch / input_boolean that operates the opener)

You can change any of these later via the integration's Configure option.

Migrating from the AppDaemon app

Your old AppDaemon config mapped straight across:

AppDaemon arg Integration field
top_sensor Top sensor
lower_sensor Bottom sensor
toggle_switch Toggle switch

The input_select helper and the cover.garage_template template cover are no longer needed — remove them once the integration's own cover entity is in use.

Installation

Copy custom_components/monitorgarage into your Home Assistant config/custom_components directory (or add this repository to HACS as a custom repository), then restart Home Assistant.

About

Home Assistant custom integration to monitor and control a single-button garage door via two contact sensors, with learned open/close position tracking.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages