From 5705098054e6660323ff5f21fe60381433f9e0ab Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Tue, 16 Jun 2026 12:50:05 +0200 Subject: [PATCH] devices: stub-access driver (bench-test flows without a relay) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A live QR scan reached the app but rejected: 'reader not on an access-equipped lane' — the dispatcher requires an access device on the reader's lane. Add a no-op stub-access driver (access category, no config) whose pulseOpen only logs and does no device I/O, so the QR->permit->accept flow (incl. the beep) can be tested without the Dingtian relay connected. Not for production; registered in the catalog. --- packages/devices/src/drivers/access-stub.ts | 35 +++++++++++++++++++++ packages/devices/src/drivers/index.ts | 3 ++ packages/devices/src/index.ts | 1 + wiki/log.md | 10 ++++++ 4 files changed, 49 insertions(+) create mode 100644 packages/devices/src/drivers/access-stub.ts diff --git a/packages/devices/src/drivers/access-stub.ts b/packages/devices/src/drivers/access-stub.ts new file mode 100644 index 0000000..adc19fb --- /dev/null +++ b/packages/devices/src/drivers/access-stub.ts @@ -0,0 +1,35 @@ +import type { AccessControlDevice, DeviceHealth } from "../interfaces.js"; +import type { AccessDriver, DeviceConfig } from "../registry.js"; +import { stubLog } from "./common.js"; + +// Stub access controller — a no-op barrier for BENCH TESTING the entry/exit/permit +// flows without real relay hardware. `pulseOpen` just logs "intent to open"; it +// performs no device I/O, so it can stand in on a lane while the real +// [[dingtian-relay]] isn't connected. NOT for production. See first-run-setup.md. + +class StubAccess implements AccessControlDevice { + readonly driverId = "stub-access"; + constructor(_config: DeviceConfig) {} + async connect(): Promise {} + async disconnect(): Promise {} + async healthCheck(): Promise { + return { status: "ready", detail: "stub (no real barrier)" }; + } + async pulseOpen(doorId: number): Promise { + stubLog(this.driverId, `pulseOpen door ${doorId} (stub — no relay fired)`); + } + async getDoorStatus(): Promise<"open" | "closed"> { + return "closed"; + } +} + +export const stubAccessDriver: AccessDriver = { + id: "stub-access", + category: "access", + label: "Stub barrier (bench testing — no relay)", + description: + "A no-op access controller for testing the flows without hardware. pulseOpen only logs; no relay is fired. Not for production.", + transports: ["tcp-ip"], + configFields: [], + create: (c) => new StubAccess(c), +}; diff --git a/packages/devices/src/drivers/index.ts b/packages/devices/src/drivers/index.ts index d1400ae..44df414 100644 --- a/packages/devices/src/drivers/index.ts +++ b/packages/devices/src/drivers/index.ts @@ -3,6 +3,7 @@ import { registry } from "../registry.js"; import { dingtianDriver } from "./access-dingtian.js"; +import { stubAccessDriver } from "./access-stub.js"; import { dahuaDriver, hikvisionDriver } from "./camera.js"; import { rongtaDriver } from "./printer-rongta.js"; import { geeQrReaderDriver, tcpipReaderDriver, wiegandReaderDriver } from "./reader.js"; @@ -14,6 +15,7 @@ export function registerBuiltinDrivers(): void { if (registered) return; registered = true; registry.register(dingtianDriver); + registry.register(stubAccessDriver); registry.register(wiegandReaderDriver); registry.register(tcpipReaderDriver); registry.register(geeQrReaderDriver); @@ -24,6 +26,7 @@ export function registerBuiltinDrivers(): void { export { dingtianDriver, + stubAccessDriver, wiegandReaderDriver, tcpipReaderDriver, geeQrReaderDriver, diff --git a/packages/devices/src/index.ts b/packages/devices/src/index.ts index 7e07852..2a2067f 100644 --- a/packages/devices/src/index.ts +++ b/packages/devices/src/index.ts @@ -11,6 +11,7 @@ export { setDeviceLogSink, type DeviceLogSink } from "./drivers/common.js"; export { registerBuiltinDrivers, dingtianDriver, + stubAccessDriver, wiegandReaderDriver, tcpipReaderDriver, geeQrReaderDriver, diff --git a/wiki/log.md b/wiki/log.md index 2baef3e..ca1b2f2 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -681,3 +681,13 @@ guarantee. Recorded in [[dingtian-relay]] (new Hardening section). matching permit → status:1 + open; re-scan → exit; unknown card → status:0; unassigned serial → status:0. Full build 5/5. - Updated [[gee-qr-er80]] (assignment as-built). + +## [2026-06-16] feature | stub-access driver (bench-test the flows without a relay) +- Live QR scan reached the real app (.jsp, serial resolved) but rejected: "reader not on an + access-equipped lane" — lane 1 had the reader but no access device. The dispatcher requires an + access device on the same lane. +- Added a no-op **`stub-access`** driver (access category, no config): `pulseOpen` just logs, no + device I/O — stands in on a lane to test QR→permit→accept (incl. the beep) without the + [[dingtian-relay]] connected. NOT for production. Registered in the catalog. +- To get a live accept: assign Stub barrier to the reader's lane + a permit whose QR = the scanned + code → status:1 (2-beep) + logged pulseOpen.