# Kiosks (wall displays) Full-screen web kiosks on the two Nest compute nodes, each driving its attached display (DP/HDMI) via Chromium. Run as Kubernetes pods on the msp001 cluster (the Nest site). Infra/build/troubleshooting: see fritzlab skill `msp001.md` + the `fritzlab/kiosk` repo. | Node | Pod (ns `kiosk`) | Output | Showing | |---|---|---|---| | host101 | `kiosk-host101` | card0-DP-1 (4K) | kiosk.vino.network | | host102 | `kiosk-host102` | card0-DP-1 (4K) | kiosk.vino.network | Each node drives one screen; multi-screen would need host100's GT 730 (see KIOSK-DESIGN.md). Design target = **3840×2160 (4K UHD), devicePixelRatio 1, 24-bit color, landscape-primary**. Both displays render at native 4K; the Chromium kiosk has no toolbar/taskbar, so viewport == screen == avail.screen == 3840×2160 (no CSS px scaling — 1 CSS px = 1 device px). Layout for the kiosk page should look right at full 4K landscape. (Confirmed on-screen 2026-06-01.) The default page both screens show is `https://kiosk.vino.network` — an editorial wall-art homepage: a **live iOS-Weather-style animated sky** + live local-time clock + current weather from `api.open-meteo.com`. The sky is driven by the Open-Meteo `weather_code` (+ `wind_speed`) + `sunrise`/`sunset`: sun (clear/partly days) and moon (clear/partly nights) **arc across the sky by real time-of-day**, with a warm dawn/dusk horizon glow. Conditions rendered: clear, partly, cloudy, fog, drizzle, rain, **sleet/freezing-rain (ice)**, snow (**blizzard/blowing-snow when wind ≥24 mph**), thunderstorm (**hail on codes 96/99**), plus stars on clear nights and lightning in storms. The **moon mirrors its actual phase** (lit fraction + waxing/waning terminator, computed locally from the date — NOT an extra API call). Weather fetches on load then every 10m, with **fast retry-with-backoff (15s→5m) while failing** so a fetch that fails at pod start doesn't sit on "Weather unavailable" for the full 10m (fixed 2026-06-04; live weather scene 2026-06-04). A frozen clock/stale weather on one screen = that node's Chromium (or the whole node) is wedged, NOT a page bug — the display holds the last-good frame; recover by graceful pod restart (and see fritzlab `msp001.md` if the node itself is unreachable). A **live** clock but "Weather unavailable" + a stale day/night scene (night shown in daytime) is the opposite case — NOT Chromium: the pod can't reach `api.open-meteo.com`, so `wx()` fails and `SUN` (sunrise/sunset) freezes at its last-good values → wrong palette. Root cause is usually msp001 pod egress/DNS loss from a UDM BGP wipe (now auto-healed) — see fritzlab `msp001.md` . Confirm with `kubectl --context msp001 exec -n kiosk -- curl -sm10 https://api.open-meteo.com/v1/forecast?...` from the pod. Source repo: `websites/kiosk.vino.network` (clone at `~/code/git/code.fritzlab.net/websites/kiosk.vino.network/`), content in `html/`, served from Garage S3 via the standard site-publish flow (fritzlab `gitops.md` ). Location + house name are **query params on `KIOSK_URL`**, so one page serves any household: `?zip=&name=`. `name` sets the eyebrow + tab title; `zip` is geocoded once at load (Zippopotam.us) → lat/lon + "City, State" place line, and Open-Meteo runs `timezone=auto` so the clock follows that zip's timezone. No params → Hawks Nest / Minnetrista 55331 / `America/Chicago` defaults (bad zip falls back to these too). To point a screen at a different house, set `KIOSK_URL=https://kiosk.vino.network/?zip=...&name=...` per pod via GitOps (see for the apps-repo edit + hard-refresh). **"Update the kiosk page" = edit the page content**, NOT the KIOSK_URL: edit `html/index.html` (or `404.html`) in that repo, commit + push to main → the `Publish` Gitea Action (site-publish) syncs to the bucket in ~30s. Verify with `curl -s https://kiosk.vino.network/`. **How screens actually pick up a new deploy:** the Chromium kiosk fetches the URL ONCE at pod start and never re-polls on its own (`entrypoint.sh` only re-fetches if cage/chromium exits or the pod restarts) — `Cache-Control: must-revalidate` alone does NOT update a running screen. So the PAGE must self-reload. `index.html` polls its own ETag every 5m and `location.reload()`s only when it changes (no periodic flashing). Any new kiosk page MUST carry that ETag-poll snippet, else the screens stay on the old content until a pod restart. This changes WHICH URL a screen loads (different site), distinct from updating the kiosk page content above. The displayed URL is the `KIOSK_URL` env per pod. **GitOps (only reliable way):** edit `KIOSK_URL` in `fritzlab/apps/msp001/kiosk/kiosk/manifests/deployments.yaml` (block `kiosk-host101` or `kiosk-host102`), commit + push to `apps` main, then force the sync now instead of waiting ~3min for the poll: `kubectl --context msp001 -n argocd annotate application kiosk --overwrite argocd.argoproj.io/refresh=hard`. Pod recreates on the new page. **Do NOT use `kubectl set env` for this:** ArgoCD selfHeal reverts it to the *stale* git target (the value from the last commit it polled, not your new one) within seconds — the pod recreates but on the old URL. Push to git + hard-refresh instead. (Verified 2026-06-01.) `kubectl --context msp001 get pods -n kiosk` (want 1/1 Running). On-screen page check needs a screenshot via Chromium CDP (localhost-only) — see fritzlab `msp001.md` . Blank-screen causes: (1) cage logs `Swapchain for output ... failed test` on repeat → dirty DRM/GBM state from an UNCLEAN prior exit; (2) `getty@tty1` re-took DRM master (re-mask on the node); (3) page down (Chromium holds last-good frame). **Restart kiosk pods GRACEFULLY — never `--force`/`--grace-period=0`.** A hard kill stops cage from dropping DRM master, leaving the next cage with a dirty swapchain → black. Use `kubectl --context msp001 delete pod -n kiosk -l node=host101` (or `rollout restart deploy -n kiosk`). The image self-heals a wedged swapchain in-pod (entrypoint kills+relaunches cage after 5 swapchain failures) and a `tcpSocket:9222` liveness probe restarts a dead Chromium — but a clean shutdown is still the first line of defense. Recovery for an already-black screen = one graceful delete. (Diagnosed + hardened 2026-06-01.) Want a dashboard, photo slideshow, or Home Assistant wall panel instead of a website — just point `KIOSK_URL` at it (e.g. `https://home.vino.network`). [[home-assistant]] for HA URLs.