SmartestHome/hosts/door-panel
Amir Alexander Abdelbaki a948f4b375 Workshop assistant, fleet monitoring, infra health, and shared endpoint surfaces
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
2026-08-10 14:54:37 +02:00
..
agent Workshop assistant, fleet monitoring, infra health, and shared endpoint surfaces 2026-08-10 14:54:37 +02:00
configs Add identity registration system and Phase 18 door panel 2026-07-30 13:40:04 +02:00
live-build/config Add identity registration system and Phase 18 door panel 2026-07-30 13:40:04 +02:00
README.md Add tools/ and CoreSystemConfig.json — one source of truth for every build 2026-07-31 13:22:25 +02:00

README.md

Sway door panel

Phase 18 of docs/project-plan.md. Builds a Debian 12 live ISO for a wall panel by the door/wardrobe: weather and what to wear on the way out, who's home, groceries running low, and — the household's actual registration point — "register me as <name>" by voice.

Structurally this is ../kitchen-display/'s twin, not a new shape: one Sway workspace, one Chromium kiosk window, a thin agent that only ever switches which page is showing. The differences are what it shows by default (identity's dashboard.html instead of pantry-vision's scan/inventory/recipes) and that its microphone is expected to actually be used, not an edge-case opt-in.

The actual logic does not live in this directory../../identity/ owns person/presence data and the registration flow; ../../pantry-vision/ owns the "running low" data. This host is deliberately thin: Sway, one kiosk window, and door-panel-agent, which only controls which screen is showing — the same split every other kiosk host in this project already uses.

Compositor Sway, one workspace, one Chromium kiosk window
Autologin greetd, straight into /usr/local/bin/kiosk-session
Touch input Native Wayland wl_touch, same assumption as ../touch-panel//../kitchen-display/
Default screen identity's dashboard.html — weather + clothing suggestion, who's home, groceries running low
Registration identity's register.html — voice ("register me as <name>") or the touchscreen form
Voice wyoming-satellite + openWakeWord — opt-in like every other host's mic, but this device's actual purpose, so the build script defaults it to on
Remote control (HA/LLM) door-panel-agentShow home / Show registration buttons only
Remote control (human) SSH only — no wayvnc, same scope decision as ../touch-panel/

Hardware

No specific unit has been chosen. What's assumed: a touchscreen (native wl_touch, same caveat as every other touch host in this project) mounted by the door or wardrobe, with a microphone and speaker for voice registration/Assist playback. Unlike ../kitchen-display/, no camera is strictly required — the photo captured during registration (identity's profile-picture feature) is a nice- to-have audit artifact, not load-bearing, since identity resolution here is BLE/IRK- based, not camera-based. See docs/project-plan.md §1.16 for the (unverified, non-specific) hardware line item.

Before you build

Deploy identity/identity-web first (ENABLE_IDENTITY in tools/setup-container-host.sh) — this image builds and boots fine without it, but the dashboard will show connection errors until it exists. pantry-vision is optional (only "Running low" needs it). Then edit the # CONFIGURATION block at the top of tools/build-door-panel-iso.sh:

Variable What to put in it
MQTT_BROKER_HOST LAN IP of the container host running Mosquitto
IDENTITY_WEB_URL / IDENTITY_URL / IDENTITY_TOKEN identity's static-serving and API URLs; token must match identity/identity.env's own
PANTRY_VISION_URL / PANTRY_VISION_TOKEN Optional — only "Running low" needs these; must match pantry-vision/pantry-vision.env's own token
ENABLE_VOICE_SATELLITE Defaults to true here (unlike every other host's identical flag) — this device's whole point is voice registration, but it still needs a real mic on the specific unit before flashing it
KIOSK_USERNAME / IMAGE_HOSTNAME / DOOR_PANEL_NAME Per-device identity
SSH_AUTHORIZED_KEY Optional — password auth is disabled and there is no wayvnc

Build

sudo -E tools/build-door-panel-iso.sh

Same directory-split convention as every other host: configs/ and agent/ are human-edited and git-tracked; live-build/config/includes.chroot/ is generated, gitignored, never hand-edited.

Why one workspace, two kiosk destinations, not three

../touch-panel/ juggles three real apps (Spotify, Home Assistant, a browser) and needs a persistent touch dock to switch between them. This device only ever shows one of two pages — the everyday dashboard, or the registration form — and switches between them rarely (on an MQTT command or a voice trigger), so a dock would be overhead with nothing to navigate day-to-day. configs/sway/home-kiosk and configs/sway/identity-kiosk are separate scripts on separate Chromium profiles, same shape as ../kitchen-display/'s pantry-kiosk/identity-kiosk pair — see that host's README for why both scripts kill any Chromium instance before launching (this device also has only one workspace).

Home Assistant entities

door-panel-agent publishes two buttons on connect: Show home, Show registration — each kills and relaunches the kiosk Chromium window at the corresponding page. That's the entire MQTT surface. Reading presence/weather/ groceries, or registering a person, is a separate path — Home Assistant/the LLM calling identity's or pantry-vision's own published APIs directly, not through this agent. Voice registration in particular never touches this agent at all — see ../../identity/README.md's worked HA intent-script example.

Security boundary

Same shape and reasoning as every other host's agent in this project: door_panel_agent/mqtt_discovery.py is the entire inbound MQTT control surface of this machine — LLM tool call → HA service call → MQTT → this agent, no HTTP listener, no websocket, no exposed Sway IPC socket, no VNC. identity and pantry-vision are separate, deliberately published services with their own bearer-token boundaries — see their own READMEs' "A real network listener, unlike admin-canvas" sections for why that trust model is different on purpose.

Manual verification still outstanding

None of this has been run on hardware. In rough order — on top of everything already flagged as unverified in ../../identity/README.md (TRUSTED_ENTITY_PREFIXES above all):

  1. The ISO builds at all.
  2. greetd lands in Sway with no login prompt.
  3. Touch input as native wl_touch — same open item as every other touch host.
  4. door-panel-agent connects to Mosquitto and the device appears in HA.
  5. Register via the touchscreen form (Show registration) first, before trying voice — this exercises the whole identity path without needing HA's intent-script wiring set up. See identity/README.md's worked example for that next step.
  6. Voice registration end-to-end: wake word → "register me as <name>" → a spoken confirmation. This is the number of moving pieces (wyoming-satellite, HA's Assist pipeline, the custom sentence/intent script, identity's /register) that has the most to go wrong and the least individual testing so far.
  7. The dashboard's clothing suggestion (identity/frontend/dashboard.js's clothingSuggestion()) is a plain lookup table, untuned against real weather payloads or real household clothing preferences — reasonable starting thresholds, not measured.
  8. Switching between home-kiosk and identity-kiosk actually replaces the window rather than leaving two Chromium instances up — same untested assumption as ../kitchen-display/'s identical pair.
  9. Idle-blank timeout (20 minutes, configs/sway/config) and touch-resume — untested.