--- type: concept tags: [parking, device, indicator, radar, camera, aux-output, barrier-not-a-door] sources: [] updated: 2026-06-24 status: settled --- # Button-light indicator (radar × camera disagreement lamp) The entry button has a **12 V light**. It is driven by the host on a **spare relay** of the [[dingtian-relay|Dingtian]] controller as a 3-state indicator that combines the **[[hikvision-radar| radar]]** input with the **camera "car in zone"** signal: | Radar input | Camera (lane entry busy) | Button light | | --- | --- | --- | | detecting | **free** — no car confirmed | **BLINK** (~1 Hz) | | detecting | **busy** — camera confirms a car | **SOLID on** | | clear | — | **OFF** | It is a **disagreement indicator**: the radar sees *something* but the camera hasn't confirmed a real vehicle → blink (attention / "pull forward"); both agree → solid; nothing there → off. ## Signals - **Radar** = the presence input edge on the entry relay (`relays[].presenceInput`, the same edge the [[entry-double-press|one-car-one-ticket]] gate observes — so the lamp and the gate always agree on "a car is here"). - **Camera "car in zone"** = the existing **[[lpr-camera|lane status]]** (`LaneStatusEvent` entry busy/free, from camera vehicle detection). Already advisory; already drives the booth's barrier lights. No new camera plumbing. ## Config A controller-level `config.buttonLight = { relay, blinkOnMs?, blinkOffMs? }` (the operator picks a **spare** relay — not a barrier relay; the setup UI warns if it overlaps one). Blink defaults to 500 ms / 500 ms. ## Implementation `apps/server/src/button-light.ts` — `ButtonLightController` subscribes to `deviceEvents.onInput` (radar) + `onLaneStatus` (camera), computes the target state per controller, and drives the lamp via a **device-agnostic aux-output** capability. - **Aux-output capability.** `AuxOutputDevice { setAux(channel, on) }` on the device interface (the Dingtian driver implements it as a latch). Business logic drives the lamp through this — **never** the driver's barrier methods. - **Barrier-not-a-door is preserved.** The lamp is **not a barrier**, so holding / blinking it on a timer is fine — the [[barrier-not-a-door]] rule forbids timing a *barrier* closed, and barriers still only ever `pulseOpen`. The lamp uses the separate `setAux` latch. - **Fails OFF.** On host loss, shutdown, or a `setAux` error the lamp defaults OFF — a dead lamp is "no hint", never a misleading solid "go". SOLID is only ever held while busy + present is actively true (never latched on through a crash path). - **De-duped.** Only writes when the effective output changes, so the 50 ms input poll doesn't spam the controller over UDP. ## Status Built 2026-06-24 for the first booth (button I1, radar I2, lamp on a spare relay). Covered by `apps/server/src/button-light.test.ts` (the truth table + blink toggling + fail-OFF + de-dupe). Related: [[hikvision-radar]], [[entry-double-press]], [[lpr-camera]], [[dingtian-relay]], [[barrier-not-a-door]].