Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

130 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

myq-garage-worker

Deploy Worker GitHub release License: MIT

A Cloudflare Worker that integrates myQ notification emails and displays a clean status dashboard. State and history are stored in Cloudflare D1.

Garage status dashboard

For a step-by-step guide on how to configure MyQ, Cloudflare, and email forwarding, please see the Setup Guide.

Disclaimer: This is an unofficial, community-maintained project. It is not affiliated with, endorsed by, or sponsored by Chamberlain Group, Inc. or its myQ brand. "myQ" is a trademark of its respective owner. This software is provided as-is, without warranty.

Architecture

This Worker is one deployment with:

  1. Email Routing Handler (email): Parses myQ notification emails and writes door state/events to Cloudflare D1.
  2. Scheduled Handler (scheduled): Cron checks for left-open doors and fires webhooks.
  3. HTTP API (fetch): JSON under /api/* plus Home Assistant GET /devices.
  4. Static frontend (Workers Static Assets): Vite-built / (status) and /admin (alerts/simulate), same hostname as the API.

Tech Stack

Security (operator responsibility)

Browser dashboard access is not enforced inside this Worker. Put the Worker behind Cloudflare Zero Trust / Access with an Allow policy for humans. Access should cover /, /admin, and /api/*. If Access is missing or misconfigured, anyone who knows the URL can view the dashboard and call admin APIs.

For Home Assistant (HACS), create a second Access application whose public destination is your-hostname/devices with a Bypass (Everyone) policy. Worker-destination Access apps (one-click Workers Access) have no path field, so a Bypass on that app would unprotect the whole Worker. Cloudflare lets a more specific public destination take precedence. Set the Worker API_KEY secret — after the bypass, that key is the only guard on /devices. The integration sends Authorization: Bearer YOUR_API_KEY. Without API_KEY, /devices returns 401.

Dashboard POST /api/alert-config and POST /api/test-alert include a soft Worker-side rate limit (UX only; fails open). For abuse prevention, configure Cloudflare Rate Limiting / WAF on those routes. See SECURITY.md.

Environment Variables / Configuration

The environment variable GARAGE_DOORS must be provided at deployment time or in the Cloudflare dashboard. We do not hardcode this in wrangler.jsonc to allow dynamic CI/CD deployments.

Variable Name Description
GARAGE_DOORS A JSON object mapping the exact names of your garage doors (from the myQ app/emails) to stable door ids (D1 doors.id).
API_KEY Secret required for GET /devices (and deprecated GET /?json=true). Send Authorization: Bearer or x-api-key. Not used for the browser dashboard or /api/dashboard (use Access).
ALLOWED_EMAIL_TO (Recommended) Exact envelope recipient (RCPT TO) that must match for inbound myQ mail. Rejects other aliases if set.
ALLOWED_FORWARD_FROM (Optional) Envelope MAIL FROM when myQ mail is forwarded (e.g. your Gmail address). Header From must still be notification@myq.com. Prefer direct myQ → Cloudflare when possible.
STALE_AFTER_HOURS Hours without a door event before the status UI marks data as stale (default 48, set in wrangler.jsonc vars).

Example configuration:

{
  "Garage Door Left": "garage-left",
  "Garage Door Right": "garage-right"
}

You also need a D1 database bound as GARAGE_DB (see wrangler.jsonc). Run npm run db:migrations:remote after creating the database.

Operational health (no secrets) is available at GET /health. The status page at / auto-refreshes every 45 seconds and warns when door data is older than STALE_AFTER_HOURS.

Setup and Deployment

We provide an interactive wizard to configure your garage doors and deploy the worker:

  1. Install dependencies:
    npm install
  2. Run the interactive setup wizard:
    npm run setup

For a detailed step-by-step guide including Cloudflare Email Routing and myQ configuration, see the Setup Guide.

Local Development

  1. Install dependencies:

    npm install
  2. Build the frontend once (or use npm run frontend:dev in a second terminal with Vite’s proxy to wrangler):

    npm run frontend:build
    npm run dev
  3. Open http://localhost:8787 for status and /admin for alerts/simulate.

Testing Live Deployments

To ensure your dashboard UI updates correctly without having to open/close your physical garage doors, we have built-in testing functionality:

Option 1: Web UI Simulator

Open your Access-protected worker URL, go to Admin, choose a configured door, and pick an action (opened, closed, stopped).

Option 2: Alerts on Admin

On Admin, configure a left-open webhook (stored in D1, used by the cron job every 15 minutes):

  • Webhook URL — HTTPS only (ntfy, Pushover, Apprise, etc.; use a secret topic URL). Private/localhost destinations are rejected.
  • Threshold (minutes) — how long a door must be open before alerting
  • HTTP methodPOST sends a JSON body; GET appends title, message, and other fields as query params (handy for ntfy.sh)
  • Save — persists settings for the scheduled cron job
  • Test webhook — sends a test notification immediately using the form values

Alerts fire once when the threshold is crossed for an open session (optional reminder interval can be configured via API). Closing the door clears the latch.

Option 3: CLI Script

You can use the included CLI script from your terminal to ping the live (or local) worker:

# node scripts/test-live.js <URL> <DOOR_NAME> <ACTION>
node scripts/test-live.js https://my-worker.workers.dev "Garage Door Left" opened

Simulator and alert endpoints bypass the inbound email envelope-sender check but execute the same storage/alert logic as production.

Formatting & Linting

We maintain a strict code quality process:

  • Linting check: npm run lint
  • Formatting code: npm run format
  • Type checking: npm run typecheck

Continuous Integration / Deployment (CI/CD)

Deployments are automated through GitHub Actions when code is pushed to the main branch.

To set this up, add the following Repository Secrets in your GitHub repository (Settings -> Secrets and variables -> Actions):

  • CLOUDFLARE_API_TOKEN: Your Cloudflare API Token (scoped to Edit Workers).
  • CLOUDFLARE_ACCOUNT_ID: Your Cloudflare Account ID (e.g. 0123456789abcdef0123456789abcdef).

Add the following Repository Variables (not secrets):

  • D1_DATABASE_ID: Your Cloudflare D1 database id for myq-garage / GARAGE_DB (injected into wrangler.jsonc during CI deploy when set).

Add the following Repository Secrets:

  • GARAGE_DOORS: JSON object mapping door names to door ids (passed to the worker at deploy time).

Integrations & Automations

This worker can automatically notify external services when a garage door is left open, or simply push status updates to external services like Home Assistant.

Webhook Alerts (Left Open)

The worker checks every 15 minutes (via Cloudflare Cron Triggers) for doors left open too long and sends a webhook notification.

  1. Open the dashboard Alerts tab and configure your webhook URL, threshold, and HTTP method, then click Save.
  2. Ensure you have the [triggers] configuration in wrangler.jsonc to fire the cron job.

Alert settings are stored in D1 (not environment variables). Protect the dashboard with Cloudflare Access so only you can change webhook settings.

POST sends a JSON body:

{
  "title": "Garage Door Alert",
  "message": "Garage Door Left has been open for 1 hr 15 mins.",
  "door": "Garage Door Left",
  "state": "OPEN",
  "durationMs": 4500000,
  "durationText": "1 hr 15 mins"
}

GET appends the same fields as query parameters on your webhook URL (useful for ntfy.sh topics).

Apprise Integration

If you host an Apprise API container, you can fan-out notifications to Discord, Slack, SMS, Pushbullet, etc. Set your webhook URL to your Apprise topic endpoint, for example https://apprise.mydomain.com/notify/garage, and choose POST.

ntfy.sh Integration

ntfy.sh works well with GET (query params) or POST (JSON). Use a secret topic name in your URL — anyone with that URL can publish to your topic, independent of this worker.

Home Assistant Integration

For Home Assistant, use the companion ha-myq-garage custom integration (available via HACS). It polls GET /devices on this worker and creates cover entities with config-flow setup.

Browser dashboard: protect the Worker with Cloudflare Access (Allow policy). Do not rely on an in-app unlock page.

Home Assistant setup:

  1. Deploy this worker with GARAGE_DOORS configured at deploy time (via npm run setup or CI). Without it, /devices returns an empty array.
  2. Set a strong API_KEY secret on the worker (required for /devices; this is the sole auth after Access bypass).
  3. Create a second Access application for your-hostname/devices with Bypass (Everyone). See SETUP.md for the API recipe. Do not add Bypass to the Worker-destination Access app.
  4. Install ha-myq-garage via HACS.
  5. Add the integration in Home Assistant (Settings → Devices & Services → Add Integration → MyQ Garage):
    • API URL: https://your-worker.workers.dev
    • API key: your API_KEY value (sent as Authorization: Bearer …)

The integration calls GET /devices, which returns:

[
  { "id": "garage-left", "name": "Garage Door Left", "status": "open" },
  { "id": "garage-right", "name": "Garage Door Right", "status": "closed" }
]

id is the stable door id from GARAGE_DOORS. status is lowercase open or closed. Doors in STOPPED, UNKNOWN, or with no state yet are omitted from the response.

Browser dashboard: GET /api/dashboard (Access-protected, no API_KEY) returns door status and recent events for the static UI.

Deprecated: GET /?json=true (requires API_KEY) still returns { "doors": [...], "history": [...] } for older REST setups. Prefer GET /devices or GET /api/dashboard. Do not create an Access Bypass for /.

{
  "doors": [
    {
      "name": "Garage Door Left",
      "state": { "value": "OPEN", "createdAt": "2023-10-01T12:00:00Z" },
      "durationMs": 3600000,
      "durationText": "1 hr"
    }
  ],
  "history": [ ... ]
}

Contributing

See CONTRIBUTING.md for development setup and pull request guidelines.

License

MIT — see LICENSE.

About

Cloudflare Worker that parses myQ garage door notification emails and serves a live status dashboard

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages