-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Browser / Breeze app ─┐
│ HTTPS (via reverse proxy) or HTTP on the LAN
ac-diag.zsh ──────────┤
▼
┌──────────────────────────────┐
│ uvicorn → meow_ac.app:app │ (one worker + in-process scheduler)
│ ├── /api/auth/* pairing │
│ ├── /api/units* control │
│ ├── /api/programs favourites / schedules / curves
│ └── / static web panel
│ │ msmart-ng │
│ ▼ │
│ AC device objects (cached) │
└────────────┬─────────────────┘
│ TCP :6444 per unit
Midea AC units on the LAN
Three components share exactly one contract — the /api/* endpoints — and are
otherwise fully decoupled. Deleting either client leaves the API and the other
client working.
Small layers, assembled by an app factory. create_app() is the one place
everything is wired: it builds a config store, hands it to the device manager and
the authenticator, builds the routers from those, and mounts the static panel.
Nothing reaches for global state.
| Layer | Responsibility |
|---|---|
| Settings | Env-driven runtime config, read once. Feature flags for public exposure: docs, security headers, trusted hosts, behind-proxy, LAN-only enrolment, credential lifetime. |
| Config |
config.json as a typed store — the shared API key and the unit list, including per-unit V3 token/key. Admin-managed, shared with the pairing tool. |
| Security | Device-pairing auth. An Authenticator protocol with an API-key implementation and a device-credential implementation, composed so a request must satisfy both; plus the enrolment handshake, the credential store, and rate limiting. |
| Devices | Connection lifecycle and cache, LAN discovery, the wire schema with bounds, and one shared apply_to_unit helper. |
| Programs | Favourites, schedules and curves, their store, and the background scheduler that fires them. |
| API | Router factories, each taking its collaborators as arguments. |
Connections are lazy and cached per unit. The first request for a unit after a restart pays connect + authenticate + capability probe + refresh; later ones reuse the object. Every state read and every control write is still a live LAN round-trip — there is no push or subscribe in the protocol — so per-call latency (~0.7 s on the maintainer's units) is inherent, not a bug. Each unit has its own lock, so concurrent requests to the same unit serialise instead of racing.
The scheduler shares the control path. Schedules and curves call the very same
apply_to_unit as POST /control, which is why a scheduled change is
indistinguishable from a manual one — including in the SSE stream. One uvicorn
worker means exactly one scheduler, so nothing fires twice.
The seams are deliberate: add work through them rather than widening a module.
-
A new endpoint → write a
build_*router factory that takes its collaborators as arguments, and include it increate_app(). -
A new auth factor → implement the
Authenticatorprotocol and pass it tocreate_app(). Endpoints depend on "the authenticator" and do not change. -
A new client → speak the three
/api/*endpoint groups. The panel and the Android app are both nothing more than HTTP clients; the Flutter client plan is a third.
Read REST API for the wire contract and Control schema for the field values every component shares.
The repository holds the server package, the static panel, the CLIs, the packaging for every platform, and the container definitions. Build instructions: Building and releasing.
Breeze Core · Breeze for Android · Packages · AGPL-3.0
Start here
Install it
Use it
Reference
Run it safely
Develop and port