Adds the workshop/office assistant and the plumbing several other features were waiting on. The through-line: every new capability that could act on its own proposes instead, and says out loud when it does not know something. New service — workshop/ Project notebook (workshop.db) plus a never-pruned knowledge store (workshop-knowledge.db): standing workflow instructions by activity, keyword facts, durable project learnings, and the household's ONE hardware inventory. GET /context returns everything applying right now in one call, so the assistant is told the standing considerations rather than reminded of them. Two databases because they have different lifetimes: rebuilding the project store must not take the note about how you solder with it. Hardware statuses distinguish reserved (still on the shelf) from in_use (installed and working) — "can I use this right now" has different answers for the two, and naming a project on an in_use item never silently demotes it. Gitea repos with append-only history: commit/push/branch yes, unattended; force-push/rebase/amend/reset/filter-repo never, enforced server-side by branch protection rather than only by this code refusing. When history genuinely must be scrubbed, /scrub-request prints the commands for a human to run — the manual step is the safety mechanism. Fleet scripts: one monitoring-agent script per kind of machine, fetched by each endpoint's fleet-bootstrap timer. Remote code execution by design, so the constraints are the design — upload is a draft, publishing is separate, scripts live in SQLite rather than on the writable share, every version is kept, and the endpoint verifies the checksum and reports pass or fail. Slots exist for the ESP32s and network appliances that cannot run a script at all, holding the CheckMK-server-side config instead. Infrastructure health opnsense becomes a LIST of firewalls, each named, keyed by name rather than index. CheckMK joins it. Both are polled by workshop (always-on) and read by digest-engine, so the digest can say "critical since Tuesday" instead of quoting a six-hour-old snapshot. Three states, because "I could not ask" is not "nothing is wrong". pantry-vision All four stock movements are camera-driven; stock counts individual units and folds brand-free via Grocy product groups. Door-sensor-triggered appliance cameras record sightings as hints with timestamps, never as stock — a camera at a door cannot tell in from out. identity Per-person colour and settable profile picture, assigned to avoid collisions between people sharing an initial, on the 2-bit-per-channel lattice a colour Pebble renders natively. render/ — shared, vendored, dependency-free media-visualiser: two-tier by necessity, since most endpoints have no local audio; the synthetic tier says on screen that it is not an analysis. floorplan-3d: canvas 2D rather than three.js — the scene is prisms on a plane, which an isometric projection draws in ~200 lines, predictably on weak panels, with the frontend still at zero dependencies. Config and fleet plumbing Rooms are one vocabulary (an HA area_id) from CoreSystemConfig through the builders to suggested_area. Keycloak and FreeIPA are coupled as one decision with USR_HA_ group naming, declaration-only for now and validated as such. Immich alongside the photo share, read-only. Thin clients get the full media-key set for a wireless remote. Docs: fridge-item-location, workshop-assistant, rooms-and-endpoints, endpoint-surfaces, pebble-presence-watchface. Testing is stubbed suites and headless unit checks only — no real Grocy, camera, vision model, CheckMK, Gitea, Samba or browser has been involved. The CheckMK API shape and Gitea's branch-protection payload are written from documentation and have version-sensitive field names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FanS1vyE2gLhGkqKq6HtYj |
||
|---|---|---|
| .. | ||
| floorplan-3d | ||
| media-visualiser | ||
| README.md | ||
README.md
render/ — shared frontend surfaces
Two pages that every endpoint can show, kept here rather than inside one component
because they belong to no single service: the now-playing screen reads Home Assistant,
the floorplan reads identity, and both are shown on thin clients, touch panels, the
kitchen and door panels alike.
Same rules as digest-canvas-sdk and canvas-sdk: vendored, dependency-free, no
build step, config from ?query=params so nothing here holds a secret. Copy the
directory into a host's image; do not serve it from one place, or a kiosk that cannot
reach the container host shows nothing.
| What | Reads | |
|---|---|---|
media-visualiser/ |
now-playing: circular spectrum, album-art colours, lyrics | HA media_player state |
floorplan-3d/ |
who is in which room, extruded | identity GET /floorplan/presence |
media-visualiser
nowplaying.html?ha=http://ha:8123&token=<HA token>&entity=media_player.living_room
Two tiers, and the distinction is load-bearing. CAVA reads an audio stream; most endpoints do not have one — a kitchen panel showing what the living room is playing has no audio to analyse and never will. So:
- reactive — audio is local. Real FFT via WebAudio (
attachAudio(el)), or levels pushed fromcava -rby the host's agent (pushLevels([...])). - synthetic — everywhere else. The ring breathes from track position and tempo.
The synthetic tier says so on screen ("visual rhythm — not an audio analysis") and
sets data-tier on the canvas. That matters: the moment somebody believes it is a
spectrum, every bass drop it misses becomes a bug report.
Palette extraction rejects near-greys and near-blacks before ranking (album art is full of both, and a naive palette off a dark cover is four indistinguishable greys), then lifts each colour until it clears the background — the step that gets skipped, and why so many art-coloured visualisers are invisible on dark covers.
Lyrics: synced LRC highlights and scrolls; plain text is shown but never auto-scrolled at a guessed rate, which is wrong within ten seconds and stays wrong. Most tracks have none, so the layout treats absent as the normal case.
floorplan-3d
floorplan.html?api=http://identity:8097&token=<identity token>[&level=<id>]
Canvas 2D, not three.js — a change from what docs/endpoint-surfaces.md planned,
made while building it. The scene is prisms on a plane: no lighting model, no textures,
no model import. An isometric projection with painter's-algorithm sorting draws exactly
that in ~200 lines, runs predictably on the small panels (where a WebGL context is much
less predictable), and keeps the frontend at zero dependencies. If this ever needs real
lighting or an imported model, three.js becomes right and this becomes the fallback.
Inherited from the Pebble watchapp deliberately, because they render the same payload and divergence would mean one of them is lying:
- Occupied rooms lit, empty rooms dark — carried in lightness, not hue, because colour already means who.
- A third state, dashed, for rooms HA never reports on. Drawing "no data" as "empty" is a quiet lie.
- Occupants as colour + initial, upgraded to their photo where there is room, with the colour ring kept either way — it is what ties this marker to the same person on the watch and in the admin panel.
unplacedpeople get a visible shelf. They are who you are most often looking for.
Drag to orbit; pitch is clamped and there is no free-fly, because an unconstrained camera on a wall panel is something people knock askew and cannot get back.
The caveat no rendering fixes: this is only as right as room-level presence, which has never been measured in this house. A beautiful 3D house with everybody on the unplaced shelf is a beautiful picture of nothing — test that first with the admin panel's floorplan tab, Live ticked.