# 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-agent` — **Show 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`](../../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

```sh
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.
