77606da2c9
Brought up the real UHPPOTE controller (serial 225088491, fw 09120) end to end and recorded a procurement-level blocker. Verified on hardware: - discovery (LAN scan), host-commanded openDoor on doors 1 & 2 (physically actuated; reason="remote open door"), and live button capture (reason="push button ok"). Driver/networking fixes (packages/devices/src/drivers/access-uhppote.ts): - broadcast to subnet-directed address (lib doesn't enable SO_BROADCAST for the global 255.255.255.255 -> EACCES); - Config broadcast must match the target's subnet for unicast reply routing (fixes the health-check timeout: 5s -> 24ms ready); - discover across all local subnets, dedupe by serial; - serialize all controller I/O (concurrent calls collided on UDP :60001). Server/UX: - load .env via node --env-file-if-exists (vars weren't being read before); - SETUP_AUTH_BYPASS hardened: env-gated, dev + loopback only, fails closed otherwise; surfaced as catalog.authBypass so the wizard drops the token field; - .env.example documents all vars; inline favicon stops a 404. - apps/server/scripts/: uhppote-listen (live events, restores prior listener) and uhppote-relay (guarded door-open test). BLOCKER (wiki/decisions/access-controller-button-flow.md): the controller's push-button input auto-opens the relay in firmware with no report-without-open mode, so ticket-first entry (button -> print -> open, fail-closed) is impossible as wired. UHPPOTE can't do it on that input; ZKTeco *might* via a programmable aux input + PULL SDK but that's unverified and needs a new driver. Entry-lane hardware decision paused to focus on the business side. wiki: access-controller-button-flow (blocker), zkteco-controller (stub + assessment), uhppote-controller callout, index + log.
73 lines
2.4 KiB
JavaScript
73 lines
2.4 KiB
JavaScript
// Shared helpers for the UHPPOTE hardware test scripts.
|
|
// Run directly against the device (independent of the HTTP server).
|
|
//
|
|
// node apps/server/scripts/uhppote-listen.mjs
|
|
// node apps/server/scripts/uhppote-relay.mjs
|
|
//
|
|
// Env overrides:
|
|
// UHPPOTE_SERIAL controller serial (default 225088491)
|
|
// UHPPOTE_HOST controller IP (default 10.0.10.3)
|
|
// UHPPOTE_BCAST Config broadcast (default derived from HOST subnet)
|
|
// HOST_IP this host's IP the controller pushes events to
|
|
// (default: auto-detected interface on the controller's subnet)
|
|
|
|
import { networkInterfaces } from "node:os";
|
|
import { createRequire } from "node:module";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const uhppoted = require("uhppoted");
|
|
|
|
export const SERIAL = Number(process.env.UHPPOTE_SERIAL ?? 225088491);
|
|
export const HOST = process.env.UHPPOTE_HOST ?? "10.0.10.3";
|
|
|
|
/** Subnet-directed broadcast for the interface that owns `ip`. */
|
|
function broadcastForHost(ip) {
|
|
const o = ip.split(".").map(Number);
|
|
for (const ifaces of Object.values(networkInterfaces())) {
|
|
for (const i of ifaces ?? []) {
|
|
if (i.family !== "IPv4" || i.internal) continue;
|
|
const a = i.address.split(".").map(Number);
|
|
const m = i.netmask.split(".").map(Number);
|
|
if (o.every((x, k) => (x & m[k]) === (a[k] & m[k]))) {
|
|
return a.map((x, k) => (x & m[k]) | (~m[k] & 0xff)).join(".");
|
|
}
|
|
}
|
|
}
|
|
return "255.255.255.255";
|
|
}
|
|
|
|
/** This host's own IP on the controller's subnet (where it should push events). */
|
|
export function hostIpOnControllerSubnet(ip = HOST) {
|
|
if (process.env.HOST_IP) return process.env.HOST_IP;
|
|
const o = ip.split(".").map(Number);
|
|
for (const ifaces of Object.values(networkInterfaces())) {
|
|
for (const i of ifaces ?? []) {
|
|
if (i.family !== "IPv4" || i.internal) continue;
|
|
const a = i.address.split(".").map(Number);
|
|
const m = i.netmask.split(".").map(Number);
|
|
if (o.every((x, k) => (x & m[k]) === (a[k] & m[k]))) return i.address;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export const BCAST = process.env.UHPPOTE_BCAST ?? broadcastForHost(HOST);
|
|
|
|
export function makeCtx(timeoutMs = 5000) {
|
|
return {
|
|
config: new uhppoted.Config(
|
|
"parking",
|
|
"0.0.0.0",
|
|
`${BCAST}:60000`,
|
|
"0.0.0.0:60001",
|
|
timeoutMs,
|
|
[],
|
|
false,
|
|
),
|
|
locale: "en-US",
|
|
};
|
|
}
|
|
|
|
export const controller = { id: SERIAL, address: HOST, protocol: "udp" };
|
|
export { uhppoted };
|