Zero-dependency Python 3 CLI for checking the status of, opening, and closing Chamberlain / LiftMaster myQ smart garage door openers via the myQ v6/v5 REST API—including full support for myQ's OAuth 2.0 + PKCE + Email MFA login flow.
- Zero third-party dependencies: Uses only the Python 3.10+ standard library.
- Unified
~/.config/myq/State & Config: Stores both user configuration (~/.config/myq/config.json) and cached OAuth tokens (~/.config/myq/tokens.json,0600) in~/.config/myq/, keeping all personal setup, credentials, and tokens completely outside the repository. - Rolling OAuth Token Refresh: Automatically refreshes expired 12-hour access tokens via
grant_type=refresh_token—so Email MFA is only needed on initial login. - Pluggable Credential & MFA Hooks: Fetch your myQ password and 6-digit Email MFA code on demand from any secret manager (
pass, 1Passwordop, Bitwardenbw, macOS Keychain,systemd-creds), custom mail script (otp_cmd), environment variables, or interactive terminal prompt. - Live Door State Tracking: Checks current door state (
OPEN,CLOSED,OPENING,CLOSING,STOPPED), avoids redundant commands when already in the requested state, and polls live state transitions until completion.
- Runtime: Python
3.10+on Linux or macOS (no externalpippackages required). - Account Type: Requires a standard Chamberlain / LiftMaster / Craftsman myQ account (
partner-identity.myq-cloud.com) using email + password authentication (third-party federated logins such as "Sign in with Google/Apple" are not supported). - Supported Devices: Targets myQ garage door openers (
device_family == "garagedoor"or devices reportingstate.door_state). If your account has multiple garage doors,myqtargets the first door by default unless you pass a door name substring or serial number on the command line. - Email MFA: On initial authentication (when
~/.config/myq/tokens.jsondoes not yet exist or./myq loginis run), myQ sends a 6-digit verification code to the account's email address. By default,myqprompts interactively for this code in the terminal. For unattended/headless setups, either run./myq loginonce interactively to seed~/.config/myq/tokens.json(which then self-refreshes viarefresh_token) or configure"otp_cmd"in~/.config/myq/config.json. - Config & Token Location: Defaults to
~/.config/myq/(or$XDG_CONFIG_HOME/myq/), overridable via$MYQ_CONFIGand$MYQ_TOKENS_FILE.
# Check garage door status (default command)
./myq
./myq status
./myq status --json
# Open the garage door (checks state, sends OPEN, and polls until OPEN)
./myq open
# Close the garage door (checks state, sends CLOSE, and polls until CLOSED)
./myq close
# Send open/close without waiting for transition completion
./myq open --no-wait
./myq close --no-wait
# Force a fresh OAuth + MFA login
./myq loginIf you have multiple garage doors on your account, pass a door name substring or serial number as the second argument:
./myq open "Main Garage"
./myq close CG08XXXXXXXXmyq never requires storing plaintext passwords in the repository or passing them on the command line. By default, it reads ~/.config/myq/config.json (overridable via $MYQ_CONFIG) and caches OAuth tokens in ~/.config/myq/tokens.json (0600, overridable via $MYQ_TOKENS_FILE). If no config file is present and myq is run in an interactive terminal, it prompts for your email, password, and the 6-digit emailed verification code.
Create ~/.config/myq/config.json (chmod 700 ~/.config/myq && chmod 600 ~/.config/myq/config.json) with your email and any password_cmd (shell string or argv array) that prints your myQ password to stdout:
1Password CLI (op):
{
"email": "you@example.com",
"password_cmd": ["op", "read", "op://Private/myQ/password"]
}Unix pass:
{
"email": "you@example.com",
"password_cmd": ["pass", "show", "home/myq"]
}Bitwarden CLI (bw):
{
"email": "you@example.com",
"password_cmd": ["bw", "get", "password", "myq"]
}macOS Keychain (security):
{
"email": "you@example.com",
"password_cmd": ["security", "find-generic-password", "-a", "you@example.com", "-s", "myq", "-w"]
}Linux systemd-creds (TPM2-sealed credential):
{
"email": "you@example.com",
"password_cmd": [
"sudo",
"systemd-creds",
"decrypt",
"--name=myq-password.cred",
"/path/to/myq-password.cred",
"-"
]
}On initial login (when no valid refresh_token is cached in ~/.config/myq/tokens.json), myQ emails a 6-digit myQ Verification Code. By default, myq prompts for this code interactively in the terminal.
To automate MFA completely, place a helper script in ~/.config/myq/ and configure "otp_cmd" in ~/.config/myq/config.json (or $MYQ_OTP_CMD / --otp-cmd) with any command that prints the 6-digit code to stdout:
- Before submitting the login form,
myqrecords the Unix timestamp in$MYQ_OTP_SINCEand optionally runs"otp_prepare_cmd"(if configured), passing its stdout in$MYQ_OTP_STATEto"otp_cmd". - Once
/AccountMfa/VerifyOtpis reached,myqexecutes"otp_cmd"and extracts the 6-digit code from stdout.
| Setting | Config Key (~/.config/myq/config.json) |
Environment Variable | CLI Flag |
|---|---|---|---|
| Account Email | "email" or "email_cmd" |
MYQ_EMAIL / MYQ_EMAIL_CMD |
--email |
| Password Hook | "password_cmd" |
MYQ_PASSWORD_CMD |
--password-cmd |
| Password File | "password_file" |
MYQ_PASSWORD_FILE |
--password-file |
| Password Value | — | MYQ_PASSWORD |
(interactive prompt if omitted) |
| OTP Prepare Hook | "otp_prepare_cmd" |
MYQ_OTP_PREPARE_CMD |
— |
| OTP Fetch Hook | "otp_cmd" |
MYQ_OTP_CMD |
--otp-cmd |
| Manual OTP Prompt | — | — | --manual-otp |
| Token Cache Path | — | MYQ_TOKENS_FILE |
(defaults to ~/.config/myq/tokens.json) |
When run under a systemd service with LoadCredentialEncrypted=myq-password.cred:..., myq automatically reads $CREDENTIALS_DIRECTORY/myq-password.cred.
- Direct Login Entry (
/Account/Login): Cloudflare enforces a Managed Challenge (HTTP 429 cf-mitigated: challenge) onGET https://partner-identity.myq-cloud.com/connect/authorize. Because/connect/authorizeonly redirects unauthenticated clients to/Account/Login?ReturnUrl=/connect/authorize/callback?...,myq.pynavigates directly to/Account/Login?ReturnUrl=...(with%20 URI encoding), receiving all required session and anti-forgery cookies (__cf_bm,myqidstid,AntiForgery.MyQ.Identity) withHTTP 200. - Email MFA (
/AccountMfa/VerifyOtp): After posting credentials to/Account/LoginWithEmail, IdentityServer redirects to/AccountMfa/VerifyOtpand emails a 6-digit security code, whichmyq.pyretrieves viaotp_cmd(or interactive prompt) and submits. - Token Exchange (
/connect/token): Exchanging the single-usecom.myqops://android?code=...authorization code atPOST https://partner-identity.myq-cloud.com/connect/tokenrequires afirebase-appcheck-tokenJWT header with claims matching project628160337454(myq-production) to pass the edge gateway's401.122check. Subsequent token refreshes (grant_type=refresh_token) do not require App Check and issue rolling 12-hour tokens.