export interface FingerbankResult { deviceName: string | null; score: number | null; version: string | null; } export interface FingerbankSignals { mac: string; // "Upnp string" per Fingerbank's docs -- we pass the SSDP SERVER header // captured by deep-check-device.sh, when present (e.g. // "Linux/3.10 UPnP/1.0 MiniDLNA/1.1"), as extra fingerprinting signal // beyond the bare MAC. upnpServer: string | null; } // On-demand only (folded into the existing deep-check button), never // automatic/bulk -- keeps this well under Fingerbank's free-tier query // limits and avoids sending every device's MAC to a third party by default. export async function identifyDevice( apiKey: string, signals: FingerbankSignals ): Promise { const body: Record = { mac: signals.mac }; if (signals.upnpServer) body.upnp_user_agents = [signals.upnpServer]; const res = await fetch("https://api.fingerbank.org/api/v2/combinations/interrogate", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (res.status === 404) { // No match in Fingerbank's database -- not an error, just nothing found. return null; } if (!res.ok) { throw new Error(`Fingerbank API returned ${res.status}`); } const data = (await res.json()) as { device_name?: string; score?: number; version?: string; }; return { deviceName: data.device_name ?? null, score: data.score ?? null, version: data.version ?? null, }; }