gatectl is a small, dependency-free Python CLI for inspecting and operating
LiftMaster/MyQ residential gates and garage doors. It implements the current
OAuth/PKCE login flow, email or SMS MFA, refreshable sessions, account and
device discovery, state reads, and guarded open/close commands.
Warning
gatectl controls physical access equipment through an undocumented MyQ API.
Keep the opening clear, retain a working official app or wall control, and do
not use this project for unattended safety-critical automation.
- discovers MyQ accounts, hubs, gates, and garage doors;
- reads exact-device state with serials redacted by default;
- stores OAuth tokens and observations outside the repository with mode
0600; - opens or closes one exact account/device match;
- refuses offline devices, ambiguous states, unsupported device families, and actions MyQ has not marked as safe for unattended operation;
- waits for MyQ to report the requested terminal state.
Python 3.11 or newer is required. There are no runtime package dependencies.
cd /path/to/private/home-config
./bin/bootstrap-ctls gatectlThe private home-config bootstrap is the canonical installer: it populates the
real inventory, calls this repository's stable script/install contract, and
creates /usr/local/bin/gatectl backed by an isolated system environment under
/usr/local/lib/home-config/ctls. For development without installation, prefix
commands with PYTHONPATH=src python3 -m gatectl.
Copy the public example to the private runtime location and replace the sample
names with exact values returned by gatectl inspect:
sudo install -d -m 700 /usr/local/config/gatectl
sudo install -m 600 config/targets.example.json /usr/local/config/gatectl/targets.json{
"account": "Demo Home",
"devices": ["Driveway Gate", "Garage Door"]
}Use GATECTL_CONFIG=/path/to/targets.json or the global
--config /path/to/targets.json option to select another file. Real account,
device, host, and deployment data belongs in a private configuration repository,
not in a public fork of gatectl.
Check the current sign-in form, then start one login:
gatectl doctor
gatectl login --email you@example.com --mfa emailThe password prompt does not echo. Enter the six-digit email or SMS code when
asked. The password and MFA code are never stored; the resulting refreshable
session is written to /usr/local/config/gatectl/tokens.json with mode 0600.
If MyQ returns a browser-verification challenge, stop and retry later instead of repeatedly starting new logins. See authentication for the proven flow and recovery guidance.
gatectl inspect
gatectl status
gatectl status "Garage Door"inspect does not require a target config. status always scopes matches to
the configured account. Add --json for structured output or --show-serials
only when a serial is genuinely needed for diagnosis.
gatectl open "Garage Door"
gatectl close "Garage Door"Interactive commands require typing the requested action. Deliberate
noninteractive callers may pass --yes. By default, open waits up to 45
seconds and close up to 60 seconds for the reported state. Override this with
--wait SECONDS; --wait 0 returns after MyQ accepts the command without
claiming that movement completed.
gatectl close "Garage Door" --yes --wait 90The successful live validation sequence was closed → opening → open followed
by open → closing → closed. Closing may remain open briefly while the
opener emits its warning signal. See operations for the
full safety model and state behavior.
| Data | Default path | Git policy |
|---|---|---|
| Target names | /usr/local/config/gatectl/targets.json |
Private config repo only |
| OAuth tokens | /usr/local/config/gatectl/tokens.json |
Private config/recovery seed only |
| Last observation | ~/.local/state/gatectl/last-observation.json |
Never commit |
Passwords and MFA codes are held only for the active login request. Serial numbers are redacted in normal output and saved observations.
MyQ does not publish or support this residential API. Endpoints, client
metadata, App Check behavior, MFA forms, rate limits, and Cloudflare challenges
can change without notice. gatectl intentionally has no toggle or arbitrary
write primitive; its only device writes are the guarded open and close
endpoints.
See protocol notes, troubleshooting, and the roadmap for more detail.
gatectl— MyQ gate and garage-door status with guarded open/close.poolctl— Pentair ScreenLogic status, cleaner, and delay control.hottubctl— Sundance SmartTub temperature and freshness inspection.switchctl— named local switch status and guarded power control.
Current and future *ctl tools favor small commands, private configuration,
readable output, safe JSON, guarded writes, post-write readback, a repo-owned
script/install, and explicit uncertainty.
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
.venv/bin/ruff format --check src tests
.venv/bin/ruff check src tests
.venv/bin/detect-secrets scan --baseline .secrets.baseline
PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -vTests use mocked HTTP responses and never contact or operate a real device.
gatectl is released under the MIT License. The OAuth form parsing,
current client metadata, and endpoint work build on Vadim Belov's MIT-licensed
bvdcode/myq-home-assistant.
See NOTICE.md.
