Port "Mein Zimmer" - the Home Assistant room panel

The second of the web front-end's three pages. Lights get a toggle, five brightness
steps and six colours; shutters get four presets, up/stop/down and a window that
reflects the position Home Assistant reports. Its own violet hue rather than the
player's teal, so the two pages read as siblings rather than one bleeding into the
other - the same reason room.css has its own tokens.

The backend keeps Home Assistant's URL and token and relays the calls, so this client
learns only which entities exist, exactly as the browser does. A 404 from GET /api/ha
means Home Assistant is not configured, which is a normal answer: the tab simply does
not appear.

Polled, not subscribed, and only while the page is on screen. HA's own auth-and-
subscribe websocket is more machinery than a room panel needs, and a lamp's state is
not worth a request every 2.5 seconds when nobody is looking at it - this device has a
music player to stay out of the way of. A tap patches the entity locally and records
when; a poll that was already in flight when the patch landed is dropped for that
entity rather than putting the lamp back to "off" until the next one catches up. That
reconciliation is the one non-obvious thing in useHomeAssistant.ts and it is carried
over for the same reason.

Nothing on a shutter card animates locally. A real cover reports its own movement and
position, and a client-side guess at where it will end up is precisely what would
fight with that - the design mockup animated it because it had no backend to ask.

Two additions beyond a straight port, both because a kiosk has no pointer:

CTRL+TAB cycles the pages. The web's tab rail is pointer-only, which sits badly with a
front-end whose README claims every screen is reachable from the keyboard. Plain TAB
was already the group cycle, and CTRL+TAB is the idiom for the outer one everywhere
else.

--page opens directly on a page, which a kiosk may well want and which is the only way
in with neither keyboard nor pointer.

Verified against the real Home Assistant: the shutter reported open and the lamp off,
and both drew that way. Nothing was switched - those entities are hardware.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 01:02:18 +02:00
parent ec9d617ad8
commit 794126bfdb
9 changed files with 1462 additions and 19 deletions

View File

@@ -6,8 +6,8 @@ kiosk, and the things making it sluggish are browser-shaped: a compositor decidi
gets its own layer, a `requestAnimationFrame` loop that repaints the viewport, and a
JPEG decode per album card per cold start.
Only the music player is here. The typing game (`Tippen`), the room-lights page, parent
mode and the IR-remote assignment are all still web-only.
The music player and the room panel ("Mein Zimmer"). The typing game (`Tippen`), parent
mode and the IR-remote assignment are still web-only.
## Running it
@@ -17,6 +17,7 @@ Start the backend, then:
nix-shell # cargo, the GL/X11/Wayland libs, and Nunito
cargo run # windowed, FemtoVG
cargo run -- --server http://musicdolphin:8080
cargo run -- --page room # open on the room panel rather than the player
```
Checks:
@@ -126,6 +127,7 @@ one websocket connection and a trickle of covers.
| `src/library.rs` | The search index and the browse hierarchy. Port of `web/src/lib/search.ts`. |
| `src/keymap.rs` | The keyboard model, as a pure function. Port of `web/src/lib/keyboard.ts`. |
| `src/covers.rs` | The thumbnail cache, and the generated stand-in art. |
| `src/ha.rs` | Home Assistant through the backend's proxy, and the shutter/colour conversions. Port of `web/src/hooks/useHomeAssistant.ts` and `lib/shutter.ts`. |
| `src/oklch.rs`, `src/format.rs` | Colour conversion; durations. |
| `src/main.rs` | Wiring: worker channels in, Slint models out. |
| `ui/*.slint` | Layout only. Nothing here formats a number or picks a word. |
@@ -154,9 +156,31 @@ The same map as the web UI.
| `SHIFT+ENTER` | open the track list |
| `ESC` | back out one layer at a time |
| `/` | back to browsing |
| `CTRL+TAB` | cycle Musik / Mein Zimmer / Tippen |
Everything is clickable too.
`CTRL+TAB` is the one addition to the web front-end's map. Its tab rail is reachable
only with a pointer, which sits badly with a device whose stated design is that every
screen can be reached from the keyboard. Plain `TAB` was already the group cycle.
## Mein Zimmer
The Home Assistant room panel, in its own violet rather than the player's teal so the
two read as siblings. Lights get a toggle, five brightness steps and six colours;
shutters get four presets, up/stop/down and a window that reflects the reported
position. Nothing is animated locally - a real cover reports its own movement, and
guessing where it will end up is what would fight with that.
The whole page is a poll, not a subscription, and only while it is on screen: Home
Assistant's own auth-and-subscribe websocket is more machinery than this needs, and a
lamp is not worth a request every 2.5 s when nobody is looking. A tap patches the
entity locally and remembers when, so the poll already in flight cannot undo it - the
same reconciliation `useHomeAssistant.ts` documents, and for the same reason.
The tab appears only when the backend answers `GET /api/ha` with a config; a 404 there
means Home Assistant is not set up, which is a normal answer rather than an error.
The special keys are named in `ui/app.slint`, where the `Key.*` constants live, and
reach Rust as `"escape"`, `"left"`, `"enter"` and so on - so `src/keymap.rs` is testable
without a window and without hardcoding keysym values.