Add device discovery (UHPPOTE LAN scan) to setup
UHPPOTE controllers self-announce via UDP broadcast, but the frontend had no way to find them — the admin had to type the serial blind. Add a generic discovery capability and surface it in the setup wizard. packages/devices: - DiscoverableDriver capability + DiscoveredDevice type + isDiscoverable() guard on the registry (optional, so any driver can opt in). - uhppote driver implements discover() via uhppoted getDevices (UDP broadcast), mapping each controller's serial/IP/firmware into a DiscoveredDevice; extract shared buildCtx(). apps/server: - GET /api/setup/discover/:driverId (admin-only): runs discover() and health-checks each found device so reachability shows before assigning. - catalog now returns a `discoverable` driver-id list. apps/web: - SetupWizard "Scan for controllers" button for discoverable drivers; lists found devices with health badges; selecting one auto-fills serial + host. api client gains discoverDevices(). wiki: new device-discovery concept; cross-link from registry/setup/uhppote; note the broadcast-permission (EACCES) deployment caveat; index + log. Verified: catalog flags uhppote discoverable; discover runs and fails gracefully without hardware; non-discoverable driver -> 400; missing token -> 401.
This commit is contained in:
+28
-1
@@ -19,7 +19,10 @@ export interface CatalogEntry {
|
||||
}
|
||||
|
||||
export type DeviceCategory = "access" | "reader" | "camera" | "printer";
|
||||
export type Catalog = Record<DeviceCategory, CatalogEntry[]>;
|
||||
export type Catalog = Record<DeviceCategory, CatalogEntry[]> & {
|
||||
/** Driver ids that support LAN discovery. */
|
||||
discoverable: string[];
|
||||
};
|
||||
|
||||
export async function fetchCatalog(): Promise<Catalog> {
|
||||
const res = await fetch("/api/setup/catalog");
|
||||
@@ -27,6 +30,30 @@ export async function fetchCatalog(): Promise<Catalog> {
|
||||
return res.json() as Promise<Catalog>;
|
||||
}
|
||||
|
||||
export interface DiscoveredDevice {
|
||||
id: string;
|
||||
label: string;
|
||||
config: Record<string, string | number | boolean>;
|
||||
info?: Record<string, string>;
|
||||
health: { status: string; detail?: string };
|
||||
}
|
||||
|
||||
/** Scan the LAN for devices a driver can discover (e.g. UHPPOTE). Admin-only. */
|
||||
export async function discoverDevices(
|
||||
token: string,
|
||||
driverId: string,
|
||||
): Promise<DiscoveredDevice[]> {
|
||||
const res = await fetch(`/api/setup/discover/${driverId}`, {
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const msg = (await res.json().catch(() => ({}))) as { error?: string };
|
||||
throw new Error(msg.error ?? `discover: ${res.status}`);
|
||||
}
|
||||
const body = (await res.json()) as { devices: DiscoveredDevice[] };
|
||||
return body.devices;
|
||||
}
|
||||
|
||||
export interface AssignBody {
|
||||
lane: number;
|
||||
category: DeviceCategory;
|
||||
|
||||
Reference in New Issue
Block a user