feat(anpr): subscriber-entry bridge + admin disable toggle
CI / check (push) Failing after 15s

Wire the lane camera's vehicle event into the gated subscription flow: on a
vehicle/active push from an opt-in (config.anpr) camera, AnprBridge pulls a fresh
snapshot, runs ANPR, applies a stricter entry confidence floor, debounces, and —
matching the plate to a subscription BEFORE emitting — emits a kind:"plate" read.
The existing ReadDispatcher -> SubscriptionFlow then signs the entry/exit and opens
the barrier. A plate is never the sole authority: it routes through the same gate
(active/window/blocklist/car-count) as any credential. Fail-soft, fire-and-forget,
subscriber-only by construction. Field-verified end to end (plate AA504LX opened the
entry barrier and appended a signed vehicle_entry).

Add an admin master switch (site_config.anpr_entry_enabled, default ON) in Site
Settings that disables ONLY the barrier-driving bridge; advisory snapshot-ANPR and
lane busy/free are unaffected. Read live per event, so toggling takes effect with no
restart. Migration 0013 (additive ALTER ADD COLUMN, default 1).

- New: apps/server/src/anpr-entry.ts (AnprBridge) + tests (9)
- hikvision-alarm.ts hands vehicle detections to the bridge (fire-and-forget) + wiring tests (3)
- server.ts reorders the read flows above the hik-alarm registration
- snapshot.ts exports buildCamera for reuse
- env: VISION_ENTRY_MIN_CONFIDENCE (0.85), ANPR_DEBOUNCE_MS (12000)
- site route + SiteSettings checkbox + i18n (sq/en parity)
- wiki: lane-presence-and-anpr-entry / lpr-camera / index / log -> BUILT

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-22 19:49:18 +02:00
parent 411572511d
commit 65328b8c11
19 changed files with 579 additions and 23 deletions
+4 -3
View File
@@ -84,9 +84,10 @@ Center**, then **Alarm Settings → Alarm Server**, makes the camera **HTTP-POST
or open anything. A plate read is **advisory, never the sole reason** a barrier opens
([[append-only-event-chain]], [[opencv-anpr-service]]). Two consumers were since designed off this
same vehicle event — see **[[lane-presence-and-anpr-entry]]**: (a) BUILT — advisory lane busy/free
booth lights; (b) PLANNED — the ANPR "bridge" that snapshots → ANPR → emits a `kind:"plate"` read
for a SUBSCRIBER match through the existing gated flow (a small `apps/server` handler, not a
service). If the camera ever emits its own `<plateNumber>` we'd use it directly; this `DS-2CD1043G2`
booth lights; (b) BUILT (2026-06-22) — the ANPR "bridge" (`anpr-entry.ts`) that snapshots → ANPR →
emits a `kind:"plate"` read for a SUBSCRIBER match through the existing gated flow (a small
`apps/server` handler class, not a service). If the camera ever emits its own `<plateNumber>` we'd
use it directly; this `DS-2CD1043G2`
does not, so the server pulls the frame and hands it to the [[opencv-anpr-service|vision service]].
### Gotchas learned the hard way (2026-06-22 field session)