Add real UHPPOTE access driver via official uhppoted lib

Researched Node options for the UHPPOTE controller and chose the official
`uhppoted` npm package (MIT, actively maintained, single trivial dep). Its API
covers the full design: openDoor, getStatus, the event-log set (getEvent,
getEventIndex, setEventIndex, recordSpecialEvents), and setListener/listen.
Rejected alternatives: raw-dgram DIY, node-red-contrib-uhppoted, Go sidecar.

- packages/devices: add uhppoted@0.9.0; new `uhppote` access driver implementing
  AccessControlDevice (pulseOpen -> openDoor intent-only; healthCheck/getDoorStatus
  -> getStatus). Registered in the catalog alongside zkteco/esp32-relay.
- Local ambient types (uhppoted ships none); CJS interop via default-import +
  destructure under NodeNext.
- Config fields: controller serial (required), optional host/protocol (udp|tcp),
  doors, timeout.

Security/safety unchanged: unauthenticated UDP -> isolated VLAN assumed; relay
is intent-only ("a barrier is not a door").

wiki: record the library choice on uhppote-controller + log entry.

Verified: builds; `uhppote` shows in the catalog with correct fields;
instantiates and degrades to "offline" gracefully without hardware (on-VLAN
test pending).
This commit is contained in:
2026-06-14 08:12:27 +02:00
parent 72ba4099ea
commit 7438c0bdc2
7 changed files with 226 additions and 2 deletions
+66
View File
@@ -0,0 +1,66 @@
// Minimal ambient types for the `uhppoted` CommonJS module (no bundled types).
// Only the surface we use; extend as we adopt more of the API.
// Upstream: https://github.com/uhppoted/uhppoted-lib-nodejs
declare module "uhppoted" {
export class Config {
constructor(
name?: string,
bindAddr?: string,
broadcastAddr?: string,
listenAddr?: string,
timeout?: number,
controllers?: unknown[],
debug?: boolean,
);
}
/** Either a bare controller serial, or an addressable descriptor. */
export type Controller =
| number
| { id: number; address?: string; protocol?: "udp" | "tcp" };
export interface Ctx {
config: Config;
locale?: string;
}
export function openDoor(
ctx: Ctx,
controller: Controller,
door: number,
): Promise<{ deviceId: number; opened: boolean }>;
export function getStatus(
ctx: Ctx,
controller: Controller,
): Promise<Record<string, unknown>>;
export function getEvent(
ctx: Ctx,
controller: Controller,
index: number,
): Promise<Record<string, unknown>>;
export function getEventIndex(
ctx: Ctx,
controller: Controller,
): Promise<{ deviceId: number; index: number }>;
export function setListener(
ctx: Ctx,
controller: Controller,
address: string,
port: number,
): Promise<unknown>;
// CommonJS default export (module.exports = { ... }). Destructure from this.
const uhppoted: {
Config: typeof Config;
openDoor: typeof openDoor;
getStatus: typeof getStatus;
getEvent: typeof getEvent;
getEventIndex: typeof getEventIndex;
setListener: typeof setListener;
};
export default uhppoted;
}