You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.3 KiB
6.3 KiB
Plan
Status: ready_for_implement
Goal: implement the scope defined in ROADMAP.md — a phone-first webpage, served by a small Go backend in Docker, that shows whether the laundry is done based on the last 3 one-minute power samples from tele/tasmota_washingmashine_C21183/SENSOR.
Scope
- Go module
mqtt_washingmachine(Go 1.26), standard library plusgithub.com/eclipse/paho.mqtt.golangfor MQTT. - Layout:
main.go: config from env, wiring, graceful shutdown.internal/status/: pure logic (sample window, status evaluation).internal/sensor/: Tasmota payload parsing, MQTT subscription, per-minute sampler.internal/web/: HTTP handlers and embedded static page (go:embed).
- Docker packaging and documentation.
- Out of scope: persistence, TLS/auth, history beyond 3 samples, multiple devices.
Design decisions (from ROADMAP.md)
- Threshold: 3.00 W. A sample is "idle" only if
power < 3.0;>= 3.0is "active". - Sampling: the MQTT handler stores only the latest
ENERGY.Powervalue and its receive time. A ticker fires everySAMPLE_INTERVAL(default 60s). At each tick: if a message was received since the previous tick, record its power as a sample; otherwise record a missing sample. Then clear the "received" flag. - Window: ring of the last 3 samples (oldest first). Each sample is
{Power float64, Valid bool}. - Status evaluation (first match wins):
- fewer than 3 samples recorded since start ->
waiting, "Waiting for data...", grey - any of the last 3 samples missing ->
incomplete, "Incomplete data", grey - any sample
>= 3.0->running, "Laundry not yet done", red - otherwise (all 3
< 3.0) ->done, "Laundry done", default text color
- fewer than 3 samples recorded since start ->
- JSON contract for
GET /api/status:{"state":"running","message":"Laundry not yet done","readings":[{"power":47.7,"valid":true},{"power":0,"valid":false}]}readingshas 0–3 entries, oldest first. The page renders the display text, color class, and47.70W/--formatting. - Config env vars:
MQTT_BROKER(defaulttcp://192.168.1.12:1883),MQTT_TOPIC(defaulttele/tasmota_washingmashine_C21183/SENSOR),MQTT_USERNAME,MQTT_PASSWORD(optional),HTTP_ADDR(default:8080),SAMPLE_INTERVAL(Go duration, default60s). - MQTT client uses auto-reconnect and re-subscribes in the
OnConnecthandler.
Acceptance Criteria
GET /returns a white-background page with a viewport meta tag, a very large status text, and the last 3 readings below it in normal-size black text formatted like47.70W; missing readings show--.- The page fetches
/api/statuson load and every 60 s; no horizontal scroll at 360 px width. - Status states and colors match the table in
ROADMAP.mdexactly, including>= 3.00 W= not done. - The backend collects samples continuously with no page open; a page load immediately reflects the current state.
- A minute with no MQTT message produces a missing sample and
incompletestate. - Malformed payloads or payloads without
ENERGY.Powerare ignored (logged), without crashing or changing the latest value. docker buildsucceeds and the container runs with env-var configuration; README documents setup.go fmt ./...,go vet ./...,go test ./...all pass.
Tasks
T-001 — Status logic and Tasmota payload parsing
Tests first, then implementation.
internal/status/status_test.go,internal/status/status.go: window of 3,Evaluate, boundary tests (2.99 -> idle, 3.00 -> active, 0 W all three -> done, mixed -> running, gap -> incomplete, <3 samples -> waiting).internal/sensor/payload_test.go,internal/sensor/payload.go: parse the sample payload from the roadmap (Power139.60 etc.), missingENERGY, missingPower, invalid JSON.go.mod(module init, no external deps yet in this task).- Files to change:
go.mod,internal/status/*,internal/sensor/payload*.go. - Documentation: package doc comments; no README change yet.
T-002 — Sampler, MQTT subscription, HTTP server and page
Tests first, then implementation.
internal/sensor/sampler_test.go,internal/sensor/sampler.go: latest-value store plus ticker-driven sampling into the window, with an injectable tick channel/clock for deterministic tests (message between ticks -> valid sample, none -> missing).internal/sensor/mqtt.go: paho client, auto-reconnect, subscribe inOnConnect, feeds payloads to the parser then the sampler (thin glue, not unit-tested against a real broker).internal/web/handler_test.go,internal/web/handler.go,internal/web/static/index.html:/serves the embedded page,/api/statusreturns the JSON contract (test withhttptest, including state/message/readings per state). Page styling: white background,font-sizeabout 3rem+ bold for status viaclamp(), readings in black at normal size, red/grey classes,meta viewport, 60 ssetIntervalfetch, graceful display if the fetch fails.main.go: env config with defaults, wiring, signal-based shutdown.go.mod,go.sum: addgithub.com/eclipse/paho.mqtt.golang.- Documentation: code comments; README deferred to T-003.
T-003 — Docker packaging and README
Dockerfile(multi-stage: golang builder withCGO_ENABLED=0, finalscratchordistroless/staticimage,EXPOSE 8080).docker-compose.ymlexample with the env vars from this plan..dockerignore.README.md: replace the project TODOs (Overview, Getting Started) with what it does, status rules, env var table,docker compose upinstructions, the TasmotaTelePeriodnote (10 s, any value works since sampling is per minute), and limitations (in-memory state, LAN only, no auth). Keep the existing AI Workflow section untouched. English only.- Files to change:
Dockerfile,docker-compose.yml,.dockerignore,README.md. - Validation extra:
docker build .if Docker is available; otherwise note it in the handoff.
Validation
go fmt ./...go vet ./...go test ./...- Manual check after T-002: run with
HTTP_ADDR=:8080, publish test payloads withmosquitto_pub(if available), open/at a phone-width viewport.
Commit subjects (suggested)
- T-001:
feat(status): evaluate laundry state from the last three power samples - T-002:
feat(web): show live laundry status page fed by MQTT power readings - T-003:
feat(docker): package laundry status service with Docker and README