feat(booth): blink the Entry/Exit lights on radar presence (mirror relay 3)
The on-screen Hyrje/Dalje barrier lights were 2-state (green=free / red=busy)
off the camera lane-status only — they couldn't show the radar-only "detected,
not yet confirmed" state that makes the physical button lamp (relay 3) blink.
Now they mirror the lamp's 3-state rule per lane:
radar present + camera not busy → BLINK green↔red (~1 Hz)
camera busy → SOLID red
otherwise → SOLID green
End-to-end:
- LanePresence (lane-presence.ts): subscribes to deviceEvents.onInput, resolves
each presence edge to its lane via the new direction-agnostic presenceLaneOf()
(device-resolve.ts) — entry AND exit, unlike the entry-gated relayForPresence
the one-car-one-ticket gate uses — and emits a lane-presence {entry,exit} bus
event on change. Wired in server.ts (start + onClose).
- WS forwards it (hello snapshot + push) into live-store.radar.
- BarrierLight (BoothScreen.tsx) is now 3-state; blinks via the .lane-blink
keyframe (index.css), which holds solid-red under prefers-reduced-motion.
Same input + same rule as the lamp, so the screen and the post never disagree.
A new test (lane-presence.test.ts) caught a real bug: the first cut reused
relayForPresence, so the EXIT lane never resolved (it's entry-gated) and never
blinked — presenceLaneOf fixes it. Covers entry/exit independence, de-dupe
across several radars on one lane, and ignoring non-presence inputs.
Full workspace build/lint/test green (185 server tests). Updated the
button-light-indicator wiki page ("On-screen twin").
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -78,6 +78,22 @@ aux-output** capability.
|
||||
the setup UI takes effect on the **next radar edge**, not after a server restart. (The first cut
|
||||
loaded the map once at boot, so a just-saved lamp silently did nothing until restart.)
|
||||
|
||||
## On-screen twin — the booth barrier lights
|
||||
|
||||
The booth's **Hyrje / Dalje (Entry / Exit) indicators** mirror the physical lamp with the SAME
|
||||
3-state rule, per lane: radar-present + camera-free → **blink green↔red** (~1 Hz); camera-busy →
|
||||
**solid red**; else **solid green**. So the operator sees the same "detected, not yet confirmed →
|
||||
confirmed → clear" story on screen as the lamp shows on the post.
|
||||
|
||||
The radar half is sourced by a small server tracker, **`LanePresence` (`lane-presence.ts`)**, that
|
||||
subscribes to `deviceEvents.onInput` and resolves each presence edge to its lane via
|
||||
**`presenceLaneOf`** (`device-resolve.ts`) — direction-agnostic (entry **and** exit), unlike the
|
||||
entry-gated `relayForPresence` the one-car-one-ticket gate uses. It emits a `lane-presence`
|
||||
`{entry,exit}` bus event on change; the WS forwards it (hello snapshot + push) into the booth's
|
||||
`live-store.radar`, and `BarrierLight` (`BoothScreen.tsx`) blinks via the `.lane-blink` keyframe
|
||||
(`index.css`, holds solid-red under `prefers-reduced-motion`). The camera half is the existing
|
||||
[[lpr-camera|lane status]]. Same inputs, same rule as the lamp, so screen and post never disagree.
|
||||
|
||||
## Status
|
||||
|
||||
Built 2026-06-24 for the first booth (button I1, radar I2, lamp on a spare relay); the serialized-send
|
||||
|
||||
Reference in New Issue
Block a user