devices: stub-access driver (bench-test flows without a relay)

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.
This commit is contained in:
2026-06-16 12:50:05 +02:00
parent 68d61f2d99
commit 5705098054
4 changed files with 49 additions and 0 deletions
@@ -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<void> {}
async disconnect(): Promise<void> {}
async healthCheck(): Promise<DeviceHealth> {
return { status: "ready", detail: "stub (no real barrier)" };
}
async pulseOpen(doorId: number): Promise<void> {
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),
};
+3
View File
@@ -3,6 +3,7 @@
import { registry } from "../registry.js"; import { registry } from "../registry.js";
import { dingtianDriver } from "./access-dingtian.js"; import { dingtianDriver } from "./access-dingtian.js";
import { stubAccessDriver } from "./access-stub.js";
import { dahuaDriver, hikvisionDriver } from "./camera.js"; import { dahuaDriver, hikvisionDriver } from "./camera.js";
import { rongtaDriver } from "./printer-rongta.js"; import { rongtaDriver } from "./printer-rongta.js";
import { geeQrReaderDriver, tcpipReaderDriver, wiegandReaderDriver } from "./reader.js"; import { geeQrReaderDriver, tcpipReaderDriver, wiegandReaderDriver } from "./reader.js";
@@ -14,6 +15,7 @@ export function registerBuiltinDrivers(): void {
if (registered) return; if (registered) return;
registered = true; registered = true;
registry.register(dingtianDriver); registry.register(dingtianDriver);
registry.register(stubAccessDriver);
registry.register(wiegandReaderDriver); registry.register(wiegandReaderDriver);
registry.register(tcpipReaderDriver); registry.register(tcpipReaderDriver);
registry.register(geeQrReaderDriver); registry.register(geeQrReaderDriver);
@@ -24,6 +26,7 @@ export function registerBuiltinDrivers(): void {
export { export {
dingtianDriver, dingtianDriver,
stubAccessDriver,
wiegandReaderDriver, wiegandReaderDriver,
tcpipReaderDriver, tcpipReaderDriver,
geeQrReaderDriver, geeQrReaderDriver,
+1
View File
@@ -11,6 +11,7 @@ export { setDeviceLogSink, type DeviceLogSink } from "./drivers/common.js";
export { export {
registerBuiltinDrivers, registerBuiltinDrivers,
dingtianDriver, dingtianDriver,
stubAccessDriver,
wiegandReaderDriver, wiegandReaderDriver,
tcpipReaderDriver, tcpipReaderDriver,
geeQrReaderDriver, geeQrReaderDriver,
+10
View File
@@ -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 → matching permit → status:1 + open; re-scan → exit; unknown card → status:0; unassigned serial →
status:0. Full build 5/5. status:0. Full build 5/5.
- Updated [[gee-qr-er80]] (assignment as-built). - 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.