- kiosk-block border-left now uses the same st-vacant/booked/heating_up/
occupied/cooling_down colour scheme as the Dashboard's ZoneCard
- Layout tweak (user, 2026-08-26): a room only ever has one AC unit, so
it's pinned top-right; lower half is a row of up to 3 TRVs with setpoint
stacked below the icon rather than beside it (more legible at kiosk touch
size than the dashboard's compact rad-row)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Correction to the kiosk grid design (user, 2026-08-26): tiles now match the
Dashboard's ZoneCard -- one tile per room showing its TRV and AC together
(reusing ZoneCard's own rad-row/ac-block/fan-bar CSS), not a separate tile
per device. Selection/bulk-edit now operates on selected zones, gathering
every live device across them for KioskBulkPanel's type-aware sections.
Single-zone tap opens a modal listing that zone's device panels, mirroring
ZoneDetailModal's device loop minus the staff-only schedule/override UI.
- KioskZoneBlock.tsx replaces KioskDeviceBlock.tsx
- KioskZonePanel.tsx: new, one control panel per live device in a zone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Full-screen /kiosk route, separate from the staff SPA: dedicated long-lived
kiosk login (own password, hashed, mints its own hnf_session cookie locally
via CENTRAL_AUTH_SECRET -- no central auth changes needed), scrollable grid
of small mode/setpoint blocks per live-controllable device (shelly_trv,
mhi_mqtt, daikin), tap-to-edit modal reusing the existing per-type control
panels, and a type-aware bulk multiselect (separate TRV/AC sections, only
applied to matching selected devices, TRV commands staggered).
- backend/src/routes/kiosk.js: public POST /api/kiosk/login
- backend/src/routes/settings.js: admin set/check kiosk password (bcrypt)
- backend/src/routes/config.js: exclude/protect kiosk_password_hash
- frontend/src/pages/Kiosk.tsx + components/Kiosk*: the kiosk UI
- reuses GET /api/status for the combined device list -- no new status
endpoint needed since the kiosk token now also carries the 'view' cap
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
activity_log only ever recorded state transitions/overrides/sync/errors —
useful for "what did the app decide" but not "what was the room actually
doing" when a guest/staff complaint comes in after the fact.
Backend:
- New device_readings time-series table (room_temp, target_temp, mode,
power_state, fan_speed, valve_position, heating_active), throttled to
~1 row/device/minute via recordReading() in db.js regardless of how
often a caller fires — MQTT pushes can be far chattier than that.
- Wired into every place zone_devices' live-status cache already gets
updated: ac-poller.js, daikin-poller.js, and mqtt.js's TRV
(handleStatusBlob/handleInfo) and native-MQTT MHI (writeMhiState)
handlers.
- 4-day retention by default (config key history_retention_days),
pruned every 6h by the new history-pruner.js.
- GET /api/devices/:id/history?hours=96 to read it back.
Frontend:
- DeviceHistoryModal: room temp vs setpoint line chart, plus a
valve-position chart (TRVs) or fan-speed step chart (AC), plus a
diffed mode/power/fan change log — 24h/4d/7d range picker. Uses
recharts, matching the convention already established in
utilities/wages/reports.
- Graph icon on each device in both ZoneDetailModal and the Devices
page opens it.
Side effect: frontend/package-lock.json was missing vite-plugin-pwa's
whole dependency tree even though package.json already declared it —
pre-existing drift, reconciled by this npm install (unrelated to the
recharts addition, just came along with it).
Backend:
- New valve_position column, populated from shellies/<id>/info alongside
wifi_rssi (was already a column, never actually written to).
- New GET/POST /api/devices/:id/trv-status and /trv-control routes, mirroring
the mhi-status/mhi-control and daikin-status/daikin-control pattern. Control
goes through the existing setTargetWithRetry() reliability pipeline.
Frontend:
- New TrvControlPanel.tsx: +/- setpoint control debounced 1s after the last
click before firing a command (these are battery/deep-sleep valves going
through a real retry/backoff pipeline, not a cheap register write like MHI/
Daikin), plus room temp, valve position %, wifi signal, and a live "time
since last update" readout. Wired into ZoneDetailModal for shelly_trv rows.
- ZoneCard dashboard tile: radiator icon now just shows red when heating_active
(valve open) / grey when idle, with only the target setpoint alongside it
(dropped the room-temp half, redundant with the zone-level temp line above).
- Remove status badge from ZoneCard, keep left-border indicator only (with hover title for accessibility)
- Rename heating_up label from 'Heating Up' to 'Preparing'
- Recolour room states: booked=green, occupied=blue, cooling_down=purple (matches NewBook's status colours); vacant stays grey, heating_up stays amber
- Decouple rad-icon.heating from --st-occupied (now blue) to --st-heating-up (amber) so an actively-heating radiator icon still reads warm
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds actual room-temperature capture for TRVs over MQTT and a background
Modbus poller for AC mode/fan/lock/room-temp, so the dashboard tile can show
real setpoint-vs-actual readings, an AC status block, and up to 3 radiator
icons per zone instead of just the setpoint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports the state machine, retry/backoff, and guest-override detection from
the retired homeassistant-newbook-heating-component, without depending on
Home Assistant. Backend (Fastify/pg) + frontend (React/Vite/TS) following
standard stack conventions; LXC 128 (127 was already taken by utilities).
MHI/Midea/Daikin/boiler drivers and the shared MQTT broker (LXC 104) are
later phases/infra, not included here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>