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

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 plus github.com/eclipse/paho.mqtt.golang for 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.0 is "active".
  • Sampling: the MQTT handler stores only the latest ENERGY.Power value and its receive time. A ticker fires every SAMPLE_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):
    1. fewer than 3 samples recorded since start -> waiting, "Waiting for data...", grey
    2. any of the last 3 samples missing -> incomplete, "Incomplete data", grey
    3. any sample >= 3.0 -> running, "Laundry not yet done", red
    4. otherwise (all 3 < 3.0) -> done, "Laundry done", default text color
  • JSON contract for GET /api/status:
    {"state":"running","message":"Laundry not yet done","readings":[{"power":47.7,"valid":true},{"power":0,"valid":false}]}
    

    readings has 0–3 entries, oldest first. The page renders the display text, color class, and 47.70W / -- formatting.

  • Config env vars: MQTT_BROKER (default tcp://192.168.1.12:1883), MQTT_TOPIC (default tele/tasmota_washingmashine_C21183/SENSOR), MQTT_USERNAME, MQTT_PASSWORD (optional), HTTP_ADDR (default :8080), SAMPLE_INTERVAL (Go duration, default 60s).
  • MQTT client uses auto-reconnect and re-subscribes in the OnConnect handler.

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 like 47.70W; missing readings show --.
  • The page fetches /api/status on load and every 60 s; no horizontal scroll at 360 px width.
  • Status states and colors match the table in ROADMAP.md exactly, 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 incomplete state.
  • Malformed payloads or payloads without ENERGY.Power are ignored (logged), without crashing or changing the latest value.
  • docker build succeeds 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 (Power 139.60 etc.), missing ENERGY, missing Power, 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 in OnConnect, 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/status returns the JSON contract (test with httptest, including state/message/readings per state). Page styling: white background, font-size about 3rem+ bold for status via clamp(), readings in black at normal size, red/grey classes, meta viewport, 60 s setInterval fetch, graceful display if the fetch fails.
  • main.go: env config with defaults, wiring, signal-based shutdown.
  • go.mod, go.sum: add github.com/eclipse/paho.mqtt.golang.
  • Documentation: code comments; README deferred to T-003.

T-003 — Docker packaging and README

  • Dockerfile (multi-stage: golang builder with CGO_ENABLED=0, final scratch or distroless/static image, EXPOSE 8080).
  • docker-compose.yml example 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 up instructions, the Tasmota TelePeriod note (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 with mosquitto_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