@camstack/addon-provider-hikvision 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/assets/icon.svg +6 -0
- package/dist/addon.js +5106 -0
- package/dist/addon.js.map +1 -0
- package/dist/addon.mjs +7 -0
- package/dist/addon.mjs.map +1 -0
- package/dist/chunk-OIL2FS73.mjs +5117 -0
- package/dist/chunk-OIL2FS73.mjs.map +1 -0
- package/dist/index.js +5118 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +17 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/addon.ts","../src/hikvision-camera.ts","../src/schema.ts","../src/hikvision-isapi-client.ts","../src/digest-auth.ts","../src/accessories/index.ts","../src/accessories/light.ts","../src/accessories/base.ts","../src/accessories/siren.ts","../src/intercom/intercom-orchestrator.ts","../src/intercom/g711-encoder.ts","../src/intercom/hikvision-intercom-session.ts","../src/intercom/werift-intercom-peer.ts","../src/metadata-populator.ts"],"sourcesContent":["import type {\n ConfigUISchema,\n CreateDeviceSpec,\n DeviceConstructor,\n FieldProbeResult,\n IDevice,\n ProviderRegistration,\n SystemEvent,\n} from '@camstack/types'\nimport { BaseDeviceProvider, DeviceType, EventCategory } from '@camstack/types'\nimport { HikvisionCamera, hikvisionCameraSchema, HIKVISION_ADDON_ID } from './hikvision-camera.js'\nimport { HikvisionIsapiClient } from './hikvision-isapi-client.js'\n\nfunction getString(obj: Record<string, unknown>, key: string): string {\n const v = obj[key]\n return typeof v === 'string' ? v : ''\n}\n\nfunction getBoolean(obj: Record<string, unknown>, key: string): boolean {\n const v = obj[key]\n return v === true\n}\n\nfunction getNumber(obj: Record<string, unknown>, key: string, fallback: number): number {\n const v = obj[key]\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback\n}\n\n/**\n * Build the Add-Device creation form. One section per logical block —\n * connection, credentials, optional HTTPS — and a single `host` probe\n * that runs `/ISAPI/System/deviceInfo` so the operator gets immediate\n * confirmation the camera responds before saving.\n */\nfunction buildCreationFormSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'identity',\n title: 'Camera',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Name', required: true, placeholder: 'Driveway' },\n ],\n },\n {\n id: 'credentials',\n title: 'Credentials',\n description: 'ISAPI uses HTTP Digest auth — no plaintext password on the wire even over HTTP. Required for the host probe.',\n columns: 2,\n fields: [\n { type: 'text', key: 'username', label: 'Username', default: 'admin', required: true },\n { type: 'password', key: 'password', label: 'Password', required: true, showToggle: true },\n ],\n },\n {\n id: 'connection',\n title: 'Connection',\n description: 'ISAPI HTTP(S) endpoint. Default port 80 (HTTP); switch to 443 + HTTPS for cameras with TLS enabled.',\n columns: 2,\n fields: [\n { type: 'probe', key: 'host', label: 'Camera IP', required: true, placeholder: '192.168.1.100', inputType: 'text' },\n { type: 'number', key: 'port', label: 'Port', default: 80, min: 1, max: 65535, step: 1 },\n { type: 'boolean', key: 'https', label: 'Use HTTPS', default: false, style: 'switch' },\n ],\n },\n ],\n }\n}\n\nfunction isStreamBrokerReady(event: SystemEvent): boolean {\n if (event.category !== 'system.ready-state') return false\n const data = event.data as Record<string, unknown>\n return data['capName'] === 'stream-broker' && data['state'] === 'ready'\n}\n\n/** True when the string looks like a real hardware identifier — not\n * an all-zeros / all-`F` placeholder. Mirrors the Reolink helper. */\nfunction isMeaningfulIdentifier(s: string): boolean {\n if (!s || s.length < 6) return false\n const distinct = new Set(s.toLowerCase().split('').filter((c) => c !== '0' && c !== 'f'))\n return distinct.size >= 1\n}\n\nexport class HikvisionProviderAddon extends BaseDeviceProvider {\n protected readonly addonId = HIKVISION_ADDON_ID\n protected readonly providerName = 'Hikvision'\n protected readonly deviceClasses: Partial<Record<DeviceType, DeviceConstructor<IDevice>>> = {\n // `HikvisionCamera`'s config schema has 30+ explicitly-typed fields.\n // The variance check between `DeviceConfig<typeof hikvisionCameraSchema>`\n // (concrete shape) and `DeviceConfig<z.ZodObject<core.$ZodLooseShape>>`\n // (the IDevice shape) causes TS2589 \"Type instantiation is excessively\n // deep\" before TS can prove the assignment. The runtime types ARE\n // assignable; this cast tells TS to skip the deep variance check.\n // Reolink/ONVIF have smaller schemas and pass without it.\n [DeviceType.Camera]: HikvisionCamera as unknown as DeviceConstructor<IDevice>,\n }\n\n constructor() { super({}) }\n\n /**\n * Hikvision stableId derivation — uniform `mac → host(IP)` chain,\n * matching the Reolink provider's pattern.\n *\n * Priority:\n * 1. mac — hardware MAC from `/ISAPI/System/Network/interfaces/1`,\n * persisted into `deviceCache.mac` by `onCreateDevice`.\n * Durable across firmware updates + DHCP renewals.\n * 2. host — operator-typed IP/hostname (DHCP-reservable). Always\n * present on Hikvision adds (`onCreateDevice` validates\n * host is non-empty before this code path).\n *\n * No addon-id prefix — the deviceMeta row key already namespaces by\n * addon (`<addonId>:<stableId>`).\n */\n protected override generateStableId(_type: DeviceType, config?: Record<string, unknown>): string {\n const cfg = config ?? {}\n const cache = (cfg['deviceCache'] as Record<string, unknown> | undefined) ?? {}\n const macRaw = typeof cache['mac'] === 'string' ? cache['mac'].trim() : ''\n const mac = macRaw.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()\n if (isMeaningfulIdentifier(mac)) return `mac-${mac}`\n const host = typeof cfg['host'] === 'string' ? cfg['host'].trim() : ''\n if (host.length > 0) {\n const slug = host.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n if (slug.length > 0) return `host-${slug}`\n }\n const hostLabel = host || '(unknown host)'\n const reason = `Hikvision probe on ${hostLabel} resolved neither mac nor host address — cannot persist a stable row key. Verify network reachability + credentials, then retry.`\n try {\n const now = Date.now()\n void this.ctx.api?.alerts?.emit?.mutate?.({\n id: `hikvision-no-stableid-${now}`,\n category: 'addon.error',\n severity: 'error',\n title: `Hikvision: missing identifier on ${hostLabel}`,\n message: reason,\n status: 'active',\n read: false,\n createdAt: now,\n updatedAt: now,\n source: { type: 'addon', id: this.addonId },\n metadata: { host: hostLabel },\n })\n } catch { /* alerts cap optional */ }\n throw new Error(`Hikvision: ${reason}`)\n }\n\n protected override async onInitialize(): Promise<ProviderRegistration[]> {\n const regs = await super.onInitialize()\n\n // Re-publish every Hikvision camera's streams to the broker on\n // boot-order races + broker restart. Mirrors RTSP / Reolink — the\n // broker is the source of truth for stream registry, and the\n // provider just makes sure its own devices are reflected there\n // after every ready transition.\n this.subscribe(\n { category: EventCategory.SystemReadyState },\n (event) => {\n if (!isStreamBrokerReady(event)) return\n void this.republishAll().catch((err: unknown) => {\n this.ctx.logger.warn('Failed to re-publish Hikvision streams after broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n },\n )\n\n // Mirror broker-derived health into the device.online flag —\n // identical pattern to the RTSP provider. Hikvision's alarm stream\n // also flips `markOnline(true)` on first connect; the broker's\n // view is the authoritative source for video-side aliveness, the\n // alarm stream tells us about control-plane reachability.\n this.subscribe(\n { category: EventCategory.DeviceStateChanged },\n (event) => {\n const data = event.data as { deviceId?: number; capName?: string; slice?: { online?: boolean } }\n if (data.capName !== 'camera-streams') return\n const deviceId = data.deviceId\n if (typeof deviceId !== 'number') return\n const registry = this.ctx.kernel.deviceRegistry\n if (!registry) return\n if (registry.getAddonId(deviceId) !== this.addonId) return\n const device = registry.getById(deviceId)\n if (!device) return\n const online = data.slice?.online === true\n if (device.online !== online) device.online = online\n },\n )\n\n return regs\n }\n\n // ── Creation ─────────────────────────────────────────────────────\n\n protected async onGetCreationSchema(type: DeviceType): Promise<ConfigUISchema | null> {\n if (type !== DeviceType.Camera) return null\n return buildCreationFormSchema()\n }\n\n protected async onCreateDevice(type: DeviceType, config: Record<string, unknown>): Promise<CreateDeviceSpec> {\n if (type !== DeviceType.Camera) {\n throw new Error(`Hikvision provider does not support device type: ${type}`)\n }\n const name = getString(config, 'name').trim()\n if (!name) throw new Error('Camera name is required')\n const host = getString(config, 'host').trim()\n if (!host) throw new Error('Camera IP is required')\n const password = getString(config, 'password')\n if (!password) throw new Error('Password is required')\n\n const username = getString(config, 'username').trim() || 'admin'\n const https = getBoolean(config, 'https')\n const port = getNumber(config, 'port', https ? 443 : 80)\n\n const isapi = new HikvisionIsapiClient({ host, port, https, username, password })\n let info: Awaited<ReturnType<HikvisionIsapiClient['getDeviceInfo']>>\n try {\n info = await isapi.getDeviceInfo()\n } catch (err) {\n throw new Error(\n `Hikvision probe failed at ${host}:${port}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n this.ctx.logger.info('Hikvision deviceInfo probe', {\n tags: { host },\n meta: {\n model: info.model ?? '?',\n serial: info.serialNumber ?? '?',\n firmware: info.firmwareVersion ?? '?',\n deviceType: info.deviceType ?? '?',\n },\n })\n\n // Host network probe — fetches the canonical interface MAC so\n // `generateStableId` can derive `mac-<mac>` (durable across DHCP\n // renewals). Best-effort: a 4xx here leaves probedMac=null and\n // generateStableId falls back to host slug.\n let probedMac: string | null = null\n try {\n const net = await isapi.getNetworkInterfaceInfo()\n probedMac = net.mac\n this.ctx.logger.info('Hikvision network probe', {\n tags: { host },\n meta: { mac: net.mac ?? '(unset)', ip: net.ip ?? '(unset)' },\n })\n } catch (netErr) {\n this.ctx.logger.warn('Hikvision network probe failed (non-fatal — stableId will fall back to host)', {\n tags: { host },\n meta: { error: netErr instanceof Error ? netErr.message : String(netErr) },\n })\n }\n\n const parsed = hikvisionCameraSchema.parse({\n host,\n port,\n https,\n username,\n password,\n rtspTransport: 'unicast',\n deviceCache: {\n deviceType: 'camera',\n ...(info.model ? { model: info.model } : {}),\n ...(info.serialNumber ? { serialNumber: info.serialNumber } : {}),\n ...(info.firmwareVersion ? { firmwareVersion: info.firmwareVersion } : {}),\n ...(info.firmwareVersionInfo ? { firmwareVersionInfo: info.firmwareVersionInfo } : {}),\n ...(info.hardwareVersion ? { hardwareVersion: info.hardwareVersion } : {}),\n ...(info.deviceType ? { isapiDeviceType: info.deviceType } : {}),\n ...(probedMac ? { mac: probedMac } : {}),\n probedAt: Date.now(),\n },\n })\n\n return {\n meta: { type: DeviceType.Camera, name },\n config: parsed,\n }\n }\n\n // ── Field probing ────────────────────────────────────────────────\n\n override async testCreationField(input: {\n type: DeviceType\n key: string\n value: unknown\n formValues?: Record<string, unknown>\n }): Promise<FieldProbeResult> {\n if (input.key !== 'host') {\n return { status: 'ok', labels: ['no probe available'] }\n }\n const form = input.formValues ?? { host: typeof input.value === 'string' ? input.value : '' }\n const host = getString(form, 'host').trim()\n const password = getString(form, 'password')\n if (!host) return { status: 'error', error: 'Provide a host before running the test' }\n if (!password) return { status: 'error', error: 'Enter a password before running the test' }\n const username = getString(form, 'username').trim() || 'admin'\n const https = getBoolean(form, 'https')\n const port = getNumber(form, 'port', https ? 443 : 80)\n\n try {\n const isapi = new HikvisionIsapiClient({ host, port, https, username, password })\n const info = await isapi.getDeviceInfo()\n const labels: string[] = []\n if (info.model) labels.push(info.model)\n if (info.deviceType) labels.push(info.deviceType)\n if (info.firmwareVersion) labels.push(`Firmware: ${info.firmwareVersion}`)\n labels.push(`${https ? 'HTTPS' : 'HTTP'} :${port}`)\n return { status: 'ok', labels }\n } catch (err) {\n return {\n status: 'error',\n error: err instanceof Error ? err.message : String(err),\n }\n }\n }\n\n // ── Restore ──────────────────────────────────────────────────────\n //\n // Default `BaseDeviceProvider.onRestoreDevices` impl iterates\n // saved rows and dispatches via `deviceClasses` map. The\n // device's `onCreated` lifecycle hook (see `HikvisionCamera`)\n // publishes streams to the broker — no override needed here.\n\n // ── Internal — re-publish every HikvisionCamera to the broker ───\n\n private async republishAll(): Promise<void> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n let published = 0\n for (const dev of all as readonly IDevice[]) {\n if (!(dev instanceof HikvisionCamera)) continue\n try {\n await dev.publishToBroker()\n published++\n } catch (err) {\n this.ctx.logger.debug('publishToBroker threw during republish', {\n tags: { deviceId: dev.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (published > 0) {\n this.ctx.logger.info('Re-published Hikvision streams to stream-broker', {\n meta: { published, total: all.length },\n })\n }\n }\n}\n","import {\n BaseDevice,\n DeviceType,\n DeviceFeature,\n EventCategory,\n createEvent,\n hydrateSchema,\n snapshotCapability,\n rebootCapability,\n motionCapability,\n osdCapability,\n ptzCapability,\n ptzAutotrackCapability,\n intercomCapability,\n type OsdOverlay,\n type OsdStatus,\n type PtzAutotrackSettings,\n type PtzAutotrackStatus,\n type PtzAutotrackRuntimeState,\n createRuntimeStateBridge,\n AccessoryKind,\n ACCESSORY_LABEL,\n type AccessoryKindValue,\n type AccessoryChildSpec,\n} from '@camstack/types'\nimport type {\n ConfigUISchema,\n ConfigUISchemaWithValues,\n DeviceContext,\n ICameraDevice,\n StreamSourceEntry,\n InferNativeProvider,\n} from '@camstack/types'\nimport {\n hikvisionCameraSchema,\n HIKVISION_ADDON_ID,\n HIKVISION_AI_CLASS_MAP,\n DEFAULT_CAMERA_NUMBER,\n type HikvisionCameraConfig,\n type HikvisionDeviceCache,\n} from './schema.js'\nimport {\n HikvisionIsapiClient,\n subscribeAlarms,\n type HikvisionAlarmEvent,\n type HikvisionOverlayConfig,\n type HikvisionStreamingChannel,\n} from './hikvision-isapi-client.js'\nimport {\n createAccessoryDevice,\n type HikvisionLightHost,\n type HikvisionSirenHost,\n} from './accessories/index.js'\nimport {\n IntercomOrchestrator,\n type IntercomAudioCodecApi,\n} from './intercom/intercom-orchestrator.js'\nimport { HikvisionIntercomSession } from './intercom/hikvision-intercom-session.js'\nimport { WeriftIntercomPeer } from './intercom/werift-intercom-peer.js'\nimport { populateHikvisionMetadata } from './metadata-populator.js'\n\n/**\n * Narrow shape of the `audio-codec` cap router off `ctx.api`. We\n * keep the type local so the file doesn't depend on the generated\n * router types — only the four methods the intercom orchestrator\n * actually invokes.\n */\ninterface AudioCodecRouterShape {\n createDecodeSession: { mutate: (input: {\n codec: string\n sourceSampleRate: number\n sourceChannels: number\n targetSampleRate: number\n targetChannels: number\n tag?: string\n }) => Promise<{ sessionId: string; nodeId: string }> }\n pushEncodedFrame: { mutate: (input: {\n sessionId: string\n nodeId?: string\n data: Uint8Array\n pts?: number\n }) => Promise<void> }\n pullPcm: { query: (input: {\n sessionId: string\n nodeId?: string\n maxCount: number\n }) => Promise<readonly { data: Uint8Array; sampleRate: number; channels: number; pts: number }[]> }\n closeSession: { mutate: (input: { sessionId: string; nodeId?: string }) => Promise<void> }\n}\n\nexport {\n hikvisionCameraSchema,\n HIKVISION_ADDON_ID,\n} from './schema.js'\n\n/** One published broker stream for this camera. */\ninterface PublishedStream {\n readonly camStreamId: string\n readonly channel: HikvisionStreamingChannel\n readonly rtspUrl: string\n}\n\n/**\n * Hikvision camera device — ISAPI control channel for snapshot, reboot,\n * and the alarm event stream; RTSP URLs handed to the broker for video\n * pull (the broker handles the actual TCP/UDP RTSP, codec negotiation,\n * and re-broadcast — same model as `RtspCamera`).\n *\n * v0.1 surface:\n * - `snapshot` cap → ISAPI `/Streaming/channels/{id}/picture`\n * - `reboot` cap → ISAPI `/System/reboot`\n * - `motion` cap → emitted from the alarm-stream parser\n * - Stream broker publish for every channel reported by\n * `/Streaming/channels` (kind: `pull-rtsp`)\n *\n * Deferred (v0.2+, see plugin README):\n * - Supplemental light (`/ISAPI/Image/channels/{id}/supplementLight`)\n * - Siren via GPIO (`/ISAPI/System/IO/inputs/{port}`)\n * - PTZ (`/ISAPI/PTZCtrl/channels/{id}/...`)\n * - Two-way audio (intercom) — needs RTP relay support\n */\nexport class HikvisionCamera\n extends BaseDevice<typeof hikvisionCameraSchema>\n implements ICameraDevice, HikvisionLightHost, HikvisionSirenHost\n{\n readonly type = DeviceType.Camera as const\n\n /**\n * Features derived from the persisted `deviceCache` probe — surfaced\n * via `device-manager.getDevice`. Conservative by default: we only\n * report capabilities the device claimed during the last successful\n * probe (no default-on for accessory caps that may not be wired).\n */\n get features(): readonly DeviceFeature[] {\n const cfg = this.config.get('deviceCache')\n const out: DeviceFeature[] = [DeviceFeature.NativeSnapshot, DeviceFeature.Rebootable]\n if (cfg?.hasPtz === true) out.push(DeviceFeature.PanTiltZoom)\n if (cfg?.hasAutotrack === true) out.push(DeviceFeature.PtzAutotrack)\n if (this.intercomEnabled()) out.push(DeviceFeature.TwoWayAudio)\n return out\n }\n\n /** Resolve the operator-visible intercom mode → enabled bool.\n * `auto` requires a positive probe (`hasIntercom`), `'isapi'` always\n * enables (operator override for cameras whose channel list 404s but\n * accept audioData writes), `'off'` disables outright. `'onvif'` is\n * reserved for Phase 2 — treated as `'off'` until we land the\n * backchannel path. */\n private intercomEnabled(): boolean {\n const mode = this.config.get('twoWayAudioMode') ?? 'auto'\n if (mode === 'off' || mode === 'onvif') return false\n if (mode === 'isapi') return true\n // auto: probe-driven\n return this.config.get('deviceCache')?.hasIntercom === true\n }\n\n /** Idempotent guard for the `ptz-autotrack` cap. */\n private ptzAutotrackRegistered = false\n /** Idempotent guard for the `intercom` cap. */\n private intercomRegistered = false\n /**\n * Active intercom orchestrator. The `intercom` cap is `singleton`-mode\n * device-scoped — at most one talk session is open per camera at a\n * time. Lazy-built on first `startSession`; reused for the device's\n * lifetime (the orchestrator's own single-active-session guard\n * handles repeated starts). `null` when never opened.\n */\n private intercomOrchestrator: IntercomOrchestrator | null = null\n /** Single-flight guard for the autotrack camera refresh. */\n private autotrackRefreshInFlight: Promise<void> | null = null\n\n /**\n * Single-flight guard for the v0.2 capability discovery probe. Reset\n * on settings patches that change credentials (`disconnectAll`) so\n * the next `publishToBroker` re-probes against the fresh client.\n */\n private accessoryCapsProbed = false\n\n /** Lazy-built ISAPI client. Recreated on credential / host changes. */\n private isapi: HikvisionIsapiClient | null = null\n /** Active published streams keyed by `camStreamId`. */\n private readonly published = new Map<string, PublishedStream>()\n /** Alarm-stream subscription controller; aborted on disconnect. */\n private alarmController: AbortController | null = null\n /** Reconnect attempt counter — drives exponential backoff for the alarm stream. */\n private alarmReconnectAttempts = 0\n /** Pending alarm-reconnect timer. */\n private alarmReconnectTimer: ReturnType<typeof setTimeout> | null = null\n /** Hysteresis: drop motion-active back to inactive after this many ms with\n * no fresh `VMD` event. Hikvision fires VMD every ~1s while motion is\n * active; missing 2 ticks signals the burst is over. */\n private static readonly MOTION_INACTIVE_AFTER_MS = 3_000\n /** Last observed motion timestamp per camStreamId — drives the hysteresis above. */\n private readonly lastMotionAt = new Map<string, number>()\n /** Per-camStreamId timer that flips motion back to inactive. */\n private readonly motionInactiveTimers = new Map<string, ReturnType<typeof setTimeout>>()\n /** Latest in-memory OSD snapshot — populated on connect + on every\n * osd-cap getStatus call + every 5min poll. Drives the readonly OSD\n * section in the camera settings UI; null until the first refresh\n * succeeds. */\n private osdSnapshot: HikvisionOverlayConfig | null = null\n /** Periodic OSD-snapshot poll. Overlays change rarely so 5min is\n * plenty; the cap's own getStatus refreshes on demand for the\n * universal overlay UI. */\n private osdPollTimer: ReturnType<typeof setInterval> | null = null\n private static readonly OSD_POLL_INTERVAL_MS = 300_000\n\n constructor(ctx: DeviceContext) {\n super(ctx, hikvisionCameraSchema, { type: DeviceType.Camera })\n this.registerNativeCapabilities()\n }\n\n /**\n * Register per-device native cap providers. Snapshot + reboot are\n * always registered; motion is registered when the alarm stream\n * subscription succeeds (deferred to `connect()`).\n */\n private registerNativeCapabilities(): void {\n const snapshotProvider: InferNativeProvider<typeof snapshotCapability> = {\n getSnapshot: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`HikvisionCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`)\n }\n const isapi = this.ensureClient()\n const channelId = `${DEFAULT_CAMERA_NUMBER}01`\n try {\n const { buffer, contentType } = await isapi.getSnapshot(channelId)\n return {\n base64: buffer.toString('base64'),\n contentType,\n }\n } catch (err) {\n this.ctx.logger.warn('hikvision snapshot failed', {\n tags: { deviceId: this.id },\n meta: { channelId, error: err instanceof Error ? err.message : String(err) },\n })\n return null\n }\n },\n invalidateCache: async () => {\n // No local cache — the snapshot wrapper owns it.\n },\n }\n this.ctx.registerNativeCap(snapshotCapability, snapshotProvider)\n\n const rebootProvider: InferNativeProvider<typeof rebootCapability> = {\n reboot: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`HikvisionCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`)\n }\n const isapi = this.ensureClient()\n await isapi.reboot()\n return { success: true as const }\n },\n }\n this.ctx.registerNativeCap(rebootCapability, rebootProvider)\n\n // Motion cap — `isDetected` reads through the locally-mirrored slice\n // the alarm-stream parser writes via `setCapSlice`. Symmetric with\n // Reolink's pattern: provider exposes a fast read-through, the\n // pipeline-runner / device-state writers own the slice transitions.\n const motionProvider: InferNativeProvider<typeof motionCapability> = {\n isDetected: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`HikvisionCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`)\n }\n return this.state.motion?.detected ?? false\n },\n }\n this.ctx.registerNativeCap(motionCapability, motionProvider)\n // Seed the slice so subsequent partial writes merge cleanly through\n // `patchCapState` and the read path returns a populated record\n // before the first alarm event arrives.\n this.setCapSlice(motionCapability, {\n detected: false,\n lastDetectedAt: null,\n autoClearAfterMs: HikvisionCamera.MOTION_INACTIVE_AFTER_MS,\n })\n\n // Intercom is gated on the persisted probe (`hasIntercom`) +\n // operator setting (`twoWayAudioMode`). On a fresh device the\n // probe hasn't run yet so this no-ops — the post-probe call site\n // (`probeAndPersistAccessoryCaps`) re-invokes once `hasIntercom`\n // lands so the cap appears without a restart.\n this.registerIntercomIfSupported()\n\n // OSD overlays — every Hikvision channel exposes `/overlays`,\n // including legacy DS-7600 firmwares. The provider proxies the\n // three Hikvision overlay slots (datetime / channel-name / 4-8\n // free-text overlays) into the cap's homogeneous `OsdOverlay[]`\n // shape so the universal admin-ui form renders them with zero\n // per-vendor branching.\n this.registerOsdCap(DEFAULT_CAMERA_NUMBER)\n }\n\n /**\n * Register the `intercom` cap when the camera advertises two-way\n * audio (or operator forced `'isapi'`). Server-WebRTC-shaped:\n * `startSession` returns an SDP offer, `handleAnswer` applies the\n * browser's response, the orchestrator pumps Opus RTP through the\n * `audio-codec` cap (Opus → PCM s16le @ 8 kHz mono) and feeds the\n * PCM into `HikvisionIntercomSession` for G.711 encode + sticky-PUT\n * onto `/ISAPI/System/TwoWayAudio/channels/{id}/audioData`.\n *\n * The `audio-codec` cap is REQUIRED — without it (no audio-codec\n * addon installed) intercom can't function. We register the cap\n * regardless and surface the missing dep as a clear error from\n * `startSession`; this preserves the \"cap visible in bindings/docs\"\n * UX without falsely claiming readiness when audio decode isn't\n * available.\n *\n * Idempotent: subsequent calls (e.g. after a probe lands or the\n * operator flips `twoWayAudioMode`) noop once the cap is already\n * registered.\n */\n private registerIntercomIfSupported(): void {\n if (this.intercomRegistered) return\n if (!this.intercomEnabled()) return\n this.intercomRegistered = true\n\n const provider: InferNativeProvider<typeof intercomCapability> = {\n startSession: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`HikvisionCamera: intercom deviceId mismatch, expected ${this.id}, got ${deviceId}`)\n }\n if (this.disabled) {\n throw new Error('Hikvision intercom: device is disabled — re-enable it before opening a talk session')\n }\n const audioCodec = this.resolveAudioCodecApi()\n const isapi = this.ensureClient()\n if (!this.intercomOrchestrator) {\n this.intercomOrchestrator = new IntercomOrchestrator({\n deviceTag: `camera-${this.id}`,\n logger: this.ctx.logger.withTags({ deviceId: this.id, scope: 'intercom' }),\n audioCodec,\n peerFactory: ({ logger }) => new WeriftIntercomPeer({ logger }),\n talkSessionFactory: ({ logger }) => new HikvisionIntercomSession({\n client: isapi,\n logger,\n deviceTag: `camera-${this.id}`,\n }),\n })\n }\n return this.intercomOrchestrator.start()\n },\n handleAnswer: async ({ deviceId, sessionId, sdpAnswer }) => {\n if (deviceId !== this.id) return\n if (!this.intercomOrchestrator) {\n throw new Error(`Hikvision intercom: handleAnswer with no active orchestrator (sessionId=${sessionId})`)\n }\n await this.intercomOrchestrator.handleAnswer(sessionId, sdpAnswer)\n },\n stopSession: async ({ deviceId, sessionId }) => {\n if (deviceId !== this.id) return\n if (!this.intercomOrchestrator) return\n await this.intercomOrchestrator.stop(sessionId)\n },\n }\n this.ctx.registerNativeCap(intercomCapability, provider)\n this.ctx.logger.info('intercom cap registered (ISAPI two-way audio)', {\n tags: { deviceId: this.id },\n })\n }\n\n /**\n * Register the `osd` cap. Builds the cap's homogeneous `OsdOverlay[]`\n * snapshot from Hikvision's three overlay slots (datetime, channel\n * name, free-text) on every `getStatus`. `setOverlay` routes the\n * patch to the right per-element ISAPI PUT.\n *\n * Hikvision's overlay positions are normalized pixel coords (X/Y);\n * we don't translate them into the cap's enum positions because the\n * cap's coarse 9-zone grid loses information. The provider declares\n * `availablePositions: []` so the universal UI hides the position\n * field — only `enabled` and `text` are operator-relevant here.\n */\n private registerOsdCap(cameraNumber: number): void {\n const provider: InferNativeProvider<typeof osdCapability> = {\n getStatus: async ({ deviceId }): Promise<OsdStatus> => {\n if (deviceId !== this.id) {\n throw new Error(`HikvisionCamera: osd deviceId mismatch, expected ${this.id}, got ${deviceId}`)\n }\n const isapi = this.ensureClient()\n const config = await isapi.getOverlayConfig(cameraNumber)\n if (!config) return { overlays: [] }\n // Cache for the readonly OSD section in the camera settings\n // UI — hydrateSchema reads from this.osdSnapshot synchronously.\n this.osdSnapshot = config\n const overlays: OsdOverlay[] = []\n // Date/time — firmware-rendered; operator can only flip\n // visibility, not edit the format.\n overlays.push({\n id: 'dateTime',\n kind: 'timestamp',\n enabled: config.dateTime.enabled,\n availablePositions: [],\n readOnly: true,\n })\n // Channel name — text tracks `deviceName`, owned by the\n // camera-side time/network settings; we expose visibility only.\n overlays.push({\n id: 'channelName',\n kind: 'text',\n enabled: config.channelName.enabled,\n text: this.name,\n availablePositions: [],\n readOnly: true,\n })\n for (const t of config.textOverlays) {\n overlays.push({\n id: `text-${t.id}`,\n kind: 'text',\n enabled: t.enabled,\n text: t.text ?? '',\n availablePositions: [],\n })\n }\n return { overlays }\n },\n setOverlay: async ({ deviceId, overlayId, patch }) => {\n if (deviceId !== this.id) return\n const isapi = this.ensureClient()\n if (overlayId === 'dateTime') {\n await isapi.setDateTimeOverlay(cameraNumber, { enabled: patch.enabled })\n return\n }\n if (overlayId === 'channelName') {\n await isapi.setChannelNameOverlay(cameraNumber, { enabled: patch.enabled })\n return\n }\n if (overlayId.startsWith('text-')) {\n const id = overlayId.slice('text-'.length)\n await isapi.setTextOverlay(cameraNumber, id, {\n enabled: patch.enabled,\n text: patch.text,\n })\n return\n }\n },\n }\n this.ctx.registerNativeCap(osdCapability, provider)\n }\n\n /**\n * Resolve the `audio-codec` cap router off `ctx.api`. Throws a clear\n * error when the cap isn't mounted (no audio-codec addon installed)\n * so the operator gets actionable feedback at session-open time\n * rather than a generic dispatch failure.\n */\n private resolveAudioCodecApi(): IntercomAudioCodecApi {\n const root = this.ctx.api as unknown as { audioCodec?: AudioCodecRouterShape }\n const router = root.audioCodec\n if (!router) {\n throw new Error(\n 'Hikvision intercom: `audio-codec` capability is not mounted. ' +\n 'Install + enable the `addon-audio-codec-nodeav` addon (or any other ' +\n 'addon that provides the `audio-codec` capability) before opening ' +\n 'a talk session.',\n )\n }\n return {\n createDecodeSession: (input) => router.createDecodeSession.mutate(input),\n pushEncodedFrame: (input) => router.pushEncodedFrame.mutate(input),\n pullPcm: (input) => router.pullPcm.query(input),\n closeSession: (input) => router.closeSession.mutate(input),\n }\n }\n\n // ── Connection lifecycle ─────────────────────────────────────────\n\n /**\n * Initialise the ISAPI client (lazy — happens on first cap call) and\n * cache it for the lifetime of the credentials. A subsequent\n * `applySettingsPatch` call that mutates host / port / scheme /\n * credentials drops the cache so the next call rebuilds.\n *\n * Exposed publicly because accessory children share the parent's\n * client through the `HikvisionAccessoryHost` contract — there's\n * no separate auth flow per accessory, just one ISAPI session\n * funnelled through the whole device tree.\n */\n ensureClient(): HikvisionIsapiClient {\n if (this.isapi) return this.isapi\n const host = this.config.get('host')\n const port = this.config.get('port') ?? (this.config.get('https') ? 443 : 80)\n this.isapi = new HikvisionIsapiClient(\n {\n host,\n port,\n https: this.config.get('https') ?? false,\n username: this.config.get('username') ?? 'admin',\n password: this.config.get('password'),\n },\n this.ctx.logger.withTags({ deviceId: this.id }),\n )\n return this.isapi\n }\n\n /**\n * Discover RTSP streams and publish them to the stream-broker. Run\n * after device creation + on broker-ready events. Idempotent — the\n * broker keys entries by `(deviceId, camStreamId)`. Failures fall\n * back to a synthetic `{cam}01`/`{cam}02` channel set so older\n * NVRs (no `/ISAPI/Streaming/channels`) still get streams published.\n */\n /**\n * Phase 3 — probe ISAPI for PTZ / supplemental-light / siren / alarm-IO\n * support and persist into `deviceCache`. Runs BEFORE\n * `getAccessoryChildren()` so the spawn loop sees fresh probe results.\n * Best-effort: failures retain the previous deviceCache values.\n *\n * Also kicks the hardware-metadata populator (model / firmware /\n * serial / mac / ...) on every kernel-driven probe so an addon\n * restart re-syncs the metadata blob with current firmware.\n */\n override async onProbe(): Promise<void> {\n if (!this.accessoryCapsProbed) {\n this.accessoryCapsProbed = true\n try {\n await this.probeAndPersistAccessoryCaps()\n } catch (err) {\n this.ctx.logger.debug('hikvision accessory cap probe failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n this.accessoryCapsProbed = false\n }\n }\n // Hardware-identity metadata refresh — runs on every probe (every\n // addon restart). `populateHikvisionMetadata` is idempotent\n // (`setMetadata` short-circuits no-diff patches).\n try {\n const isapi = this.ensureClient()\n await populateHikvisionMetadata(isapi, {\n id: this.id,\n ctx: this.ctx,\n setDeviceCachePatch: async (cachePatch) => {\n const previous = this.config.get('deviceCache') ?? {}\n await this.config.setAll({ deviceCache: { ...previous, ...cachePatch } })\n },\n })\n } catch (err) {\n this.ctx.logger.debug('hikvision metadata populate failed (non-fatal)', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n /** Phase 5 — fired after probe + accessory reconciliation. Publishes\n * streams; gated on `disabled`. */\n override async onActivate(): Promise<void> {\n if (this.disabled) {\n this.ctx.logger.info('Hikvision device disabled — skipping initial broker publish')\n return\n }\n await this.publishToBroker().catch((err: unknown) => {\n this.ctx.logger.warn('publishToBroker on activate failed — will retry on broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n // Best-effort initial OSD snapshot for the readonly settings tab —\n // success populates `this.osdSnapshot` for the next form open;\n // failure (camera offline, ISAPI 4xx) just leaves the section\n // showing \"—\" until the next periodic poll.\n void this.refreshOsdSnapshot()\n if (this.osdPollTimer === null) {\n this.osdPollTimer = setInterval(\n () => { void this.refreshOsdSnapshot() },\n HikvisionCamera.OSD_POLL_INTERVAL_MS,\n )\n }\n }\n\n /** Single-flight OSD config fetch; updates the in-memory snapshot\n * consumed by the readonly OSD section in `getSettingsUISchema`. */\n private async refreshOsdSnapshot(): Promise<void> {\n try {\n const isapi = this.ensureClient()\n const config = await isapi.getOverlayConfig(DEFAULT_CAMERA_NUMBER)\n if (config) this.osdSnapshot = config\n } catch (err) {\n this.ctx.logger.debug('osd snapshot refresh failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n async publishToBroker(): Promise<void> {\n const isapi = this.ensureClient()\n let channels: readonly HikvisionStreamingChannel[]\n try {\n channels = await isapi.listChannels()\n if (channels.length === 0) channels = this.fallbackChannels()\n } catch (err) {\n this.ctx.logger.debug('listChannels failed — using synthetic fallback', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n channels = this.fallbackChannels()\n }\n\n const seen = new Set<string>()\n for (const ch of channels) {\n if (!ch.enabled) continue\n const camStreamId = this.deriveCamStreamId(ch)\n const url = this.buildRtspUrl(ch.id)\n seen.add(camStreamId)\n try {\n await this.ctx.api.streamBroker.publishCameraStream.mutate({\n deviceId: this.id,\n camStreamId,\n kind: 'pull-rtsp',\n url,\n ...(ch.width && ch.height ? { resolution: { width: ch.width, height: ch.height } } : {}),\n label: this.deriveStreamLabel(ch),\n })\n this.published.set(camStreamId, { camStreamId, channel: ch, rtspUrl: url })\n } catch (err) {\n this.ctx.logger.warn('publishCameraStream failed', {\n tags: { deviceId: this.id, camStreamId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n // Retract any stream we previously published that's no longer in\n // the camera's offer (e.g. operator disabled the substream).\n for (const camStreamId of [...this.published.keys()]) {\n if (seen.has(camStreamId)) continue\n try {\n await this.ctx.api.streamBroker.retractCameraStream.mutate({ deviceId: this.id, camStreamId })\n } catch (err) {\n this.ctx.logger.debug('retractCameraStream failed', {\n tags: { deviceId: this.id, camStreamId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n this.published.delete(camStreamId)\n }\n\n // Kick off (or re-establish) the alarm subscription once we have a\n // working ISAPI session. Best-effort — failure logs and schedules a\n // retry, the rest of the surface stays usable.\n this.ensureAlarmSubscription()\n\n // Accessory caps probe moved to `onProbe()` — kernel orchestrates\n // it BEFORE accessory reconciliation. By the time we land here, the\n // probe is already complete and accessories are spawned.\n }\n\n /**\n * Probe the camera for PTZ / supplemental-light / alarm-input\n * support and persist the discovery into `deviceCache`. PTZ stays\n * on the parent (vendor-agnostic — Reolink does the same) and is\n * registered as a native cap inline; light + siren turn into child\n * accessory devices spawned by the kernel from `getAccessoryChildren`.\n *\n * Each probe is independent — a missing PTZ doesn't suppress the\n * light probe, etc. Best-effort; failures log at debug and skip\n * that probe path without affecting the rest of the device surface.\n *\n * MUST be awaited from `onCreated` so the kernel's accessory\n * auto-spawn (which reads `deviceCache.hasSupplementalLight` /\n * `hasAlarmIo` via `getAccessoryChildren`) sees the freshest probe\n * results.\n */\n private async probeAndPersistAccessoryCaps(): Promise<void> {\n const isapi = this.ensureClient()\n const cameraNumber = DEFAULT_CAMERA_NUMBER\n\n const cacheUpdate: Partial<NonNullable<HikvisionDeviceCache>> = {}\n\n const ptzCaps = await isapi.getPtzCapabilities(cameraNumber)\n if (ptzCaps && (ptzCaps.hasPan || ptzCaps.hasTilt || ptzCaps.hasZoom)) {\n this.registerPtzCap(cameraNumber)\n cacheUpdate.hasPtz = true\n\n // Autotrack lives under PTZ — only worth probing when the\n // camera has PTZ at all. The smartTracking endpoint exists on\n // every PTZ-capable Hikvision firmware, but capability\n // discovery (`smartTrackingCapabilities`) tells us which\n // optional knobs (`duration`, `smartTrackingType`) the camera\n // accepts.\n const trackingCaps = await isapi.getSmartTrackingCapabilities(cameraNumber)\n if (trackingCaps) {\n cacheUpdate.hasAutotrack = true\n cacheUpdate.autotrackSupportsDuration = trackingCaps.supportsDuration\n cacheUpdate.autotrackSupportsType = trackingCaps.supportsType\n this.registerPtzAutotrackCap(cameraNumber)\n }\n }\n\n const lightCaps = await isapi.getSupplementLightCapabilities(cameraNumber)\n if (lightCaps && lightCaps.modes.length > 0) {\n // Pick the canonical \"on\" mode. Hikvision spotlights advertise\n // `colorVuWhiteLight` on newer firmwares + `manual` on older.\n // We default to `colorVuWhiteLight` when present, else fall\n // back to the first non-`close` mode. Persist BOTH the full\n // mode list (so the LightAccessory's settings UI can render\n // the select) and the default (so first-spawn config seeds\n // with the right value).\n const defaultMode = lightCaps.modes.includes('colorVuWhiteLight')\n ? 'colorVuWhiteLight'\n : (lightCaps.modes.find((m) => m !== 'close') ?? null)\n if (defaultMode) {\n cacheUpdate.hasSupplementalLight = true\n cacheUpdate.lightSupportedModes = [...lightCaps.modes]\n cacheUpdate.lightDefaultMode = defaultMode\n // White-light support — anything other than `irLight` / `close`\n // is operator-driven visible illumination. IR-only cameras\n // (`['irLight','close']`) get `false` here → no accessory\n // device spawn, only the parent Light tab.\n const whiteLightModes = ['colorVuWhiteLight', 'whiteLight', 'manual', 'mixed']\n cacheUpdate.lightHasWhiteLight = lightCaps.modes.some((m) => whiteLightModes.includes(m))\n }\n // Snapshot the current state for the parent Light tab.\n const lightState = await isapi.getSupplementLight(cameraNumber)\n if (lightState) {\n cacheUpdate.lightSnapshot = {\n mode: lightState.mode,\n whiteLightBrightness: lightState.whiteLightBrightness,\n irLightBrightness: lightState.irLightBrightness,\n brightnessRegulatorMode: lightState.brightnessRegulatorMode,\n }\n }\n }\n\n const alarmInputs = await isapi.getAlarmInputs()\n if (alarmInputs && alarmInputs.length > 0) {\n // ISAPI exposes the port as a numeric string (`\"1\"`, `\"2\"`); we\n // persist the numeric form so the SirenAccessory's settings\n // schema (numeric `port` field) doesn't have to coerce on every\n // read. Drop ids that fail to parse — they shouldn't reach us\n // from a healthy camera, but a malformed XML response shouldn't\n // poison the persisted cache.\n const ports = alarmInputs\n .map((a) => Number.parseInt(a.id, 10))\n .filter((n) => Number.isFinite(n) && n > 0)\n if (ports.length > 0) {\n cacheUpdate.hasAlarmIo = true\n cacheUpdate.alarmInputPorts = ports\n }\n }\n\n // TwoWayAudio probe — best-effort. Older firmwares 404 here; we\n // record `hasIntercom: false` in that case so the auto mode keeps\n // the cap unregistered. Operators on those firmwares can still\n // force the cap by setting `twoWayAudioMode: 'isapi'`.\n try {\n const channels = await isapi.listTwoWayAudioChannels()\n if (channels.length > 0) {\n cacheUpdate.hasIntercom = true\n const channelId = channels[0]!.id\n const codec = channels[0]?.audioCompressionType ?? null\n if (codec) cacheUpdate.intercomAudioCompressionType = codec\n // Audio capabilities for the Audio settings tab — populates\n // the codec / input-type selects + speaker-volume slider\n // bounds. Best-effort: 4xx here just leaves the form on\n // safe defaults.\n const audioCaps = await isapi.getAudioCapabilities(channelId)\n if (audioCaps) {\n cacheUpdate.audioCapabilities = {\n audioCodecs: [...audioCaps.audioCodecs],\n audioInputTypes: [...audioCaps.audioInputTypes],\n speakerVolumeMin: audioCaps.speakerVolumeMin,\n speakerVolumeMax: audioCaps.speakerVolumeMax,\n supportsNoiseReduction: audioCaps.supportsNoiseReduction,\n }\n }\n } else {\n cacheUpdate.hasIntercom = false\n }\n } catch (err) {\n this.ctx.logger.debug('hikvision intercom probe failed (best-effort)', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n cacheUpdate.hasIntercom = false\n }\n\n // Image tuning snapshot — color / WDR / IR-cut probed in parallel,\n // each best-effort. Legacy firmwares may 404 on a sub-resource;\n // we record only what we got. Drives the Image settings tab's\n // hydrated values.\n try {\n const [color, sharpness, wdr, ircut] = await Promise.all([\n isapi.getImageColor(DEFAULT_CAMERA_NUMBER).catch(() => null),\n // Sharpness lives on its own endpoint — fetch in parallel.\n // Fall back to `color.sharpness` (rare folded firmware) if\n // the dedicated endpoint 404s.\n isapi.getImageSharpness(DEFAULT_CAMERA_NUMBER).catch(() => null),\n isapi.getImageWdr(DEFAULT_CAMERA_NUMBER).catch(() => null),\n isapi.getIrcutFilter(DEFAULT_CAMERA_NUMBER).catch(() => null),\n ])\n const imageSnapshot: NonNullable<HikvisionDeviceCache>['imageSnapshot'] = {}\n if (color) {\n if (color.brightness !== null) imageSnapshot.brightness = color.brightness\n if (color.contrast !== null) imageSnapshot.contrast = color.contrast\n if (color.saturation !== null) imageSnapshot.saturation = color.saturation\n if (color.sharpness !== null) imageSnapshot.sharpness = color.sharpness\n }\n if (sharpness !== null) imageSnapshot.sharpness = sharpness\n if (wdr) {\n imageSnapshot.wdrEnabled = wdr.enabled\n if (wdr.level !== null) imageSnapshot.wdrLevel = wdr.level\n }\n if (ircut) imageSnapshot.ircutMode = ircut.mode\n if (Object.keys(imageSnapshot).length > 0) {\n cacheUpdate.imageSnapshot = imageSnapshot\n }\n } catch (err) {\n this.ctx.logger.debug('hikvision image probe failed (best-effort)', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n\n // Streaming channel snapshots — probe every channel the camera\n // enumerates via `/ISAPI/Streaming/channels` (typically main /\n // sub / third on multi-stream firmwares; just main on single-\n // stream models). Drives the Stream settings tab — one sub-tab\n // per detected channel, each with its own firmware-advertised\n // bounds. Best-effort: per-channel failures don't abort the\n // probe of the others, and a complete miss leaves the form\n // empty (no Streaming sub-tab is rendered).\n try {\n const streamingChannels = await isapi.listChannels().catch(() => [] as readonly HikvisionStreamingChannel[])\n const snapshots: Record<string, NonNullable<HikvisionDeviceCache>['streamSnapshots'] extends Record<string, infer V> | undefined ? V : never> = {}\n for (const channel of streamingChannels) {\n try {\n // Probe channel config + dynamicCap in parallel — the latter\n // 404s on legacy firmware so the failure path here just\n // omits supportedFrameRates from the snapshot, leaving the\n // FPS field as a free-typed number.\n const [cfg, caps] = await Promise.all([\n isapi.getStreamingChannel(channel.id),\n isapi.getStreamingChannelCapabilities(channel.id).catch(() => null),\n ])\n if (!cfg) continue\n snapshots[channel.id] = {\n channelName: cfg.channelName,\n videoCodecType: cfg.videoCodecType,\n maxFrameRate: cfg.maxFrameRate,\n videoQualityControlType: cfg.videoQualityControlType,\n vbrUpperCap: cfg.vbrUpperCap.value,\n vbrUpperCapMin: cfg.vbrUpperCap.min,\n vbrUpperCapMax: cfg.vbrUpperCap.max,\n constantBitRate: cfg.constantBitRate.value,\n constantBitRateMin: cfg.constantBitRate.min,\n constantBitRateMax: cfg.constantBitRate.max,\n fixedQuality: cfg.fixedQuality,\n govLength: cfg.govLength.value,\n govLengthMin: cfg.govLength.min,\n govLengthMax: cfg.govLength.max,\n smartCodecEnabled: cfg.smartCodecEnabled,\n resolutionWidth: cfg.resolution.width,\n resolutionHeight: cfg.resolution.height,\n ...(caps && caps.supportedFrameRates.length > 0\n ? { supportedFrameRates: [...caps.supportedFrameRates] }\n : {}),\n }\n } catch (err) {\n this.ctx.logger.debug('hikvision streaming channel probe failed', {\n tags: { deviceId: this.id, channelId: channel.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (Object.keys(snapshots).length > 0) {\n cacheUpdate.streamSnapshots = snapshots\n }\n } catch (err) {\n this.ctx.logger.debug('hikvision streaming probe failed (best-effort)', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n\n if (Object.keys(cacheUpdate).length === 0) return\n // Persist the probe results so next boot can short-circuit the\n // probe (informational only — the caps still re-register at\n // runtime). Spread current cache to avoid clobbering other\n // probe-time fields like model / serial / firmware.\n const current = this.config.get('deviceCache') ?? {}\n try {\n await this.config.setAll({\n deviceCache: { ...current, ...cacheUpdate, probedAt: Date.now() },\n })\n } catch (err) {\n this.ctx.logger.debug('hikvision deviceCache update failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n\n // Now that hasIntercom (et al) is persisted, re-evaluate cap\n // registration. The ctor's call no-opped because `hasIntercom`\n // was undefined; this second pass picks it up without restart.\n this.registerIntercomIfSupported()\n\n // Re-publish the meta blob so probe-driven feature flags\n // (`TwoWayAudio`, `PanTiltZoom`, `PtzAutotrack`) appear in\n // `deviceManager.listAll` immediately — without this they only\n // surface on the next server restart for forked-worker devices.\n void this.refreshFeatures()\n }\n\n // ── PTZ cap ──────────────────────────────────────────────────────\n\n private registerPtzCap(cameraNumber: number): void {\n const isapi = this.ensureClient()\n const provider: InferNativeProvider<typeof ptzCapability> = {\n move: async ({ deviceId, pan, tilt, zoom }) => {\n if (deviceId !== this.id) return\n // `move` is treated as a one-shot continuous burst: 500ms\n // of motion, then a zero-magnitude stop. This mirrors how\n // Hikvision PTZ commands behave from their own web UI —\n // there's no \"absolute step\" endpoint, only continuous.\n await isapi.ptzContinuous(cameraNumber, { pan, tilt, zoom })\n await new Promise<void>((r) => setTimeout(r, 500))\n await isapi.ptzContinuous(cameraNumber, { pan: 0, tilt: 0, zoom: 0 })\n },\n continuousMove: async ({ deviceId, pan, tilt, zoom }) => {\n if (deviceId !== this.id) return\n await isapi.ptzContinuous(cameraNumber, { pan, tilt, zoom })\n },\n stop: async ({ deviceId }) => {\n if (deviceId !== this.id) return\n await isapi.ptzContinuous(cameraNumber, { pan: 0, tilt: 0, zoom: 0 })\n },\n getPresets: async ({ deviceId }) => {\n if (deviceId !== this.id) return []\n const presets = await isapi.listPtzPresets(cameraNumber)\n return presets.map((p) => ({ id: p.id, name: p.name ?? `Preset ${p.id}` }))\n },\n goToPreset: async ({ deviceId, presetId }) => {\n if (deviceId !== this.id) return\n await isapi.ptzGoToPreset(cameraNumber, presetId)\n },\n goHome: async ({ deviceId }) => {\n if (deviceId !== this.id) return\n // Hikvision doesn't expose a dedicated \"home\" endpoint —\n // convention is preset id `1` is home. If the camera doesn't\n // have it configured the goto returns 4xx, surfaced as a\n // thrown error.\n await isapi.ptzGoToPreset(cameraNumber, '1')\n },\n getPosition: async () => {\n // Hikvision exposes absolute position via `/PTZCtrl/channels/{id}/status`\n // but the data is firmware-dependent and frequently absent on\n // models that report PTZ capability. We return a stub so the\n // cap router has a non-throwing default; consumers that need\n // accurate positioning should subscribe to the position event.\n return { pan: 0, tilt: 0, zoom: 0 }\n },\n }\n this.ctx.registerNativeCap(ptzCapability, provider)\n this.ctx.logger.info('hikvision: PTZ cap registered', {\n tags: { deviceId: this.id },\n meta: { cameraNumber },\n })\n }\n\n // ── PTZ autotrack cap ────────────────────────────────────────────\n\n /**\n * Register the `ptz-autotrack` cap. Hikvision exposes autotrack via\n * `/ISAPI/PTZCtrl/channels/{cam}/smartTracking`; the cap surface\n * (cross-vendor) carries three settings: `targetType`,\n * `stopDelaySeconds`, `disappearDelaySeconds`. Only the first two\n * map onto the firmware (when the model advertises support — see\n * `autotrackSupportsType` / `autotrackSupportsDuration` flags from\n * the probe); `disappearDelaySeconds` is accepted at the cap layer\n * but ignored at push time because Hikvision's firmware-side\n * disappear timer is fixed.\n */\n private registerPtzAutotrackCap(cameraNumber: number): void {\n if (this.ptzAutotrackRegistered) return\n this.ptzAutotrackRegistered = true\n const isapi = this.ensureClient()\n\n const CAP_NAME = 'ptz-autotrack'\n const STALE_MS = 10_000\n\n const supportedTargetTypesForCache = (): PtzAutotrackStatus['supportedTargetTypes'] => {\n const cache = this.config.get('deviceCache') ?? {}\n // Hikvision's `<smartTrackingType>` accepts these tokens on\n // models that probe-true for `autotrackSupportsType`. When\n // the firmware reports type-not-supported, return an empty\n // list so the UI hides the selector (auto-only tracking).\n return cache.autotrackSupportsType === true\n ? [\n { value: 'auto', label: 'Auto' },\n { value: 'human', label: 'Human' },\n { value: 'vehicle', label: 'Vehicle' },\n ]\n : []\n }\n\n const readSlice = (): PtzAutotrackRuntimeState | undefined =>\n this.runtimeState.getCapState<PtzAutotrackRuntimeState>(CAP_NAME)\n\n const refreshFromCamera = async (): Promise<void> => {\n if (this.autotrackRefreshInFlight) return this.autotrackRefreshInFlight\n const promise = (async () => {\n const state = await isapi.getSmartTracking(cameraNumber).catch(() => null)\n if (state === null) {\n this.ctx.logger.debug('hikvision autotrack refresh failed — keeping last slice', {\n tags: { deviceId: this.id },\n })\n return\n }\n const prev = readSlice()\n // Hikvision's smartTracking GET returns { enabled, duration,\n // trackingType }. `disappearDelaySeconds` is firmware-fixed\n // (Hikvision doesn't expose a disappear timer), so we carry\n // forward whatever the slice already had — the operator's\n // last-set value, or the cap default 10.\n const currentSettings: PtzAutotrackSettings = {\n targetType: state.trackingType ?? '',\n stopDelaySeconds: state.duration ?? prev?.currentSettings?.stopDelaySeconds ?? 30,\n disappearDelaySeconds: prev?.currentSettings?.disappearDelaySeconds ?? 15,\n }\n this.runtimeState.setCapState(CAP_NAME, {\n enabled: state.enabled,\n lastChangedAt: prev?.enabled === state.enabled ? (prev?.lastChangedAt ?? 0) : Date.now(),\n currentSettings,\n supportedTargetTypes: supportedTargetTypesForCache(),\n lastFetchedAt: Date.now(),\n })\n })()\n this.autotrackRefreshInFlight = promise\n try { await promise } finally { this.autotrackRefreshInFlight = null }\n }\n\n const bridge = createRuntimeStateBridge({\n runtimeState: this.runtimeState,\n cap: ptzAutotrackCapability,\n ownDeviceId: this.id,\n refresh: refreshFromCamera,\n staleMs: STALE_MS,\n empty: (): PtzAutotrackStatus => ({\n enabled: false,\n lastChangedAt: 0,\n currentSettings: null,\n supportedTargetTypes: supportedTargetTypesForCache(),\n }),\n })\n\n const provider: InferNativeProvider<typeof ptzAutotrackCapability> = {\n getStatus: bridge.getStatus,\n setEnabled: async ({ deviceId, enabled }) => {\n if (deviceId !== this.id) return\n const cache = this.config.get('deviceCache') ?? {}\n // Carry the slice's current settings forward so toggling on\n // doesn't accidentally reset duration/type — the firmware\n // re-applies whatever was there last.\n const current = readSlice()?.currentSettings ?? {\n targetType: '',\n stopDelaySeconds: 30,\n disappearDelaySeconds: 15,\n }\n const params: { enabled: boolean; duration?: number; trackingType?: string } = { enabled }\n if (cache.autotrackSupportsDuration === true) params.duration = current.stopDelaySeconds\n if (cache.autotrackSupportsType === true && current.targetType.length > 0) {\n params.trackingType = current.targetType\n }\n await isapi.setSmartTracking(cameraNumber, params)\n await refreshFromCamera()\n },\n getSettings: async ({ deviceId }) => {\n if (deviceId !== this.id) return null\n await bridge.ensureFresh()\n return readSlice()?.currentSettings ?? null\n },\n setSettings: async ({ deviceId, settings }) => {\n if (deviceId !== this.id) return\n const current = readSlice()?.currentSettings ?? {\n targetType: '',\n stopDelaySeconds: 30,\n disappearDelaySeconds: 15,\n }\n const merged: PtzAutotrackSettings = {\n targetType: settings.targetType ?? current.targetType,\n stopDelaySeconds: settings.stopDelaySeconds ?? current.stopDelaySeconds,\n disappearDelaySeconds: settings.disappearDelaySeconds ?? current.disappearDelaySeconds,\n }\n\n // Push to firmware only the knobs the camera actually supports.\n // The cap layer accepts the full cross-vendor schema; we\n // narrow at the push site. `disappearDelaySeconds` is always\n // dropped — Hikvision firmware-side disappear timer is fixed.\n const cache = this.config.get('deviceCache') ?? {}\n const params: { enabled: boolean; duration?: number; trackingType?: string } = {\n enabled: readSlice()?.enabled ?? false,\n }\n if (cache.autotrackSupportsDuration === true) params.duration = merged.stopDelaySeconds\n if (cache.autotrackSupportsType === true && merged.targetType.length > 0) {\n params.trackingType = merged.targetType\n }\n try {\n await isapi.setSmartTracking(cameraNumber, params)\n } catch (err) {\n this.ctx.logger.warn('hikvision autotrack setSettings firmware push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n throw err\n }\n // Re-read the camera so we mirror what was actually accepted\n // (firmwares may clamp duration to a permitted enum).\n await refreshFromCamera()\n },\n }\n this.ctx.registerNativeCap(ptzAutotrackCapability, provider)\n this.ctx.logger.info('hikvision: ptz-autotrack cap registered', {\n tags: { deviceId: this.id },\n meta: { cameraNumber },\n })\n }\n\n // ── Accessory child auto-spawn ───────────────────────────────────\n //\n // Supplemental light + siren land as child accessories (one device\n // per kind, each with its own `switch` cap, settings UI, and per-\n // accessory state). The parent's `getAccessoryChildren` is called\n // by the kernel after `onCreated` resolves; the kernel handles\n // allocate-id / persist-meta / persist-config / construct / register\n // for each spec via the same `kernel.devices.*` API it uses for\n // top-level devices (see `spawnAccessoryChild` in the device-cap\n // proxy). Restoring an existing accessory is automatic: the kernel\n // detects the persisted row by the deterministic stableId\n // (`{parent.stableId}-acc-{kind}`) and skips the pre-persist step.\n //\n // The `parent: this` capture is intentional — accessories receive\n // the parent through the closure so they can call `ensureClient()`\n // / `lightSupportedModes()` / `alarmInputPorts()` without a\n // circular import.\n override getAccessoryChildren(): readonly AccessoryChildSpec[] {\n const cache = this.config.get('deviceCache')\n if (!cache) return []\n const out: AccessoryChildSpec[] = []\n const push = (kind: AccessoryKindValue, type: DeviceType, label: string, config: Record<string, unknown>): void => {\n out.push({\n stableIdSuffix: `acc-${kind}`,\n meta: {\n type,\n name: `${this.name} ${label}`,\n },\n config,\n factory: (ctx) => createAccessoryDevice(kind, ctx, this),\n })\n }\n // Spawn the LightAccessory ONLY when the camera has at least one\n // operator-driven white-light mode. IR-only models advertise just\n // `irLight,close` — the IR LEDs are auto-driven by the light sensor\n // and a separate Switch/Light child device would be misleading\n // (operator can't really \"turn them off\" — they fire on darkness).\n // For those models, the supplemental light tunes via the parent's\n // Light settings tab instead. See `probeAndPersistAccessoryCaps`\n // for `lightHasWhiteLight` derivation.\n if (cache.hasSupplementalLight === true && cache.lightHasWhiteLight === true) {\n const defaultMode = cache.lightDefaultMode ?? 'colorVuWhiteLight'\n push(AccessoryKind.Floodlight, DeviceType.Light, ACCESSORY_LABEL[AccessoryKind.Floodlight], {\n kind: AccessoryKind.Floodlight,\n cameraNumber: DEFAULT_CAMERA_NUMBER,\n mode: defaultMode,\n defaultBrightness: 100,\n })\n }\n if (cache.hasAlarmIo === true) {\n // Seed the SirenAccessory with the first probed port so the\n // built-in siren (commonly port 1) flips out of the box on\n // single-input cameras.\n const firstPort = cache.alarmInputPorts?.[0] ?? 1\n push(AccessoryKind.Siren, DeviceType.Siren, ACCESSORY_LABEL[AccessoryKind.Siren], {\n kind: AccessoryKind.Siren,\n cameraNumber: DEFAULT_CAMERA_NUMBER,\n port: firstPort,\n pulseDurationMs: 0,\n })\n }\n return out\n }\n\n // Accessor methods exposed via `HikvisionLightHost` /\n // `HikvisionSirenHost`. Kept thin — they just unwrap the persisted\n // probe results from `deviceCache`. Empty defaults on cameras that\n // didn't expose the corresponding capability (the schema's `?:`\n // markers + the runtime `?? []` keep the shape ergonomic).\n lightSupportedModes(): readonly string[] {\n return this.config.get('deviceCache')?.lightSupportedModes ?? []\n }\n alarmInputPorts(): readonly number[] {\n return this.config.get('deviceCache')?.alarmInputPorts ?? []\n }\n\n private deriveCamStreamId(ch: HikvisionStreamingChannel): string {\n // Single-camera devices: `main` / `sub` / `third` for parity with\n // the well-known stream-broker labels. Multi-channel devices\n // disambiguate with the camera number prefix (`ch2-main`, ...).\n const slot = ch.streamSlot === 1 ? 'main' : ch.streamSlot === 2 ? 'sub' : `slot-${ch.streamSlot}`\n return ch.cameraNumber === 1 ? `native:${slot}` : `ch${ch.cameraNumber}-${slot}`\n }\n\n private deriveStreamLabel(ch: HikvisionStreamingChannel): string {\n const slot = ch.streamSlot === 1 ? 'Main' : ch.streamSlot === 2 ? 'Sub' : `Slot ${ch.streamSlot}`\n return ch.cameraNumber === 1 ? slot : `Cam ${ch.cameraNumber} — ${slot}`\n }\n\n private fallbackChannels(): readonly HikvisionStreamingChannel[] {\n // No discovery worked — assume single camera with main + sub.\n return [\n { id: '101', cameraNumber: 1, streamSlot: 1, enabled: true, codec: null, width: null, height: null, maxBitrateKbps: null },\n { id: '102', cameraNumber: 1, streamSlot: 2, enabled: true, codec: null, width: null, height: null, maxBitrateKbps: null },\n ]\n }\n\n private buildRtspUrl(channelId: string): string {\n const username = encodeURIComponent(this.config.get('username') ?? 'admin')\n const password = encodeURIComponent(this.config.get('password'))\n const host = this.config.get('host')\n const transport = this.config.get('rtspTransport') ?? 'unicast'\n // Hikvision's canonical RTSP URL — port 554 is the firmware default\n // and is independent of the ISAPI port. Surfacing it as a separate\n // field would let installations behind a NAT remap; deferred until\n // a real user reports the need.\n return `rtsp://${username}:${password}@${host}:554/Streaming/Channels/${channelId}?transportmode=${transport}`\n }\n\n /**\n * Compat shim for legacy `ICameraDevice.getStreamSources()` consumers.\n * Real broker traffic flows through `publishCameraStream` (above) —\n * this just reflects the same data into the legacy shape.\n */\n async getStreamSources(): Promise<readonly StreamSourceEntry[]> {\n const out: StreamSourceEntry[] = []\n for (const p of this.published.values()) {\n const profileHint = p.channel.streamSlot === 1 ? 'high' : p.channel.streamSlot === 2 ? 'low' : undefined\n out.push({\n id: p.camStreamId,\n label: this.deriveStreamLabel(p.channel),\n protocol: 'rtsp' as const,\n url: p.rtspUrl,\n ...(profileHint ? { profileHint } : {}),\n ...(p.channel.width && p.channel.height ? { resolution: { width: p.channel.width, height: p.channel.height } } : {}),\n })\n }\n return out\n }\n\n // ── Alarm stream subscription ────────────────────────────────────\n\n private ensureAlarmSubscription(): void {\n if (this.alarmController) return\n const isapi = this.ensureClient()\n this.alarmController = subscribeAlarms(isapi, {\n onConnected: () => {\n this.alarmReconnectAttempts = 0\n this.markOnline(true)\n this.ctx.logger.info('alarm stream connected', { tags: { deviceId: this.id } })\n },\n onEvent: (ev) => this.handleAlarmEvent(ev),\n onError: (err) => {\n this.ctx.logger.warn('alarm stream error — will reconnect', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n this.alarmController = null\n this.scheduleAlarmReconnect()\n },\n })\n }\n\n private scheduleAlarmReconnect(): void {\n if (this.alarmReconnectTimer) return\n const attempts = ++this.alarmReconnectAttempts\n // Exponential backoff capped at 60s — same shape as Reolink.\n const delayMs = Math.min(60_000, 1_000 * 2 ** Math.min(attempts, 6))\n this.alarmReconnectTimer = setTimeout(() => {\n this.alarmReconnectTimer = null\n this.ensureAlarmSubscription()\n }, delayMs)\n }\n\n private handleAlarmEvent(ev: HikvisionAlarmEvent): void {\n // Resolve the channel id reported by the camera back to one of OUR\n // camStream ids so downstream consumers (analytics, motion-overlay)\n // see consistent identifiers. Fall back to `native:main` when the\n // camera doesn't report a channel (single-camera devices often\n // omit the field entirely).\n const camStreamId = this.resolveAlarmCamStreamId(ev.channelId)\n\n const isMotion = ev.type === 'vmd' || ev.type === 'motiondetection'\n const isSmart =\n ev.type === 'linedetection' ||\n ev.type === 'fielddetection' ||\n ev.type === 'regionentrance' ||\n ev.type === 'regionexiting' ||\n ev.type === 'attendedbaggage' ||\n ev.type === 'unattendedbaggage'\n\n if (isMotion) {\n this.handleMotionEvent(ev, camStreamId)\n return\n }\n if (isSmart) {\n this.emitSmartDetection(ev, camStreamId)\n // Smart detection events also imply motion — surface both so a\n // pipeline gating on `motion-active` doesn't miss the burst.\n this.handleMotionEvent(ev, camStreamId)\n return\n }\n // Unknown event types are logged at debug level and otherwise\n // dropped; we don't want to flood the event bus with vendor noise.\n this.ctx.logger.debug('hikvision: unhandled alarm event', {\n tags: { deviceId: this.id },\n meta: { type: ev.type, state: ev.state },\n })\n }\n\n private resolveAlarmCamStreamId(channelId: string | null): string {\n if (!channelId) return 'native:main'\n // Look up by reported channel id against our published map. Hikvision\n // alarm events emit the parent camera channel (`1`, `2`, ...) on\n // some firmwares and the full encoded id (`101`) on others — accept\n // both.\n for (const p of this.published.values()) {\n if (p.channel.id === channelId) return p.camStreamId\n if (String(p.channel.cameraNumber) === channelId) return p.camStreamId\n }\n return 'native:main'\n }\n\n /** EventSource tag used on every emit — declared once so `tags`\n * on consumers stay consistent (alert-center correlates events back\n * to the originating addon via this `addonId`). */\n private get eventSource(): { type: string; id: number; addonId: string; deviceId: number } {\n return { type: 'device', id: this.id, addonId: HIKVISION_ADDON_ID, deviceId: this.id }\n }\n\n private handleMotionEvent(ev: HikvisionAlarmEvent, camStreamId: string): void {\n const now = ev.observedAt\n const wasInactive = (this.lastMotionAt.get(camStreamId) ?? 0) === 0\n this.lastMotionAt.set(camStreamId, now)\n\n if (wasInactive) {\n // Edge transition: emit the activated event + flip the cap state.\n // The runner subscribes to `MotionOnMotionChanged` (`source:\n // 'onboard'`) and drives its phase machine off it; we don't\n // write the motion slice directly because the pipeline-runner\n // is the canonical writer (see `motion.cap.ts` doc).\n this.ctx.eventBus.emit(createEvent(\n EventCategory.MotionOnMotionChanged,\n this.eventSource,\n { deviceId: this.id, detected: true, timestamp: now, source: 'onboard' },\n ))\n try {\n this.setCapSlice(motionCapability, {\n detected: true,\n lastDetectedAt: now,\n autoClearAfterMs: HikvisionCamera.MOTION_INACTIVE_AFTER_MS,\n })\n } catch { /* swallow */ }\n }\n\n // Reset the inactivity timer.\n const existing = this.motionInactiveTimers.get(camStreamId)\n if (existing) clearTimeout(existing)\n const timer = setTimeout(() => {\n const clearedAt = Date.now()\n this.lastMotionAt.set(camStreamId, 0)\n this.motionInactiveTimers.delete(camStreamId)\n this.ctx.eventBus.emit(createEvent(\n EventCategory.MotionOnMotionChanged,\n this.eventSource,\n { deviceId: this.id, detected: false, timestamp: clearedAt, source: 'onboard' },\n ))\n try {\n const lastDetectedAt = this.state.motion?.lastDetectedAt ?? clearedAt\n this.setCapSlice(motionCapability, {\n detected: false,\n lastDetectedAt,\n autoClearAfterMs: HikvisionCamera.MOTION_INACTIVE_AFTER_MS,\n })\n } catch { /* swallow */ }\n }, HikvisionCamera.MOTION_INACTIVE_AFTER_MS)\n this.motionInactiveTimers.set(camStreamId, timer)\n }\n\n private emitSmartDetection(ev: HikvisionAlarmEvent, _camStreamId: string): void {\n // `_camStreamId` is currently advisory — the canonical\n // `detection.camera-native` payload is camera-scoped, not\n // stream-scoped, mirroring how Reolink emits AI pushes. We pass\n // it in so future per-stream detection routing can drop in\n // without re-threading the call site.\n const className = ev.aiClass ? (HIKVISION_AI_CLASS_MAP[ev.aiClass.toLowerCase()] ?? ev.aiClass.toLowerCase()) : null\n if (!className) return\n this.ctx.eventBus.emit(createEvent(\n EventCategory.DetectionCameraNative,\n this.eventSource,\n {\n cameraId: this.id,\n source: 'onboard',\n detections: [{ class: className, timestamp: ev.observedAt }],\n },\n ))\n }\n\n // ── Driver-authored settings UI ──────────────────────────────────\n\n override getSettingsUISchema(): ConfigUISchemaWithValues {\n // Tab/section taxonomy is shared with the Reolink driver — see\n // `addon-provider-reolink/src/reolink-camera.ts`. Section order\n // and ids are kept identical so the operator sees the same layout\n // regardless of which vendor a given camera uses. Vendor-specific\n // fields live inside their respective sections; sections that\n // don't apply to a vendor (e.g. VCA on Reolink) are simply\n // omitted on that side.\n const osdValues = this.osdSnapshotForUi()\n const schema: ConfigUISchema = {\n sections: [\n // ── Connection (lives under the General tab) ────────────────\n //\n // The driver-authored Connection card stays on the well-known\n // `general` tab (alongside the device-manager identity card and\n // any other addon contributions targeting `general`). We don't\n // promote it to its own top-level tab — connection settings are\n // edited rarely and pairing them with the always-visible General\n // tab keeps the camera-detail nav lean.\n {\n id: 'connection',\n tab: 'general',\n title: 'Connection',\n description: 'ISAPI HTTP(S) endpoint + RTSP transport + Digest credentials.',\n columns: 2,\n fields: [\n { type: 'text', key: 'host', label: 'Camera IP', required: true, placeholder: '192.168.1.100' },\n { type: 'number', key: 'port', label: 'Port', default: 80, min: 1, max: 65535, step: 1 },\n { type: 'boolean', key: 'https', label: 'Use HTTPS', default: false, style: 'switch' },\n {\n type: 'select',\n key: 'rtspTransport',\n label: 'RTSP transport',\n default: 'unicast',\n options: [\n { value: 'unicast', label: 'Unicast (default)' },\n { value: 'multicast', label: 'Multicast (rare)' },\n ],\n },\n { type: 'text', key: 'username', label: 'Username', default: 'admin' },\n { type: 'password', key: 'password', label: 'Password', required: true, showToggle: true },\n ],\n },\n // ── Motion ──────────────────────────────────────────────────\n {\n id: 'motion',\n tab: 'motion',\n title: 'On-camera motion (VMD)',\n description:\n 'On-camera VMD (Video Motion Detection). Hikvision firmware advertises sensitivity in 20-unit increments (0/20/40/60/80/100); values are clamped on push. Disabling stops the camera from emitting motion frames on the alarm stream.',\n columns: 2,\n fields: [\n {\n type: 'boolean',\n key: 'motionEnabled',\n label: 'Motion detection',\n description: 'Master switch for on-camera VMD.',\n default: true,\n style: 'switch',\n span: 2,\n },\n {\n type: 'slider',\n key: 'motionSensitivity',\n label: 'Sensitivity',\n description: 'Higher = triggers on smaller motion. Restricted to firmware-advertised 20-unit increments — slider snaps to 0/20/40/60/80/100.',\n min: 0,\n max: 100,\n // Discrete step matches Hikvision firmware grid; the\n // slider thumb + number-input + stepper buttons all\n // snap to multiples of 20.\n step: 20,\n default: 60,\n showValue: true,\n showStepper: true,\n span: 2,\n },\n ],\n },\n // ── Image tuning (color / WDR / IR-cut) ─────────────────────\n ...(this.config.get('deviceCache')?.imageSnapshot\n ? [(() => {\n const snap = this.config.get('deviceCache')!.imageSnapshot!\n return {\n id: 'image',\n tab: 'image' as const,\n title: 'Image',\n description:\n 'Color levels (brightness/contrast/saturation/sharpness 0..100), WDR (high-dynamic range), IR-cut filter day/night switch. Pushed per-resource to `/ISAPI/Image/channels/1/{color,WDR,IrcutFilter}`.',\n columns: 2 as const,\n fields: [\n {\n type: 'slider' as const,\n key: 'imageBrightness',\n label: 'Brightness',\n min: 0, max: 100, step: 5,\n default: snap.brightness ?? 50,\n showValue: true,\n showStepper: true,\n },\n {\n type: 'slider' as const,\n key: 'imageContrast',\n label: 'Contrast',\n min: 0, max: 100, step: 5,\n default: snap.contrast ?? 50,\n showValue: true,\n showStepper: true,\n },\n {\n type: 'slider' as const,\n key: 'imageSaturation',\n label: 'Saturation',\n min: 0, max: 100, step: 5,\n default: snap.saturation ?? 50,\n showValue: true,\n showStepper: true,\n },\n {\n type: 'slider' as const,\n key: 'imageSharpness',\n label: 'Sharpness',\n min: 0, max: 100, step: 5,\n default: snap.sharpness ?? 50,\n showValue: true,\n showStepper: true,\n },\n {\n type: 'boolean' as const,\n key: 'imageWdrEnabled',\n label: 'WDR',\n description: 'Wide Dynamic Range — balances scenes with bright + dark areas (e.g. backlit doorways). Costs a bit of frame rate / detail.',\n default: snap.wdrEnabled ?? false,\n style: 'switch' as const,\n span: 2 as const,\n },\n {\n type: 'slider' as const,\n key: 'imageWdrLevel',\n label: 'WDR level',\n description: 'Higher = more aggressive HDR fusion; can introduce halos in extreme scenes.',\n min: 0, max: 100, step: 10,\n default: snap.wdrLevel ?? 50,\n showValue: true,\n showStepper: true,\n span: 2 as const,\n showWhen: { field: 'imageWdrEnabled', equals: true },\n },\n {\n type: 'select' as const,\n key: 'imageIrcutMode',\n label: 'Day/Night',\n description: 'IR-cut filter behaviour. `auto` switches based on ambient light; `day`/`night` force colour or B&W; `schedule` uses the camera-side schedule (configure on the camera UI).',\n default: snap.ircutMode ?? 'auto',\n options: [\n { value: 'auto', label: 'Auto (light-driven)' },\n { value: 'day', label: 'Day (force colour)' },\n { value: 'night', label: 'Night (force B&W + IR)' },\n { value: 'schedule', label: 'Schedule (camera-side)' },\n ],\n span: 2 as const,\n },\n ],\n }\n })()]\n : []),\n // ── Light (supplemental IR / white) ─────────────────────────\n ...(this.config.get('deviceCache')?.hasSupplementalLight === true\n ? [(() => {\n const cache = this.config.get('deviceCache')!\n const modes = cache.lightSupportedModes ?? []\n const snap = cache.lightSnapshot ?? {}\n const hasWhite = cache.lightHasWhiteLight === true\n const supportsRegulator = (snap.brightnessRegulatorMode ?? null) !== null\n return {\n id: 'light',\n tab: 'light' as const,\n title: hasWhite ? 'Supplemental light' : 'IR illuminator',\n description: hasWhite\n ? 'Mode + per-mode brightness for the camera\\'s supplemental light. White-light cameras also expose a Switch / Light child device — operator can toggle on/off there; this tab tunes the firmware-side mode + regulator.'\n : 'IR-only night-vision illuminator. Auto mode lets the camera drive brightness off the light sensor; manual pins it to the slider value. No child Switch device — the IR LEDs are auto-driven, an operator on/off toggle would be misleading.',\n columns: 2 as const,\n fields: [\n {\n type: 'select' as const,\n key: 'lightMode',\n label: 'Mode',\n description: 'Active illumination mode. `close` turns the supplemental light off entirely; `irLight` enables the IR LEDs; white-light modes (when available) drive visible illumination.',\n default: snap.mode ?? cache.lightDefaultMode ?? 'irLight',\n options: modes.map((m) => ({\n value: m,\n label: friendlyHikvisionLightMode(m),\n })),\n },\n ...(supportsRegulator\n ? [{\n type: 'select' as const,\n key: 'lightBrightnessRegulator',\n label: 'Brightness regulator',\n description: '`auto` lets the camera adjust brightness off its built-in light sensor; `manual` pins it to the operator-set value below.',\n default: snap.brightnessRegulatorMode ?? 'auto',\n options: [\n { value: 'auto', label: 'Auto (sensor-driven)' },\n { value: 'manual', label: 'Manual (operator-set)' },\n ],\n }]\n : []),\n ...(modes.includes('irLight')\n ? [{\n type: 'slider' as const,\n key: 'lightIrBrightness',\n label: 'IR brightness',\n description: 'IR LED brightness 0..100. Honoured by firmware only when `mode = irLight` AND regulator is `manual`. Higher = brighter IR field, more visible bloom on bright surfaces.',\n min: 0,\n max: 100,\n step: 5,\n default: snap.irLightBrightness ?? 100,\n showValue: true,\n showStepper: true,\n span: 2 as const,\n }]\n : []),\n ...(hasWhite\n ? [{\n type: 'slider' as const,\n key: 'lightWhiteBrightness',\n label: 'White-light brightness',\n description: 'White-light brightness 0..100. Honoured by firmware only when a white-light mode is active.',\n min: 0,\n max: 100,\n step: 5,\n default: snap.whiteLightBrightness ?? 100,\n showValue: true,\n showStepper: true,\n span: 2 as const,\n }]\n : []),\n ],\n }\n })()]\n : []),\n // ── Two-way audio mode (always visible) ─────────────────────\n // Mirrors Scrypted's `twoWayAudio` choice (None | Hikvision |\n // ONVIF). Drives `intercomEnabled()` → cap registration +\n // `DeviceFeature.TwoWayAudio` advertisement. Always rendered\n // (independent of `audioCapabilities`) so the operator can\n // disable intercom on cameras whose probe failed or force the\n // ISAPI path on cameras whose channel list 404s.\n {\n id: 'two-way-audio-mode',\n tab: 'audio' as const,\n title: 'Two-way audio',\n description:\n 'Source for the intercom (talk) channel. `auto` enables when the `/ISAPI/System/TwoWayAudio/channels` probe finds at least one channel; force `isapi` when the probe 404s but the camera accepts audioData writes; `off` disables intercom registration entirely. `onvif` is reserved for the upcoming Profile-T backchannel path and currently behaves like `off`.',\n columns: 1 as const,\n fields: [\n {\n type: 'select' as const,\n key: 'twoWayAudioMode',\n label: 'Mode',\n default: 'auto',\n options: [\n { value: 'auto', label: `Auto${this.config.get('deviceCache')?.hasIntercom === true ? ' (probe: enabled)' : ' (probe: disabled)'}` },\n { value: 'isapi', label: 'Hikvision (ISAPI, force-on)' },\n { value: 'onvif', label: 'ONVIF (Profile-T, not yet implemented)' },\n { value: 'off', label: 'Off' },\n ],\n },\n ],\n },\n // ── Audio ───────────────────────────────────────────────────\n ...(this.config.get('deviceCache')?.audioCapabilities\n ? [{\n id: 'audio',\n tab: 'audio' as const,\n title: 'Two-way audio channel',\n description:\n 'Codec, speaker volume, noise reduction, and input source for the camera\\'s `/ISAPI/System/TwoWayAudio/channels/1` endpoint. Settings here drive both the camera-side speaker output and the intercom playback path.',\n columns: 2 as const,\n fields: [\n {\n type: 'select' as const,\n key: 'audioCompressionType',\n label: 'Audio codec',\n description: 'Negotiated codec for both intercom (push) and any speaker prompts. G.711 µ-law has the broadest interop; G.722.1 + AAC give better quality on supporting firmware.',\n default: '',\n options: [\n { value: '', label: 'Firmware default' },\n ...(this.config.get('deviceCache')?.audioCapabilities?.audioCodecs ?? [])\n .map((v) => ({ value: v, label: v })),\n ],\n },\n {\n type: 'select' as const,\n key: 'audioInputType',\n label: 'Input source',\n description: 'Mic-in (built-in microphone) vs Line-in (rear-panel TRS). Cameras without a line input only advertise MicIn here.',\n default: '',\n options: [\n { value: '', label: 'Firmware default' },\n ...(this.config.get('deviceCache')?.audioCapabilities?.audioInputTypes ?? [])\n .map((v) => ({ value: v, label: v })),\n ],\n },\n {\n type: 'slider' as const,\n key: 'audioSpeakerVolume',\n label: 'Speaker volume',\n description: 'Camera output volume. Affects intercom playback and any audio-alarm sounds the linkage fires.',\n min: this.config.get('deviceCache')?.audioCapabilities?.speakerVolumeMin ?? 0,\n max: this.config.get('deviceCache')?.audioCapabilities?.speakerVolumeMax ?? 100,\n step: 5,\n default: 80,\n showValue: true,\n showStepper: true,\n span: 2 as const,\n },\n ...(this.config.get('deviceCache')?.audioCapabilities?.supportsNoiseReduction\n ? [{\n type: 'boolean' as const,\n key: 'audioNoiseReduction',\n label: 'Noise reduction',\n description: 'Camera-side noise filter. Helpful in noisy environments; can clip soft speech if too aggressive.',\n default: true,\n style: 'switch' as const,\n span: 2 as const,\n }]\n : []),\n ],\n }]\n : []),\n // ── Streaming (one section per detected channel) ────────────\n ...buildStreamingSections(this.config.get('deviceCache')?.streamSnapshots),\n // ── Alarms (motion-event linkage outputs) ──────────────────\n {\n id: 'alarms',\n tab: 'alarms',\n title: 'Motion linkage',\n description:\n 'Camera-side reactions wired to the VMD trigger. Each toggle adds/removes an `<EventTriggerNotification>` entry on `/ISAPI/Event/triggers/VMD-1`. Methods we don\\'t manage (record, email, ftp, …) survive the round-trip — operators can keep richer linkages set up via the camera UI.',\n columns: 2,\n fields: [\n {\n type: 'boolean',\n key: 'alarmBeep',\n label: 'Audio beep',\n description: 'Camera fires its built-in beep speaker on motion.',\n default: false,\n style: 'switch',\n },\n {\n type: 'boolean',\n key: 'alarmWhiteLight',\n label: 'White-light flash',\n description: 'Strobes the supplemental white light on motion. Only effective on cameras with a controllable white-light LED (ColorVu / G2-LSU).',\n default: false,\n style: 'switch',\n },\n {\n type: 'boolean',\n key: 'alarmIoOutput',\n label: 'IO relay',\n description: 'Closes the camera\\'s alarm-output relay on motion. Wire your siren / external alarm panel here.',\n default: false,\n style: 'switch',\n },\n {\n type: 'boolean',\n key: 'alarmCenter',\n label: 'Notification center',\n description: 'Push a notification to the Hikvision IVMS / Hik-Connect cloud on motion. Independent of camstack\\'s own event pipeline.',\n default: false,\n style: 'switch',\n },\n ],\n },\n // ── OSD (readonly view of the current overlay state) ───────\n {\n id: 'osd',\n tab: 'osd',\n title: 'OSD overlays',\n description:\n 'On-screen overlay snapshot from `/ISAPI/System/Video/inputs/channels/{cam}/overlays`. Edits live on the universal \"OSD\" cap surface — these fields just mirror what the camera is currently displaying.',\n columns: 2,\n fields: [\n {\n type: 'boolean',\n key: '_osdDateTimeEnabled',\n label: 'Date/time visible',\n description: 'Camera firmware-rendered timestamp — format lives on the camera-side time settings page.',\n default: false,\n style: 'switch',\n readonlyField: true,\n },\n {\n type: 'boolean',\n key: '_osdChannelNameEnabled',\n label: 'Channel name visible',\n description: 'Tracks the camera `deviceName`. Toggle visibility via the OSD cap.',\n default: false,\n style: 'switch',\n readonlyField: true,\n },\n {\n type: 'text',\n key: '_osdTextOverlay1',\n label: 'Text overlay #1',\n description: 'Free-text overlay slot 1 — edit via OSD cap.',\n readonlyField: true,\n span: 2,\n },\n {\n type: 'text',\n key: '_osdTextOverlay2',\n label: 'Text overlay #2',\n readonlyField: true,\n span: 2,\n },\n {\n type: 'text',\n key: '_osdTextOverlay3',\n label: 'Text overlay #3',\n readonlyField: true,\n span: 2,\n },\n {\n type: 'text',\n key: '_osdTextOverlay4',\n label: 'Text overlay #4',\n readonlyField: true,\n span: 2,\n },\n ],\n },\n // ── Advanced (VCA + debug) ──────────────────────────────────\n {\n id: 'advanced',\n tab: 'advanced',\n title: 'Advanced',\n description: 'VCA resource allocation + ISAPI tracing. Use temporarily — debug flags add log volume; VCA changes require a camera reboot.',\n columns: 1,\n fields: [\n {\n type: 'select',\n key: 'vcaResource',\n label: 'VCA resource',\n description:\n 'Trade smart-detection compute for the third stream slot. `Smart` and `Face snap` disable the third stream; `Close` frees the slot but turns off on-camera AI. Switching modes triggers an automatic camera reboot.',\n default: 'smart',\n options: [\n { value: 'smart', label: 'Smart detection (default)' },\n { value: 'facesnap', label: 'Face snap' },\n { value: 'close', label: 'Closed (third stream available)' },\n ],\n },\n {\n type: 'boolean',\n key: 'debugGeneral',\n label: 'General ISAPI logs',\n description: 'Request/response status, retries, digest re-challenges.',\n default: false,\n style: 'switch',\n },\n {\n type: 'multiselect',\n key: 'debugFlags',\n label: 'Trace categories',\n default: [],\n options: [\n { value: 'debugIsapi', label: 'ISAPI request/response bodies' },\n { value: 'traceAlarmStream', label: 'Alarm stream parser (per-block)' },\n { value: 'traceSnapshot', label: 'Snapshot timing + bytes' },\n ],\n },\n ],\n },\n ],\n }\n const values: Record<string, unknown> = {\n host: this.config.get('host'),\n port: this.config.get('port'),\n https: this.config.get('https') ?? false,\n username: this.config.get('username'),\n password: this.config.get('password'),\n rtspTransport: this.config.get('rtspTransport') ?? 'unicast',\n motionEnabled: this.config.get('motionEnabled') ?? true,\n motionSensitivity: this.config.get('motionSensitivity') ?? 60,\n vcaResource: this.config.get('vcaResource') ?? 'smart',\n audioCompressionType: this.config.get('audioCompressionType') ?? '',\n audioSpeakerVolume: this.config.get('audioSpeakerVolume') ?? 80,\n audioNoiseReduction: this.config.get('audioNoiseReduction') ?? true,\n audioInputType: this.config.get('audioInputType') ?? '',\n // Per-channel streaming synthetic keys — one entry per detected\n // channel (`streamCh101_codec`, `streamCh102_fps`, …). Hydrated\n // from the persisted operator overrides (`streamChannels[id]`)\n // first, falling back to the firmware-reported snapshot\n // (`deviceCache.streamSnapshots[id]`) when the operator hasn't\n // touched a field. The `extractStreamPatches` helper walks\n // these keys back out at save-time.\n ...streamingSyntheticValues(\n this.config.get('streamChannels') ?? {},\n this.config.get('deviceCache')?.streamSnapshots,\n ),\n alarmBeep: this.config.get('alarmBeep') ?? false,\n alarmWhiteLight: this.config.get('alarmWhiteLight') ?? false,\n alarmIoOutput: this.config.get('alarmIoOutput') ?? false,\n alarmCenter: this.config.get('alarmCenter') ?? false,\n imageBrightness: this.config.get('imageBrightness')\n ?? this.config.get('deviceCache')?.imageSnapshot?.brightness\n ?? 50,\n imageContrast: this.config.get('imageContrast')\n ?? this.config.get('deviceCache')?.imageSnapshot?.contrast\n ?? 50,\n imageSaturation: this.config.get('imageSaturation')\n ?? this.config.get('deviceCache')?.imageSnapshot?.saturation\n ?? 50,\n imageSharpness: this.config.get('imageSharpness')\n ?? this.config.get('deviceCache')?.imageSnapshot?.sharpness\n ?? 50,\n imageWdrEnabled: this.config.get('imageWdrEnabled')\n ?? this.config.get('deviceCache')?.imageSnapshot?.wdrEnabled\n ?? false,\n imageWdrLevel: this.config.get('imageWdrLevel')\n ?? this.config.get('deviceCache')?.imageSnapshot?.wdrLevel\n ?? 50,\n imageIrcutMode: this.config.get('imageIrcutMode')\n ?? this.config.get('deviceCache')?.imageSnapshot?.ircutMode\n ?? 'auto',\n lightMode: this.config.get('lightMode')\n ?? this.config.get('deviceCache')?.lightSnapshot?.mode\n ?? this.config.get('deviceCache')?.lightDefaultMode\n ?? 'irLight',\n lightIrBrightness: this.config.get('lightIrBrightness')\n ?? this.config.get('deviceCache')?.lightSnapshot?.irLightBrightness\n ?? 100,\n lightWhiteBrightness: this.config.get('lightWhiteBrightness')\n ?? this.config.get('deviceCache')?.lightSnapshot?.whiteLightBrightness\n ?? 100,\n lightBrightnessRegulator: this.config.get('lightBrightnessRegulator')\n ?? this.config.get('deviceCache')?.lightSnapshot?.brightnessRegulatorMode\n ?? 'auto',\n debugGeneral: this.config.get('debugGeneral') ?? false,\n debugFlags: this.config.get('debugFlags') ?? [],\n ...osdValues,\n }\n return hydrateSchema(schema, values)\n }\n\n /** Project the in-memory OSD snapshot into the readonly fields the\n * `osd` section displays. Returns sentinel values (`false` /\n * empty string) until the first refresh succeeds — drives the\n * \"—\" placeholder in the readonly form widget. */\n private osdSnapshotForUi(): Record<string, unknown> {\n const snap = this.osdSnapshot\n const out: Record<string, unknown> = {\n _osdDateTimeEnabled: snap?.dateTime.enabled ?? false,\n _osdChannelNameEnabled: snap?.channelName.enabled ?? false,\n }\n const slots = ['1', '2', '3', '4']\n for (let i = 0; i < slots.length; i++) {\n const slotId = slots[i]!\n const item = snap?.textOverlays.find((t) => t.id === slotId)\n const text = item?.enabled ? (item.text ?? '') : ''\n out[`_osdTextOverlay${i + 1}`] = text\n }\n return out\n }\n\n override async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n // Strip readonly OSD shadow fields before persisting (they're\n // populated from `this.osdSnapshot`, not from the operator). Then\n // pull every `streamCh{id}_*` synthetic key out into per-channel\n // wire patches and a single `streamChannels` record update — the\n // schema persists the latter; the former drives the per-channel\n // ISAPI PUT below.\n const osdStripped = stripOsdShadowFields(patch)\n const { cleaned, wirePatches: streamWirePatches, storeOverrides: streamStoreOverrides } =\n extractStreamPatches(osdStripped)\n // Merge per-channel store overrides into the existing record so\n // un-touched channels keep their persisted values. setAll already\n // shallow-merges at the top level, but `streamChannels` is itself\n // a record — rebuild explicitly.\n if (Object.keys(streamStoreOverrides).length > 0) {\n const currentChannels = this.config.get('streamChannels') ?? {}\n const merged: Record<string, Record<string, unknown>> = {}\n for (const [id, prev] of Object.entries(currentChannels)) merged[id] = { ...prev }\n for (const [id, next] of Object.entries(streamStoreOverrides)) {\n merged[id] = { ...(merged[id] ?? {}), ...next }\n }\n cleaned.streamChannels = merged\n }\n await this.config.setAll(cleaned)\n const typed = cleaned as Partial<HikvisionCameraConfig>\n\n // ── Connection-shape change → reconnect ─────────────────────\n if (\n typed.host !== undefined\n || typed.port !== undefined\n || typed.https !== undefined\n || typed.username !== undefined\n || typed.password !== undefined\n ) {\n await this.disconnectAll()\n await this.publishToBroker().catch((err) => {\n this.ctx.logger.warn('publishToBroker after settings patch failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n // ── Motion detection push ───────────────────────────────────\n if (typed.motionEnabled !== undefined || typed.motionSensitivity !== undefined) {\n try {\n const isapi = this.ensureClient()\n await isapi.setMotionDetection(DEFAULT_CAMERA_NUMBER, {\n enabled: typed.motionEnabled,\n sensitivityLevel: typed.motionSensitivity,\n })\n } catch (err) {\n this.ctx.logger.warn('motion detection settings push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n // ── Audio settings push (TwoWayAudio channel 1) ─────────────\n if (\n typed.audioCompressionType !== undefined\n || typed.audioSpeakerVolume !== undefined\n || typed.audioNoiseReduction !== undefined\n || typed.audioInputType !== undefined\n ) {\n try {\n const isapi = this.ensureClient()\n // Drop empty-string sentinel from select fields — they mean\n // \"use firmware default\" → don't push the tag at all so the\n // camera keeps its current value.\n const audioPatch: {\n audioCompressionType?: string\n speakerVolume?: number\n noiseReduction?: boolean\n audioInputType?: string\n } = {}\n if (typeof typed.audioCompressionType === 'string' && typed.audioCompressionType.length > 0) {\n audioPatch.audioCompressionType = typed.audioCompressionType\n }\n if (typeof typed.audioSpeakerVolume === 'number') {\n audioPatch.speakerVolume = typed.audioSpeakerVolume\n }\n if (typeof typed.audioNoiseReduction === 'boolean') {\n audioPatch.noiseReduction = typed.audioNoiseReduction\n }\n if (typeof typed.audioInputType === 'string' && typed.audioInputType.length > 0) {\n audioPatch.audioInputType = typed.audioInputType\n }\n if (Object.keys(audioPatch).length > 0) {\n await isapi.setAudioSettings('1', audioPatch)\n }\n } catch (err) {\n this.ctx.logger.warn('audio settings push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n // ── Image tuning push (color + sharpness + WDR + IR-cut) ───\n if (\n typed.imageBrightness !== undefined\n || typed.imageContrast !== undefined\n || typed.imageSaturation !== undefined\n ) {\n try {\n const isapi = this.ensureClient()\n await isapi.setImageColor(DEFAULT_CAMERA_NUMBER, {\n brightness: typed.imageBrightness,\n contrast: typed.imageContrast,\n saturation: typed.imageSaturation,\n })\n } catch (err) {\n this.ctx.logger.warn('image color push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (typed.imageSharpness !== undefined) {\n try {\n const isapi = this.ensureClient()\n await isapi.setImageSharpness(DEFAULT_CAMERA_NUMBER, typed.imageSharpness)\n } catch (err) {\n this.ctx.logger.warn('image sharpness push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (typed.imageWdrEnabled !== undefined || typed.imageWdrLevel !== undefined) {\n try {\n const isapi = this.ensureClient()\n await isapi.setImageWdr(DEFAULT_CAMERA_NUMBER, {\n enabled: typed.imageWdrEnabled,\n level: typed.imageWdrLevel,\n })\n } catch (err) {\n this.ctx.logger.warn('image WDR push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n // ── Supplemental light push ────────────────────────────────\n if (\n typed.lightMode !== undefined\n || typed.lightIrBrightness !== undefined\n || typed.lightWhiteBrightness !== undefined\n || typed.lightBrightnessRegulator !== undefined\n ) {\n try {\n const isapi = this.ensureClient()\n await isapi.setSupplementLight(DEFAULT_CAMERA_NUMBER, {\n mode: typed.lightMode,\n irLightBrightness: typed.lightIrBrightness,\n whiteLightBrightness: typed.lightWhiteBrightness,\n brightnessRegulatorMode: typed.lightBrightnessRegulator,\n })\n } catch (err) {\n this.ctx.logger.warn('supplemental light push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n if (typed.imageIrcutMode !== undefined) {\n try {\n const isapi = this.ensureClient()\n await isapi.setIrcutFilter(DEFAULT_CAMERA_NUMBER, { mode: typed.imageIrcutMode })\n } catch (err) {\n this.ctx.logger.warn('image IR-cut push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n // ── Per-channel streaming push ─────────────────────────────\n // One PUT per touched channel; the operator can edit main + sub\n // simultaneously and we'll fan out without holding off on the\n // others when one channel rejects.\n for (const [id, channelPatch] of Object.entries(streamWirePatches)) {\n try {\n const isapi = this.ensureClient()\n await isapi.setStreamingChannel(id, channelPatch)\n } catch (err) {\n this.ctx.logger.warn('streaming channel push failed', {\n tags: { deviceId: this.id, channelId: id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n // ── Motion linkage (VMD-{cam}) push ────────────────────────\n if (\n typed.alarmBeep !== undefined\n || typed.alarmWhiteLight !== undefined\n || typed.alarmIoOutput !== undefined\n || typed.alarmCenter !== undefined\n ) {\n try {\n const isapi = this.ensureClient()\n const linkagePatch: Parameters<typeof isapi.setMotionTriggerLinkage>[1] = {}\n if (typeof typed.alarmBeep === 'boolean') linkagePatch.beep = typed.alarmBeep\n if (typeof typed.alarmWhiteLight === 'boolean') linkagePatch.whiteLight = typed.alarmWhiteLight\n if (typeof typed.alarmIoOutput === 'boolean') linkagePatch.ioOutput = typed.alarmIoOutput\n if (typeof typed.alarmCenter === 'boolean') linkagePatch.center = typed.alarmCenter\n if (Object.keys(linkagePatch).length > 0) {\n await isapi.setMotionTriggerLinkage(DEFAULT_CAMERA_NUMBER, linkagePatch)\n }\n } catch (err) {\n this.ctx.logger.warn('motion linkage push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n // ── VCA resource → triggers reboot when changed ─────────────\n if (typed.vcaResource !== undefined) {\n try {\n const isapi = this.ensureClient()\n const changed = await isapi.setVcaResource(DEFAULT_CAMERA_NUMBER, typed.vcaResource)\n if (changed) {\n this.ctx.logger.info('VCA resource mode changed — rebooting camera to apply', {\n tags: { deviceId: this.id },\n meta: { vcaResource: typed.vcaResource },\n })\n // Hikvision firmware needs a reboot for the new VCA\n // allocation to take effect. The lib's reboot endpoint\n // ACKs immediately and the camera reconnects on its own\n // alarm-stream backoff.\n await isapi.reboot().catch((err) => {\n this.ctx.logger.warn('VCA-triggered reboot failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n } catch (err) {\n this.ctx.logger.warn('VCA resource push failed', {\n tags: { deviceId: this.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n\n // ── Teardown ─────────────────────────────────────────────────────\n\n override async removeDevice(): Promise<void> {\n this.ctx.logger.info('Removing Hikvision camera', { tags: { deviceId: this.id } })\n if (this.osdPollTimer) {\n clearInterval(this.osdPollTimer)\n this.osdPollTimer = null\n }\n await this.disconnectAll()\n for (const camStreamId of this.published.keys()) {\n try {\n await this.ctx.api.streamBroker.retractCameraStream.mutate({ deviceId: this.id, camStreamId })\n } catch (err) {\n this.ctx.logger.debug('retractCameraStream failed', {\n tags: { deviceId: this.id, camStreamId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n this.published.clear()\n }\n\n private async disconnectAll(): Promise<void> {\n if (this.alarmReconnectTimer) {\n clearTimeout(this.alarmReconnectTimer)\n this.alarmReconnectTimer = null\n }\n if (this.alarmController) {\n try { this.alarmController.abort() } catch { /* swallow */ }\n this.alarmController = null\n }\n for (const t of this.motionInactiveTimers.values()) clearTimeout(t)\n this.motionInactiveTimers.clear()\n this.lastMotionAt.clear()\n this.isapi = null\n this.markOnline(false)\n }\n}\n\n/**\n * Drop the underscore-prefixed OSD shadow fields a settings save\n * round-trips back. The form controller submits every displayed field\n * regardless of `readonlyField`, so the camera-driver `applySettings`\n * gets `_osdDateTimeEnabled` etc. mixed in with the real keys. The\n * schema doesn't define those keys → `config.setAll` would reject the\n * patch on Zod parse. Stripping them here keeps the form layer\n * unchanged at the cost of a tiny per-save filter.\n */\n/** Render Hikvision's terse `supplementLightMode` tokens into operator-\n * friendly labels. Unknown modes pass through verbatim. */\nfunction friendlyHikvisionLightMode(mode: string): string {\n switch (mode) {\n case 'close': return 'Off'\n case 'irLight': return 'IR only (night-vision)'\n case 'whiteLight': return 'White light'\n case 'colorVuWhiteLight': return 'ColorVu White Light'\n case 'manual': return 'Manual (operator-driven)'\n case 'mixed': return 'Mixed (IR + white)'\n case 'auto': return 'Auto'\n case 'schedule': return 'Scheduled'\n case 'eventTrigger': return 'Event-triggered'\n default: return mode\n }\n}\n\nfunction stripOsdShadowFields(patch: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(patch)) {\n if (k.startsWith('_osd')) continue\n out[k] = v\n }\n return out\n}\n\n/** Per-channel stream field key prefix. The form uses synthetic flat\n * keys (`streamCh101_codec`, `streamCh102_fps`, …) because the schema\n * stores the per-channel overrides as a record (`streamChannels[id]`).\n * This regex extracts (channelId, fieldName) from a synthetic key. */\nconst STREAM_KEY_RE = /^streamCh(\\d+)_(codec|fps|qualityType|vbrCap|cbrBitrate|fixedQuality|gov|smartCodec)$/\n\ninterface StreamSnapshotEntry {\n readonly channelName?: string | null\n readonly videoCodecType?: string | null\n readonly maxFrameRate?: number | null\n readonly videoQualityControlType?: 'VBR' | 'CBR' | null\n readonly vbrUpperCap?: number | null\n readonly vbrUpperCapMin?: number\n readonly vbrUpperCapMax?: number\n readonly constantBitRate?: number | null\n readonly constantBitRateMin?: number\n readonly constantBitRateMax?: number\n readonly fixedQuality?: number | null\n readonly govLength?: number | null\n readonly govLengthMin?: number\n readonly govLengthMax?: number\n readonly smartCodecEnabled?: boolean\n readonly resolutionWidth?: number | null\n readonly resolutionHeight?: number | null\n readonly supportedFrameRates?: readonly number[]\n}\n\n/**\n * Render-friendly label for a Hikvision streaming channel id. Hikvision\n * encodes ids as `{cam}{slot}` (e.g. `101` = cam 1 main, `102` = sub,\n * `103` = third). We derive the label from the slot rather than the\n * camera-supplied `<channelName>` — most firmwares set channelName to\n * the camera's display name (`\"Driveway\"`) so it's redundant with the\n * device-detail page header AND identical across every channel of the\n * same camera. The slot-based label is what operators expect (Main /\n * Sub / Third). The raw channelName is the last-resort fallback for\n * non-standard slot encodings.\n */\nfunction streamChannelLabel(id: string, channelName: string | null): string {\n if (id.length >= 2) {\n const slot = id.slice(-2)\n if (slot === '01') return 'Main stream'\n if (slot === '02') return 'Sub stream'\n if (slot === '03') return 'Third stream'\n }\n if (channelName && channelName.trim().length > 0) return channelName.trim()\n return `Channel ${id}`\n}\n\n/**\n * Build one settings section per probed streaming channel. The form's\n * schema is dynamic (channels vary per device) so this is invoked at\n * `getSettingsUISchema()` time off the persisted snapshot map.\n *\n * Each section uses synthetic keys (`streamCh{id}_*`) — the dispatcher\n * unpacks them back into `streamChannels[id].{field}` before\n * `config.setAll`. Bounds come straight from the firmware-advertised\n * `min`/`max` attributes captured at probe.\n */\nfunction buildStreamingSections(\n snapshots: Record<string, StreamSnapshotEntry> | undefined,\n): readonly import('@camstack/types').ConfigSection[] {\n if (!snapshots || Object.keys(snapshots).length === 0) return []\n // Stable order: numeric ascending so main → sub → third.\n const sortedIds = Object.keys(snapshots).sort((a, b) => {\n const na = Number(a); const nb = Number(b)\n return Number.isFinite(na) && Number.isFinite(nb) ? na - nb : a.localeCompare(b)\n })\n const sections: import('@camstack/types').ConfigSection[] = []\n for (const id of sortedIds) {\n const snap = snapshots[id]!\n // Bind firmware-advertised bounds to safe defaults so the UI\n // sliders always have concrete min/max even on legacy firmware\n // that doesn't advertise the range attributes.\n const vbrMin = snap.vbrUpperCapMin ?? 32\n const vbrMax = snap.vbrUpperCapMax ?? 16384\n const cbrMin = snap.constantBitRateMin ?? 32\n const cbrMax = snap.constantBitRateMax ?? 16384\n const govMin = snap.govLengthMin ?? 1\n const govMax = snap.govLengthMax ?? 400\n const title = streamChannelLabel(id, snap.channelName ?? null)\n const resolution = (snap.resolutionWidth && snap.resolutionHeight)\n ? `${snap.resolutionWidth}×${snap.resolutionHeight}`\n : null\n const description = `\\`/ISAPI/Streaming/channels/${id}\\`${resolution ? ` — ${resolution}` : ''}. Codec / bitrate / FPS / GOP. Range bounds come from firmware-advertised limits — values are clamped on push.`\n sections.push({\n id: `stream-${id}`,\n tab: 'streaming',\n title,\n description,\n columns: 2,\n fields: [\n {\n type: 'select',\n key: `streamCh${id}_codec`,\n label: 'Codec',\n description: 'H.265 ~50% less bandwidth at equivalent quality but higher CPU on the decoder side. H.264 broadest interop. Smart codec only valid when codec is `H.265`.',\n default: snap.videoCodecType ?? '',\n options: [\n { value: '', label: 'Firmware default' },\n { value: 'H.264', label: 'H.264' },\n { value: 'H.265', label: 'H.265' },\n ],\n },\n // FPS — driven off the firmware-advertised list when\n // `/dynamicCap` returned options; otherwise fall back to a\n // free-typed number for legacy firmware.\n ...(snap.supportedFrameRates && snap.supportedFrameRates.length > 0\n ? [{\n type: 'select' as const,\n key: `streamCh${id}_fps`,\n label: 'Frame rate',\n description: 'Frames per second — restricted to the values this channel advertises in `/dynamicCap`.',\n default: String(snap.maxFrameRate ?? snap.supportedFrameRates[0]),\n options: snap.supportedFrameRates.map((v) => ({ value: String(v), label: `${v} fps` })),\n } satisfies import('@camstack/types').ConfigField]\n : [{\n type: 'number' as const,\n key: `streamCh${id}_fps`,\n label: 'Frame rate',\n description: 'Frames per second. Default 25fps for PAL territories, 30 for NTSC.',\n default: snap.maxFrameRate ?? 25,\n min: 1,\n max: 60,\n step: 1,\n unit: 'fps',\n } satisfies import('@camstack/types').ConfigField]),\n {\n type: 'select',\n key: `streamCh${id}_qualityType`,\n label: 'Bitrate mode',\n description: 'VBR (variable) targets a quality level and bursts up to the cap during motion; CBR (constant) holds a fixed bitrate regardless of scene complexity.',\n default: snap.videoQualityControlType ?? 'VBR',\n options: [\n { value: 'VBR', label: 'VBR (variable)' },\n { value: 'CBR', label: 'CBR (constant)' },\n ],\n },\n {\n type: 'select',\n key: `streamCh${id}_fixedQuality`,\n label: 'VBR quality',\n description: 'Quality target for VBR mode (Hikvision uses a discrete 6-step ladder, not a 1-100 slider).',\n default: String(snap.fixedQuality ?? 60),\n options: [\n { value: '20', label: 'Lowest (20)' },\n { value: '30', label: 'Lower (30)' },\n { value: '40', label: 'Low (40)' },\n { value: '60', label: 'Medium (60)' },\n { value: '80', label: 'High (80)' },\n { value: '100', label: 'Highest (100)' },\n ],\n showWhen: { field: `streamCh${id}_qualityType`, equals: 'VBR' },\n },\n {\n type: 'slider',\n key: `streamCh${id}_vbrCap`,\n label: 'VBR cap (kbps)',\n description: `Upper bound on VBR bitrate during motion bursts. Camera firmware bound: ${vbrMin}..${vbrMax} kbps.`,\n min: vbrMin,\n max: vbrMax,\n step: 256,\n default: snap.vbrUpperCap ?? 4096,\n showValue: true,\n showStepper: true,\n unit: 'kbps',\n span: 2,\n showWhen: { field: `streamCh${id}_qualityType`, equals: 'VBR' },\n },\n {\n type: 'slider',\n key: `streamCh${id}_cbrBitrate`,\n label: 'CBR bitrate (kbps)',\n description: `Constant bitrate target. Hikvision range ${cbrMin}..${cbrMax} kbps.`,\n min: cbrMin,\n max: cbrMax,\n step: 256,\n default: snap.constantBitRate ?? 4096,\n showValue: true,\n showStepper: true,\n unit: 'kbps',\n span: 2,\n showWhen: { field: `streamCh${id}_qualityType`, equals: 'CBR' },\n },\n {\n type: 'slider',\n key: `streamCh${id}_gov`,\n label: 'I-frame interval',\n description: `Frames between keyframes (GovLength). Lower = better seek + faster recovery on packet loss; higher = better compression. Camera bound: ${govMin}..${govMax}.`,\n min: govMin,\n max: govMax,\n step: 5,\n default: snap.govLength ?? 50,\n showValue: true,\n showStepper: true,\n span: 2,\n },\n {\n type: 'boolean',\n key: `streamCh${id}_smartCodec`,\n label: 'Smart codec (H.265+)',\n description: 'Region-of-interest aware encoding — drops bitrate on static parts of the frame. H.265-only on most firmwares; toggling on H.264 streams may be a no-op.',\n default: snap.smartCodecEnabled,\n style: 'switch',\n span: 2,\n showWhen: { field: `streamCh${id}_codec`, notIn: ['', 'H.264'] },\n },\n ],\n })\n }\n return sections\n}\n\n/**\n * Walk a save patch and pull every `streamCh{id}_{field}` synthetic\n * key into a per-channel patch map. Returns the cleaned patch (with\n * the synthetic keys removed) PLUS the per-channel wire patches keyed\n * by channelId, ready to feed into `setStreamingChannel`. Operator\n * edits propagate to BOTH firmware (per-channel PUT) and device-store\n * (`streamChannels[id].field = value`).\n */\n/**\n * Build the synthetic key/value pairs for the Streaming sub-tabs at\n * `getSettingsUISchema()` time. Per channel id, emit\n * `streamCh{id}_{field}` keys hydrated from operator overrides first\n * (when present) → firmware snapshot (when not). The form's\n * controlled values reflect the last-saved operator value while\n * showing the firmware reality for un-edited fields.\n */\nfunction streamingSyntheticValues(\n overrides: Record<string, {\n codec?: string\n fps?: number\n qualityType?: 'VBR' | 'CBR'\n vbrCap?: number\n cbrBitrate?: number\n fixedQuality?: number\n gov?: number\n smartCodec?: boolean\n }>,\n snapshots: Record<string, StreamSnapshotEntry> | undefined,\n): Record<string, unknown> {\n if (!snapshots) return {}\n const out: Record<string, unknown> = {}\n for (const [id, snap] of Object.entries(snapshots)) {\n const ovr = overrides[id] ?? {}\n out[`streamCh${id}_codec`] = ovr.codec ?? snap.videoCodecType ?? ''\n // FPS hydration shape mirrors the field renderer: the form picks a\n // `select` (string-valued) when the firmware advertised a list,\n // otherwise a `number` input. Coerce so the controlled value\n // matches the renderer's expected type — mismatched types make\n // the field render its default and lose the operator's stored\n // override.\n const fpsValue = ovr.fps ?? snap.maxFrameRate ?? 25\n out[`streamCh${id}_fps`] = (snap.supportedFrameRates && snap.supportedFrameRates.length > 0)\n ? String(fpsValue)\n : fpsValue\n out[`streamCh${id}_qualityType`] = ovr.qualityType ?? snap.videoQualityControlType ?? 'VBR'\n out[`streamCh${id}_vbrCap`] = ovr.vbrCap ?? snap.vbrUpperCap ?? 4096\n out[`streamCh${id}_cbrBitrate`] = ovr.cbrBitrate ?? snap.constantBitRate ?? 4096\n out[`streamCh${id}_fixedQuality`] = String(ovr.fixedQuality ?? snap.fixedQuality ?? 60)\n out[`streamCh${id}_gov`] = ovr.gov ?? snap.govLength ?? 50\n out[`streamCh${id}_smartCodec`] = ovr.smartCodec ?? snap.smartCodecEnabled\n }\n return out\n}\n\nfunction extractStreamPatches(patch: Record<string, unknown>): {\n cleaned: Record<string, unknown>\n wirePatches: Record<string, Parameters<HikvisionIsapiClient['setStreamingChannel']>[1]>\n storeOverrides: Record<string, Record<string, unknown>>\n} {\n const cleaned: Record<string, unknown> = {}\n const wirePatches: Record<string, Parameters<HikvisionIsapiClient['setStreamingChannel']>[1]> = {}\n const storeOverrides: Record<string, Record<string, unknown>> = {}\n for (const [k, v] of Object.entries(patch)) {\n const m = STREAM_KEY_RE.exec(k)\n if (!m) {\n cleaned[k] = v\n continue\n }\n const id = m[1]!\n const field = m[2]!\n const wire = wirePatches[id] ?? {}\n const store = storeOverrides[id] ?? {}\n // Field-name remapping: the schema uses short keys\n // (codec/fps/qualityType/vbrCap/cbrBitrate/fixedQuality/gov/smartCodec)\n // but the wire setter takes the Hikvision-flavoured names.\n if (field === 'codec' && typeof v === 'string' && v.length > 0) {\n wire.videoCodecType = v\n store.codec = v\n }\n if (field === 'fps') {\n // Form sends a number from the free-typed input OR a string\n // from the select widget — coerce both into a finite integer\n // before pushing to the wire.\n const num = typeof v === 'number' ? v : Number(v)\n if (Number.isFinite(num)) {\n wire.maxFrameRate = num\n store.fps = num\n }\n }\n if (field === 'qualityType' && (v === 'VBR' || v === 'CBR')) {\n wire.videoQualityControlType = v\n store.qualityType = v\n }\n if (field === 'vbrCap' && typeof v === 'number') {\n wire.vbrUpperCap = v\n store.vbrCap = v\n }\n if (field === 'cbrBitrate' && typeof v === 'number') {\n wire.constantBitRate = v\n store.cbrBitrate = v\n }\n if (field === 'fixedQuality') {\n // Form select returns string — coerce to number.\n const num = typeof v === 'number' ? v : Number(v)\n if (Number.isFinite(num)) {\n wire.fixedQuality = num\n store.fixedQuality = num\n }\n }\n if (field === 'gov' && typeof v === 'number') {\n wire.govLength = v\n store.gov = v\n }\n if (field === 'smartCodec' && typeof v === 'boolean') {\n wire.smartCodecEnabled = v\n store.smartCodec = v\n }\n if (Object.keys(wire).length > 0) wirePatches[id] = wire\n if (Object.keys(store).length > 0) storeOverrides[id] = store\n }\n return { cleaned, wirePatches, storeOverrides }\n}\n","import { z } from 'zod'\n\n/**\n * Single source of truth for the addon id used in event-source tags +\n * `device.online`/`device.offline` payloads. Kept in sync with the\n * provider's `addonId` field manually — both must match for the\n * device-manager + alert-center to correlate events back to the\n * provider that produced them.\n */\nexport const HIKVISION_ADDON_ID = 'provider-hikvision'\n\n/**\n * Default ISAPI channel for single-camera devices. Hikvision encodes\n * channels as `{cameraNumber}{streamSlot}` — `101` is camera 1 / main\n * stream, `102` is camera 1 / sub-stream. Multi-channel NVRs use\n * `201` / `202` for camera 2 etc. The `DEFAULT_CAMERA_NUMBER`\n * is the offset we feed into stream-id derivation when no per-channel\n * routing context exists (probe, snapshot, single-camera devices).\n */\nexport const DEFAULT_CAMERA_NUMBER = 1\n\n/**\n * Hikvision smart-detection class strings observed in alarm XML.\n * Mapped to the loose camstack `CameraNativeDetection.className`\n * convention so other providers (Reolink, Frigate) emit the same\n * strings for the same physical concept.\n */\nexport const HIKVISION_AI_CLASS_MAP: Readonly<Record<string, string>> = {\n human: 'person',\n vehicle: 'vehicle',\n pedestrian: 'person',\n car: 'vehicle',\n animal: 'animal',\n face: 'face',\n}\n\n/**\n * Lib-side debug categories. Mirrors Reolink's `debugSocketLogs`\n * pattern — flips per-area request/response logging in the ISAPI\n * client without restarting the device. Each flag turns on a focused\n * trace stream in `HikvisionIsapiClient`.\n */\nexport const HikvisionDebugFlagSchema = z.enum([\n 'debugIsapi',\n 'traceAlarmStream',\n 'traceSnapshot',\n])\n\nexport type HikvisionDebugFlag = z.infer<typeof HikvisionDebugFlagSchema>\n\n/**\n * Persisted per-device cache of the last successful probe — saves a\n * round-trip on every reconnect by remembering whether the camera is\n * a single-channel or NVR, the model string, and the firmware\n * version. Mirrors Reolink's `deviceCache` blob — same purpose, same\n * \"trust but refresh on capability mismatch\" semantics.\n */\nexport const HikvisionDeviceCacheSchema = z.object({\n deviceType: z.enum(['camera', 'nvr']).optional(),\n model: z.string().optional(),\n serialNumber: z.string().optional(),\n firmwareVersion: z.string().optional(),\n /** Hikvision firmware version detail (`firmwareVersionInfo` field —\n * e.g. `B-R-G5-0`). Surfaced in the hardware metadata blob. */\n firmwareVersionInfo: z.string().optional(),\n /** ISAPI `<hardwareVersion>` — typically `0x0` on consumer units; kept\n * for completeness so the metadata sub-tab can show whatever the\n * firmware advertises. */\n hardwareVersion: z.string().optional(),\n /** ISAPI `<deviceType>` (e.g. `IPCamera`, `NVR`). Driver-side cache. */\n isapiDeviceType: z.string().optional(),\n /** Hardware MAC address (no separators, lowercased) from\n * `/ISAPI/System/Network/interfaces/1`. Drives `generateStableId` so\n * re-adding the same device by IP after a DHCP renewal lands on the\n * same row. */\n mac: z.string().optional(),\n channelCount: z.number().int().positive().optional(),\n hasPtz: z.boolean().optional(),\n hasSupplementalLight: z.boolean().optional(),\n /**\n * Modes the supplemental light advertises in\n * `/ISAPI/Image/channels/{cam}/supplementLight/capabilities`. Persisted\n * so the LightAccessory can render a \"mode\" select with the camera-\n * specific options (`colorVuWhiteLight`, `manual`, `irLight`, `mixed`,\n * `auto`, ...) without a fresh probe on every settings open.\n */\n lightSupportedModes: z.array(z.string()).optional(),\n /**\n * Default \"on\" mode picked at probe time. The LightAccessory uses this\n * as the schema default for its `mode` setting; operator-driven changes\n * persist on the accessory's own config row.\n */\n lightDefaultMode: z.string().optional(),\n /**\n * True when the supplemental light has at least one operator-driven\n * white-light mode (`colorVuWhiteLight`, `whiteLight`, `manual`, or\n * `mixed`). IR-only models (`irLight,close`) report false — the\n * camera's IR LEDs are auto-driven by the light sensor and there's\n * no meaningful operator switch, so we don't spawn a child Switch /\n * Light accessory device for them. Their tuning lives on the\n * parent's Light tab instead.\n */\n lightHasWhiteLight: z.boolean().optional(),\n /**\n * Live snapshot of the supplemental light state, refreshed at probe\n * time. Drives the parent Light tab's hydrated values. Each field\n * is optional — different firmware exposes different subsets.\n */\n lightSnapshot: z.object({\n mode: z.string().optional(),\n whiteLightBrightness: z.number().nullable().optional(),\n irLightBrightness: z.number().nullable().optional(),\n brightnessRegulatorMode: z.enum(['auto', 'manual']).nullable().optional(),\n }).optional(),\n hasAlarmIo: z.boolean().optional(),\n /**\n * Alarm input ports the camera enumerates via `/ISAPI/System/IO/inputs`.\n * Persisted so the SirenAccessory can render a \"port\" select with valid\n * ids on multi-input cameras; single-port devices stay on `1`.\n */\n alarmInputPorts: z.array(z.number().int().positive()).optional(),\n /**\n * True when the firmware exposes\n * `/ISAPI/PTZCtrl/channels/{cam}/smartTracking` (autotrack\n * subsystem). Drives `DeviceFeature.PtzAutotrack` + the\n * `ptz-autotrack` cap registration on the parent camera.\n */\n hasAutotrack: z.boolean().optional(),\n /** Whether the firmware accepts the `<duration>` knob — set at probe\n * time. Used by the cap's `setSettings` to decide whether to push\n * `stopDelaySeconds`. */\n autotrackSupportsDuration: z.boolean().optional(),\n /** Whether the firmware accepts the `<smartTrackingType>` knob. */\n autotrackSupportsType: z.boolean().optional(),\n /**\n * True when the camera advertises at least one TwoWayAudio channel\n * via `/ISAPI/System/TwoWayAudio/channels`. Drives\n * `DeviceFeature.TwoWayAudio` + the `intercom` cap registration.\n */\n hasIntercom: z.boolean().optional(),\n /** ISAPI codec the firmware reports for the talk channel (e.g.\n * `G.711ulaw`, `G.711alaw`). Persisted so the intercom session\n * doesn't have to re-discover on every open. Optional — discovery\n * re-runs at session start regardless. */\n intercomAudioCompressionType: z.string().optional(),\n /**\n * Audio TwoWayAudio capabilities snapshot from `/capabilities`.\n * Drives the Audio settings tab's select options (codec / input\n * type) + slider bounds (speaker volume) without re-probing on\n * every form open. Refreshed at every successful connect.\n */\n audioCapabilities: z.object({\n audioCodecs: z.array(z.string()).default([]),\n audioInputTypes: z.array(z.string()).default([]),\n speakerVolumeMin: z.number().int().default(0),\n speakerVolumeMax: z.number().int().default(100),\n supportsNoiseReduction: z.boolean().default(false),\n }).optional(),\n /**\n * Per-channel streaming snapshot keyed by Hikvision channel id\n * (`'101'` = camera-1 main, `'102'` = sub, `'103'` = third). The\n * camera's `/ISAPI/Streaming/channels` enumeration drives this list\n * — we probe every entry it returns. Drives the Stream settings\n * tab: per-channel sub-tab is rendered dynamically off this map's\n * keys. Range bounds (vbr/cbr/govLength) are firmware-advertised\n * via the same probe so each channel's slider honours its own\n * limits (sub-streams typically allow lower max bitrate).\n */\n /**\n * Image-tuning snapshot: color (brightness/contrast/saturation/\n * sharpness 0..100), WDR (enable + level), IR-cut filter mode\n * (day/night/auto/schedule). Probed on connect; drives the Image\n * settings tab's hydrated values. Each sub-field is optional —\n * legacy firmwares 404 on individual sub-resources; we record\n * what we got and the form falls back to safe defaults for the\n * rest.\n */\n imageSnapshot: z.object({\n brightness: z.number().nullable().optional(),\n contrast: z.number().nullable().optional(),\n saturation: z.number().nullable().optional(),\n sharpness: z.number().nullable().optional(),\n wdrEnabled: z.boolean().optional(),\n wdrLevel: z.number().nullable().optional(),\n ircutMode: z.string().optional(),\n }).optional(),\n streamSnapshots: z.record(z.string(), z.object({\n channelName: z.string().nullable().optional(),\n videoCodecType: z.string().nullable().optional(),\n maxFrameRate: z.number().nullable().optional(),\n videoQualityControlType: z.enum(['VBR', 'CBR']).nullable().optional(),\n vbrUpperCap: z.number().nullable().optional(),\n vbrUpperCapMin: z.number().optional(),\n vbrUpperCapMax: z.number().optional(),\n constantBitRate: z.number().nullable().optional(),\n constantBitRateMin: z.number().optional(),\n constantBitRateMax: z.number().optional(),\n fixedQuality: z.number().nullable().optional(),\n govLength: z.number().nullable().optional(),\n govLengthMin: z.number().optional(),\n govLengthMax: z.number().optional(),\n smartCodecEnabled: z.boolean().optional(),\n resolutionWidth: z.number().nullable().optional(),\n resolutionHeight: z.number().nullable().optional(),\n /** FPS values the firmware advertises in `/dynamicCap`, sorted\n * descending. Drives the FPS select on the Stream tab — when\n * empty (legacy firmware without dynamicCap) the form falls back\n * to a free-typed number input. */\n supportedFrameRates: z.array(z.number().int().positive()).optional(),\n })).optional(),\n probedAt: z.number().optional(),\n}).loose().optional()\n\nexport const hikvisionCameraSchema = z.object({\n // `name` is now base meta (DB column on `device-meta`, not in this\n // hardware-config schema). Read via `this.name` (resolved by\n // `BaseDevice` from `ctx.deviceMeta.name`); mutated via\n // `kernel.devices.setName(id, name)`.\n /** Camera IP or hostname (no scheme). */\n host: z.string().describe('Camera IP or hostname'),\n /**\n * ISAPI port. Default 80 for HTTP, 443 for HTTPS. Hikvision cameras\n * almost always ship with HTTP enabled on port 80; HTTPS on 443 is\n * common but optional.\n */\n port: z.number().int().positive().default(80).describe('ISAPI port (default 80, 443 for HTTPS)'),\n /**\n * When true, ISAPI requests use HTTPS with `rejectUnauthorized:false`\n * (Hikvision's default cert is self-signed). HTTP is the default\n * because it's what every camera ships with out of the box.\n */\n https: z.boolean().default(false),\n username: z.string().default('admin').describe('ISAPI user'),\n password: z.string().describe('ISAPI password'),\n /**\n * RTSP transport hint forwarded to the broker as `transportmode`.\n * Most installations use `unicast`; multicast needs explicit\n * camera-side configuration and is rare.\n */\n rtspTransport: z.enum(['unicast', 'multicast']).default('unicast'),\n /**\n * Two-way audio source. Hikvision firmware exposes intercom via\n * ISAPI sticky-PUT (`/ISAPI/System/TwoWayAudio/channels/...`); future\n * firmwares + ONVIF Profile T also support backchannel via RTSP\n * `Require: www.onvif.org/ver20/backchannel`. We default to `auto`\n * (probe → ISAPI when the channel list returns ≥1 entry, otherwise\n * stay disabled). Operator can force `'isapi'` to skip the probe or\n * `'off'` to disable intercom registration entirely. The `'onvif'`\n * mode is reserved for the upcoming Phase 2 backchannel support.\n */\n twoWayAudioMode: z.enum(['auto', 'isapi', 'onvif', 'off']).default('auto').describe('Two-way audio source (auto/isapi/onvif/off)'),\n /**\n * Motion detection master switch + sensitivity (0..100). Maps to\n * ISAPI `/ISAPI/System/Video/inputs/channels/{cam}/motionDetection`.\n * Disabling this stops the camera from emitting `<eventType>VMD</eventType>`\n * frames on the alarm stream — useful when an external pipeline owns\n * motion detection and the on-camera VMD is just noise.\n *\n * Sensitivity range: firmware-advertised (older builds use step=20 →\n * 5 discrete levels; newer builds use step=1 → continuous). The lib\n * clamps to the firmware's accepted range on PUT.\n */\n motionEnabled: z.boolean().default(true).describe('On-camera motion detection (VMD) master switch'),\n motionSensitivity: z.number().int().min(0).max(100).default(60)\n .describe('Motion detection sensitivity (0..100). Higher = triggers on smaller motion.'),\n /**\n * VCA (Video Content Analysis) resource allocation. Camera firmware\n * trades smart-detection compute for the third stream slot:\n * - `smart` — runs smart detection (motion-AI, line crossing,\n * region entrance/exit, intrusion). Third stream\n * slot DISABLED.\n * - `facesnap` — face capture mode. Third stream DISABLED.\n * - `close` — VCA off, third stream slot AVAILABLE.\n *\n * Switching modes triggers a camera reboot; the lib's\n * `setVcaResource` returns a flag the provider uses to surface a\n * notice to the operator instead of auto-rebooting.\n */\n vcaResource: z.enum(['smart', 'facesnap', 'close']).default('smart')\n .describe('VCA resource allocation (smart / facesnap / close)'),\n /**\n * Active audio codec on `/ISAPI/System/TwoWayAudio/channels/1`.\n * Operator picks from `deviceCache.audioCapabilities.audioCodecs`;\n * empty string = use whatever the firmware advertises as default.\n */\n audioCompressionType: z.string().default(''),\n /**\n * Speaker volume 0..100 (clamped to firmware-advertised\n * speakerVolumeMin/Max range on push). Used by the camera-side\n * audio output AND by the intercom's playback path.\n */\n audioSpeakerVolume: z.number().int().min(0).max(100).default(80),\n /** Hikvision noise-reduction toggle on the audio channel (`<noisereduce>`). */\n audioNoiseReduction: z.boolean().default(true),\n /**\n * Audio input source — `MicIn` for the built-in microphone,\n * `LineIn` for the rear-panel line input. Empty = firmware default.\n */\n audioInputType: z.string().default(''),\n /**\n * Per-channel operator overrides keyed by Hikvision channel id.\n * Each entry is partial — only the fields the operator has touched.\n * Empty / missing entries fall back to the firmware-reported snapshot\n * (`deviceCache.streamSnapshots`) at form-render and dispatch time.\n * Applied to firmware via `setStreamingChannel(id, patch)` per channel.\n */\n streamChannels: z.record(z.string(), z.object({\n codec: z.string().optional(),\n fps: z.number().int().min(1).max(120).optional(),\n qualityType: z.enum(['VBR', 'CBR']).optional(),\n vbrCap: z.number().int().min(0).optional(),\n cbrBitrate: z.number().int().min(0).optional(),\n fixedQuality: z.number().int().min(1).max(100).optional(),\n gov: z.number().int().min(1).optional(),\n smartCodec: z.boolean().optional(),\n })).default({}),\n /**\n * Motion-event linkage outputs. Operator toggles each independently;\n * the lib's `setMotionTriggerLinkage` reconciles the camera-side\n * notification-method list while preserving any methods we don't\n * manage (record, email, ftp, …).\n */\n alarmBeep: z.boolean().default(false),\n alarmWhiteLight: z.boolean().default(false),\n alarmIoOutput: z.boolean().default(false),\n alarmCenter: z.boolean().default(false),\n /**\n * Image tuning operator overrides. Each is optional — undefined\n * means \"leave firmware as-is\" (the form falls back to the probed\n * snapshot for display). Pushed to the matching ISAPI endpoint\n * (`/Image/channels/{cam}/color` / `/WDR` / `/IrcutFilter`).\n */\n imageBrightness: z.number().int().min(0).max(100).optional(),\n imageContrast: z.number().int().min(0).max(100).optional(),\n imageSaturation: z.number().int().min(0).max(100).optional(),\n imageSharpness: z.number().int().min(0).max(100).optional(),\n imageWdrEnabled: z.boolean().optional(),\n imageWdrLevel: z.number().int().min(0).max(100).optional(),\n imageIrcutMode: z.enum(['day', 'night', 'auto', 'schedule']).optional(),\n /**\n * Supplemental-light operator overrides — kept on the parent for\n * IR-only models (no accessory device). White-light models still\n * spawn the LightAccessory and tune via the cap; the parent fields\n * here drive the regulator + IR brightness even on those.\n */\n lightMode: z.string().optional(),\n lightIrBrightness: z.number().int().min(0).max(100).optional(),\n lightWhiteBrightness: z.number().int().min(0).max(100).optional(),\n lightBrightnessRegulator: z.enum(['auto', 'manual']).optional(),\n /**\n * Cached probe results — see `HikvisionDeviceCacheSchema`.\n */\n deviceCache: HikvisionDeviceCacheSchema,\n /**\n * General ISAPI debug logs (request/response status, retries).\n * Mirrors Reolink's `debugGeneral`.\n */\n debugGeneral: z.boolean().default(false),\n /**\n * Per-area trace flags — only flip what you need; each adds log\n * volume.\n */\n debugFlags: z.array(HikvisionDebugFlagSchema).default([]),\n})\n\nexport type HikvisionCameraConfig = z.infer<typeof hikvisionCameraSchema>\nexport type HikvisionDeviceCache = z.infer<typeof HikvisionDeviceCacheSchema>\n","import { Agent as HttpAgent, request as httpRequest, type ClientRequest, type IncomingMessage } from 'node:http'\nimport { Agent as HttpsAgent, request as httpsRequest } from 'node:https'\nimport type { IScopedLogger } from '@camstack/types'\nimport { buildAuthHeader, parseChallenge, type DigestState } from './digest-auth.js'\n\n/**\n * Hikvision cameras serve self-signed TLS by default. Reusing one\n * keep-alive agent per (scheme, rejectUnauthorized) pair reduces\n * connection churn for the alarm-stream + snapshot polls. The agents\n * are stateless and shared across all `HikvisionIsapiClient` instances.\n */\nconst httpAgent = new HttpAgent({ keepAlive: true })\nconst httpsAgent = new HttpsAgent({ keepAlive: true, rejectUnauthorized: false })\n\nexport interface HikvisionIsapiOptions {\n readonly host: string\n readonly port: number\n readonly https: boolean\n readonly username: string\n readonly password: string\n /** Per-request default timeout (ms) — applies to everything except\n * the long-poll alarm stream, which is unbounded. */\n readonly defaultTimeoutMs?: number\n}\n\nexport interface HikvisionDeviceInfo {\n readonly deviceName: string | null\n readonly deviceType: string | null\n readonly model: string | null\n readonly serialNumber: string | null\n readonly firmwareVersion: string | null\n readonly firmwareReleasedDate: string | null\n /** Hikvision-specific build label (`firmwareVersionInfo` —\n * e.g. `B-R-G5-0`). Optional in firmware ≤ V5.5. */\n readonly firmwareVersionInfo: string | null\n /** ISAPI `<hardwareVersion>` field — typically `0x0` on consumer units. */\n readonly hardwareVersion: string | null\n /** Raw deviceInfo XML payload. Useful for diagnostic dumps. */\n readonly raw: string\n}\n\n/** Host network interface info (`/ISAPI/System/Network/interfaces/1`).\n * Hikvision exposes one interface per physical NIC (cameras are\n * typically single-NIC); the first interface is canonical. */\nexport interface HikvisionNetworkInterfaceInfo {\n readonly mac: string | null\n readonly ip: string | null\n readonly subnetMask: string | null\n readonly defaultGateway: string | null\n readonly addressingType: string | null\n readonly speed: string | null\n readonly mtu: string | null\n}\n\n/**\n * Single TwoWayAudio channel descriptor as returned by\n * `/ISAPI/System/TwoWayAudio/channels`. Hikvision firmware ships a\n * subset of these per camera: typically channel `1` with µ-law or\n * A-law as `audioCompressionType`, sometimes G.722.1 / AAC on newer\n * firmware (we only act on G.711 today; anything else is surfaced as\n * the raw string so callers can decide whether to fall back to the\n * default codec).\n */\nexport interface HikvisionTwoWayAudioChannel {\n /** Channel id Hikvision uses in subsequent open/audioData/close URLs.\n * Almost always `'1'` for single-cam devices. */\n readonly id: string\n /** Wire codec — `G.711ulaw` / `G.711alaw` typical. Null when the\n * firmware response omitted the field (older builds). */\n readonly audioCompressionType: string | null\n /** Negotiated source sample rate (Hz). Null when not advertised. */\n readonly audioInputType?: string | null\n}\n\n/**\n * Handle returned by `openAudioDataStream`. Implements a minimal write/\n * end pair on the long-running PUT body. The `responsePromise` resolves\n * with the camera's HTTP response when the body ends (or rejects with\n * the underlying socket error). All methods are idempotent.\n */\nexport interface AudioDataStream {\n /** Push more raw codec bytes (e.g. G.711 µ-law) onto the wire. */\n write(buf: Buffer): boolean\n /** Close the request body. The PUT then finishes and the camera\n * finalises the talk session — pair with `closeTwoWayAudio` for the\n * channel-side teardown. */\n end(): void\n /** Resolves with the camera's response when the PUT completes;\n * rejects on socket error. Awaited by `stop()` for clean teardown. */\n readonly responsePromise: Promise<IncomingMessage>\n}\n\nexport interface HikvisionStreamingChannel {\n /** Channel id as Hikvision encodes it: `101`, `102`, `201`, ... */\n readonly id: string\n /** Logical camera number derived from the channel id (`101` → 1). */\n readonly cameraNumber: number\n /** Stream slot (`1` = main, `2` = sub, `3` = third). */\n readonly streamSlot: number\n readonly enabled: boolean\n readonly codec: string | null\n readonly width: number | null\n readonly height: number | null\n readonly maxBitrateKbps: number | null\n}\n\n/**\n * Minimal HTTP client for Hikvision ISAPI. Wraps Node's built-in\n * `fetch` with HTTP Digest Auth retry, keep-alive agent reuse, and\n * convenience methods for the endpoints the camstack provider needs:\n * deviceInfo (probe), snapshot, channel discovery, alarm stream.\n *\n * Stateful: caches the last digest challenge per realm so we issue one\n * auth round-trip per session instead of per request.\n */\nexport class HikvisionIsapiClient {\n private readonly opts: HikvisionIsapiOptions\n private readonly logger: IScopedLogger | null\n private readonly digest: DigestState = { nc: 0 }\n private readonly defaultTimeoutMs: number\n\n constructor(opts: HikvisionIsapiOptions, logger?: IScopedLogger) {\n this.opts = opts\n this.logger = logger ?? null\n this.defaultTimeoutMs = opts.defaultTimeoutMs ?? 10_000\n }\n\n /**\n * Resolve `https://host:port` (or `http://`) — the absolute origin\n * for every ISAPI URL. No path here.\n */\n get baseUrl(): string {\n const scheme = this.opts.https ? 'https' : 'http'\n return `${scheme}://${this.opts.host}:${this.opts.port}`\n }\n\n /**\n * Issue a single ISAPI request. Does the digest dance: first attempt\n * carries the cached challenge if any; on `401` we parse the new\n * challenge, retry once, and surface the second response (or its\n * error) to the caller.\n *\n * `init.signal` is honoured for cancellation. The `timeoutMs` knob is\n * a convenience over wiring an `AbortController` per call site —\n * pass `null` to disable the timeout (used by the alarm long-poll).\n */\n async request(uri: string, init: RequestInit & { timeoutMs?: number | null } = {}): Promise<Response> {\n const url = `${this.baseUrl}${uri}`\n const method = (init.method ?? 'GET').toUpperCase()\n const agent = this.opts.https ? httpsAgent : httpAgent\n\n const wrapInit = (authHeader: string | null): RequestInit => {\n const headers = new Headers(init.headers ?? {})\n if (authHeader) headers.set('Authorization', authHeader)\n // Node fetch passes `dispatcher` via undici, but the global\n // `fetch` in Node 22 also accepts the legacy `agent` option via\n // the typed request init when present. We thread it through as\n // an opaque property so the keep-alive agent kicks in.\n const out: RequestInit & { agent?: HttpAgent | HttpsAgent } = {\n ...init,\n method,\n headers,\n agent,\n }\n return out\n }\n\n const cachedAuth = buildAuthHeader({\n state: this.digest,\n method,\n uri,\n username: this.opts.username,\n password: this.opts.password,\n })\n\n // First attempt — with cached challenge if any.\n const ctrl = init.timeoutMs === null ? null : new AbortController()\n const timer = ctrl && init.timeoutMs !== null\n ? setTimeout(() => ctrl.abort(new Error(`ISAPI request timeout after ${init.timeoutMs ?? this.defaultTimeoutMs}ms: ${uri}`)), init.timeoutMs ?? this.defaultTimeoutMs)\n : null\n const signal = init.signal ?? ctrl?.signal\n let res: Response\n try {\n res = await fetch(url, { ...wrapInit(cachedAuth), ...(signal ? { signal } : {}) })\n } finally {\n if (timer) clearTimeout(timer)\n }\n\n if (res.status !== 401) return res\n\n // Refresh challenge from the 401 and retry exactly once.\n // Hikvision routinely cycles nonces every few minutes — that's\n // expected, not an error.\n const challenge = parseChallenge(res.headers.get('www-authenticate'))\n if (!challenge) {\n this.logger?.warn('hikvision-isapi: 401 without valid Digest challenge', { meta: { uri } })\n return res\n }\n this.digest.challenge = challenge\n this.digest.nc = 0\n\n // Drain the 401 body so the keep-alive socket is reusable.\n try { await res.body?.cancel() } catch { /* swallow */ }\n\n const retryAuth = buildAuthHeader({\n state: this.digest,\n method,\n uri,\n username: this.opts.username,\n password: this.opts.password,\n })\n const ctrl2 = init.timeoutMs === null ? null : new AbortController()\n const timer2 = ctrl2 && init.timeoutMs !== null\n ? setTimeout(() => ctrl2.abort(new Error(`ISAPI request timeout after ${init.timeoutMs ?? this.defaultTimeoutMs}ms: ${uri}`)), init.timeoutMs ?? this.defaultTimeoutMs)\n : null\n const signal2 = init.signal ?? ctrl2?.signal\n try {\n return await fetch(url, { ...wrapInit(retryAuth), ...(signal2 ? { signal: signal2 } : {}) })\n } finally {\n if (timer2) clearTimeout(timer2)\n }\n }\n\n /** GET text body (XML or plain). Throws on non-2xx. */\n async getText(uri: string, opts?: { timeoutMs?: number | null }): Promise<string> {\n const res = await this.request(uri, { method: 'GET', ...(opts?.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}) })\n if (!res.ok) throw new Error(`hikvision-isapi GET ${uri} → HTTP ${res.status}`)\n return res.text()\n }\n\n /** GET binary body. Throws on non-2xx. */\n async getBuffer(uri: string, opts?: { timeoutMs?: number | null }): Promise<Buffer> {\n const res = await this.request(uri, { method: 'GET', ...(opts?.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}) })\n if (!res.ok) throw new Error(`hikvision-isapi GET ${uri} → HTTP ${res.status}`)\n const ab = await res.arrayBuffer()\n return Buffer.from(ab)\n }\n\n /** PUT XML body. Throws on non-2xx. Returns response text (often a\n * ResponseStatus envelope). */\n async putXml(uri: string, xmlBody: string, opts?: { timeoutMs?: number | null }): Promise<string> {\n const res = await this.request(uri, {\n method: 'PUT',\n body: xmlBody,\n headers: { 'Content-Type': 'application/xml' },\n ...(opts?.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n })\n if (!res.ok) throw new Error(`hikvision-isapi PUT ${uri} → HTTP ${res.status}`)\n return res.text()\n }\n\n // ── Convenience endpoint wrappers ──────────────────────────────\n\n /**\n * `/ISAPI/System/deviceInfo` — primary probe. Every Hikvision\n * camera/NVR responds; the absence of this endpoint is what we use\n * to negative-rule a host out as Hikvision.\n */\n async getDeviceInfo(): Promise<HikvisionDeviceInfo> {\n const xml = await this.getText('/ISAPI/System/deviceInfo')\n return {\n deviceName: extractTag(xml, 'deviceName'),\n deviceType: extractTag(xml, 'deviceType'),\n model: extractTag(xml, 'model'),\n serialNumber: extractTag(xml, 'serialNumber'),\n firmwareVersion: extractTag(xml, 'firmwareVersion'),\n firmwareReleasedDate: extractTag(xml, 'firmwareReleasedDate'),\n firmwareVersionInfo: extractTag(xml, 'firmwareVersionInfo'),\n hardwareVersion: extractTag(xml, 'hardwareVersion'),\n raw: xml,\n }\n }\n\n /**\n * `/ISAPI/System/Network/interfaces/1` — host network interface info.\n * Hikvision cameras are single-NIC; interface id `1` is canonical.\n * Returns `null` for fields the firmware doesn't expose.\n *\n * Used by the addon's autodetect path to populate the durable MAC\n * identifier in `deviceCache.mac` BEFORE `generateStableId` runs, so\n * re-adding the same camera by IP lands on the same persisted row\n * even after a DHCP renewal. Mirrors Reolink's `getNetworkInfo`.\n */\n async getNetworkInterfaceInfo(interfaceId = '1'): Promise<HikvisionNetworkInterfaceInfo> {\n const xml = await this.getText(`/ISAPI/System/Network/interfaces/${interfaceId}`)\n // Hikvision wraps the addressing block in `<IPAddress>` (capital\n // I+A) — the inner host-IP tag is `<ipAddress>` (lowercase). The\n // generic case-insensitive `extractTag` would match the OUTER\n // wrapper first (returning the inner XML as the captured content,\n // which surfaced as raw XML in the Hardware sub-tab). Carve up the\n // document explicitly so each tag is matched in the right scope.\n //\n // The lowercase `<ipAddress>` ALSO appears inside `<DefaultGateway>`\n // — strip that sub-block before extracting the host IP so we don't\n // accidentally return the gateway address as the device's IP.\n const ipBlockMatch = /<IPAddress[^>]*>([\\s\\S]*?)<\\/IPAddress>/.exec(xml)\n const ipBlock = ipBlockMatch ? ipBlockMatch[1]! : ''\n const gatewayBlockMatch = /<DefaultGateway[^>]*>([\\s\\S]*?)<\\/DefaultGateway>/.exec(ipBlock)\n const gatewayBlock = gatewayBlockMatch ? gatewayBlockMatch[1]! : ''\n const ipBlockNoGateway = ipBlock.replace(/<DefaultGateway[^>]*>[\\s\\S]*?<\\/DefaultGateway>/, '')\n\n // Case-sensitive matchers — the wire payload uses precise casing\n // (`ipAddress`, `subnetMask`, `addressingType`, `MACAddress`,\n // `MTU`); accepting case variants would re-introduce the outer/\n // inner tag-name collision.\n const matchTag = (source: string, tag: string): string | null => {\n const re = new RegExp(`<${tag}>([^<]*)<\\\\/${tag}>`)\n const m = re.exec(source)\n return m ? m[1]!.trim() || null : null\n }\n\n const linkBlockMatch = /<Link[^>]*>([\\s\\S]*?)<\\/Link>/.exec(xml)\n const linkBlock = linkBlockMatch ? linkBlockMatch[1]! : xml\n\n return {\n mac: matchTag(linkBlock, 'MACAddress'),\n ip: matchTag(ipBlockNoGateway, 'ipAddress'),\n subnetMask: matchTag(ipBlockNoGateway, 'subnetMask'),\n defaultGateway: gatewayBlock ? matchTag(gatewayBlock, 'ipAddress') : null,\n addressingType: matchTag(ipBlockNoGateway, 'addressingType'),\n speed: matchTag(linkBlock, 'speed'),\n mtu: matchTag(linkBlock, 'MTU'),\n }\n }\n\n /**\n * `/ISAPI/System/reboot` — schedule a firmware reboot. Returns once\n * the camera has ACKed (it stays connected just long enough to\n * confirm; the actual reboot happens shortly after).\n */\n async reboot(): Promise<void> {\n await this.putXml('/ISAPI/System/reboot', '')\n }\n\n /**\n * `/ISAPI/Streaming/channels/{id}/picture?snapShotImageType=JPEG`\n * — single-shot JPEG capture. Channel id format is `{cam}{slot}`\n * (e.g. `101` = camera 1 main stream).\n */\n async getSnapshot(channelId: string): Promise<{ buffer: Buffer; contentType: string }> {\n const res = await this.request(`/ISAPI/Streaming/channels/${channelId}/picture?snapShotImageType=JPEG`, {\n method: 'GET',\n timeoutMs: 15_000,\n })\n if (!res.ok) throw new Error(`hikvision snapshot ch=${channelId} → HTTP ${res.status}`)\n // Hikvision firmware ships `Content-Type: image/jpeg; charset=\"UTF-8\"`\n // for binary JPEG bytes — strip every header parameter so consumers\n // can build a valid `data:<mime>;base64,...` URI.\n const rawCt = res.headers.get('content-type') ?? 'image/jpeg'\n const contentType = rawCt.split(';', 1)[0]!.trim() || 'image/jpeg'\n const ab = await res.arrayBuffer()\n return { buffer: Buffer.from(ab), contentType }\n }\n\n /**\n * `/ISAPI/Streaming/channels` — enumerate every (camera, stream\n * slot) pair the device exposes. Multi-channel NVRs return one entry\n * per channel × per slot. Older firmwares (DS-7600 series) lack this\n * endpoint and return 404; callers should fall back to a synthetic\n * `{cam}01`/`{cam}02` derivation in that case.\n */\n async listChannels(): Promise<readonly HikvisionStreamingChannel[]> {\n const xml = await this.getText('/ISAPI/Streaming/channels')\n return parseChannelsList(xml)\n }\n\n // ── Two-way audio (intercom) ──────────────────────────────────────\n //\n // Hikvision exposes a sticky-PUT intercom pattern at\n // `/ISAPI/System/TwoWayAudio/channels/{id}`:\n // 1. GET `/channels` → discover negotiated codec\n // 2. PUT `/channels/{id}/open` → arm the channel\n // 3. long-running PUT `/audioData` with chunked codec body\n // 4. PUT `/channels/{id}/close` → release the channel\n\n async listTwoWayAudioChannels(): Promise<readonly HikvisionTwoWayAudioChannel[]> {\n const xml = await this.getText('/ISAPI/System/TwoWayAudio/channels', { timeoutMs: 5_000 })\n return parseTwoWayAudioChannels(xml)\n }\n\n /**\n * `/ISAPI/System/TwoWayAudio/channels/{id}/capabilities` — read the\n * audio channel's supported set (codec list, input types, speaker\n * volume range, noise-reduction flag). Used to build the operator-\n * facing selects without hard-coding firmware-specific options. The\n * call returns `null` for cameras that don't expose two-way audio.\n */\n async getAudioCapabilities(channelId: string): Promise<HikvisionAudioCapabilities | null> {\n try {\n const xml = await this.getText(`/ISAPI/System/TwoWayAudio/channels/${channelId}/capabilities`)\n return parseAudioCapabilities(xml)\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/System/TwoWayAudio/channels/{id}` — read the current audio\n * channel configuration (active codec, speaker volume, noise\n * reduction, input type). Returns `null` when the camera doesn't\n * advertise the channel at all.\n */\n async getAudioSettings(channelId: string): Promise<HikvisionAudioSettings | null> {\n try {\n const xml = await this.getText(`/ISAPI/System/TwoWayAudio/channels/${channelId}`)\n return parseAudioSettings(xml)\n } catch {\n return null\n }\n }\n\n /**\n * Patch audio channel settings. Read-modify-write against the\n * camera's current XML so unknown fields (chip-specific tuning we\n * don't surface) are preserved verbatim. Drops keys not in the patch.\n */\n async setAudioSettings(\n channelId: string,\n patch: {\n audioCompressionType?: string\n speakerVolume?: number\n noiseReduction?: boolean\n audioInputType?: string\n },\n ): Promise<void> {\n const url = `/ISAPI/System/TwoWayAudio/channels/${channelId}`\n const current = await this.getText(url)\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.audioCompressionType !== undefined) {\n patches.push({ tag: 'audioCompressionType', value: escapeXml(patch.audioCompressionType) })\n }\n if (patch.speakerVolume !== undefined) {\n patches.push({ tag: 'speakerVolume', value: String(Math.max(0, Math.min(100, Math.round(patch.speakerVolume)))) })\n }\n if (patch.noiseReduction !== undefined) {\n patches.push({ tag: 'noisereduce', value: patch.noiseReduction ? 'true' : 'false' })\n }\n if (patch.audioInputType !== undefined) {\n patches.push({ tag: 'audioInputType', value: escapeXml(patch.audioInputType) })\n }\n if (patches.length === 0) return\n const next = applyOverlayFieldPatches(current, patches)\n await this.putXml(url, next)\n }\n\n async openTwoWayAudio(channelId: string): Promise<void> {\n const res = await this.request(`/ISAPI/System/TwoWayAudio/channels/${channelId}/open`, {\n method: 'PUT',\n timeoutMs: 5_000,\n })\n if (!res.ok) {\n throw new Error(`hikvision two-way-audio open ch=${channelId} → HTTP ${res.status}`)\n }\n try { await res.body?.cancel() } catch { /* swallow */ }\n }\n\n async closeTwoWayAudio(channelId: string): Promise<void> {\n const res = await this.request(`/ISAPI/System/TwoWayAudio/channels/${channelId}/close`, {\n method: 'PUT',\n timeoutMs: 5_000,\n })\n if (!res.ok) {\n throw new Error(`hikvision two-way-audio close ch=${channelId} → HTTP ${res.status}`)\n }\n try { await res.body?.cancel() } catch { /* swallow */ }\n }\n\n /**\n * Open the long-running `audioData` PUT. Returns a write/end pair\n * the caller pumps codec bytes into until they're done, then calls\n * `end()` + `closeTwoWayAudio` to release the channel.\n *\n * Why we drop down to `node:http` here:\n * - The `fetch` API in Node 22 supports a `ReadableStream` body\n * with `duplex:'half'`, but it buffers writes internally — fine\n * for general POSTs, lousy for low-latency audio (we want each\n * write to flush onto the wire immediately).\n * - We need to know when the camera ACKs / drops the connection\n * mid-stream so the orchestrator can abort the talk session;\n * `req.on('response')` + `req.on('error')` plumb that directly.\n *\n * Digest auth: relies on the cached challenge from the prior\n * `openTwoWayAudio()` call. If that succeeded the digest state is\n * primed, so the streaming PUT goes through on the first try. If\n * not, the camera 401s on this request and the caller should retry.\n */\n openAudioDataStream(channelId: string): AudioDataStream {\n const uri = `/ISAPI/System/TwoWayAudio/channels/${channelId}/audioData`\n const auth = buildAuthHeader({\n state: this.digest,\n method: 'PUT',\n uri,\n username: this.opts.username,\n password: this.opts.password,\n })\n const headers: Record<string, string> = {\n 'Content-Type': 'application/octet-stream',\n // Lying `Content-Length: 0` is what Hikvision firmware expects\n // for sticky two-way-audio — the body is read until the socket\n // closes regardless. Mirror Scrypted's reference impl.\n 'Content-Length': '0',\n }\n if (auth) headers['Authorization'] = auth\n\n const requestFn = this.opts.https ? httpsRequest : httpRequest\n const agent = this.opts.https ? httpsAgent : httpAgent\n const req: ClientRequest = requestFn({\n host: this.opts.host,\n port: this.opts.port,\n path: uri,\n method: 'PUT',\n headers,\n agent,\n ...(this.opts.https ? { rejectUnauthorized: false } : {}),\n })\n\n const responsePromise = new Promise<IncomingMessage>((resolve, reject) => {\n req.once('response', (res) => {\n // Drain so the socket can be reused.\n res.resume()\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve(res)\n } else {\n reject(new Error(`hikvision audioData ch=${channelId} → HTTP ${res.statusCode}`))\n }\n })\n req.once('error', (err) => reject(err))\n })\n\n let ended = false\n return {\n write(buf: Buffer): boolean {\n if (ended) return false\n return req.write(buf)\n },\n end(): void {\n if (ended) return\n ended = true\n try { req.end() } catch { /* swallow — already ended */ }\n },\n responsePromise,\n }\n }\n\n // ── PTZ ──────────────────────────────────────────────────────────\n //\n // Hikvision exposes PTZ at `/ISAPI/PTZCtrl/channels/{cam}/...`. The\n // capabilities endpoint reveals which axes the camera supports\n // (some are pan-only, some pan+tilt, fewer have zoom). All commands\n // address one camera channel — substream slot does not matter for\n // PTZ, only the parent camera number.\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/capabilities` — query supported\n * pan/tilt/zoom ranges. Returns `null` when the cam doesn't have\n * PTZ at all (404 / no `<PTZChanelCap>` block in the response).\n */\n async getPtzCapabilities(cameraNumber: number): Promise<HikvisionPtzCapabilities | null> {\n try {\n const xml = await this.getText(`/ISAPI/PTZCtrl/channels/${cameraNumber}/capabilities`)\n return parsePtzCapabilities(xml)\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/continuous` — start (or stop, with\n * zero-magnitude) a continuous PTZ move. Hikvision's speed range is\n * -100..+100 per axis; a zero-magnitude payload halts the active\n * motion (no separate /stop endpoint exists). Speed magnitude\n * controls direction + rate; the camera applies its own\n * acceleration profile.\n */\n async ptzContinuous(cameraNumber: number, command: { pan?: number; tilt?: number; zoom?: number }): Promise<void> {\n const pan = clampSpeed(command.pan ?? 0)\n const tilt = clampSpeed(command.tilt ?? 0)\n const zoom = clampSpeed(command.zoom ?? 0)\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><PTZData><pan>${pan}</pan><tilt>${tilt}</tilt><zoom>${zoom}</zoom></PTZData>`\n await this.putXml(`/ISAPI/PTZCtrl/channels/${cameraNumber}/continuous`, xml)\n }\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/presets` — list configured presets.\n * Each preset has a numeric `id`, an optional `presetName`, and\n * `enabled` flag — disabled presets are omitted from the result.\n */\n async listPtzPresets(cameraNumber: number): Promise<readonly HikvisionPtzPreset[]> {\n try {\n const xml = await this.getText(`/ISAPI/PTZCtrl/channels/${cameraNumber}/presets`)\n return parsePtzPresets(xml)\n } catch {\n return []\n }\n }\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/presets/{id}/goto` — move the\n * camera to a stored preset. The numeric id matches what\n * `listPtzPresets` returns; out-of-range / disabled ids return\n * HTTP 4xx and surface as a thrown error here.\n */\n async ptzGoToPreset(cameraNumber: number, presetId: string): Promise<void> {\n await this.putXml(`/ISAPI/PTZCtrl/channels/${cameraNumber}/presets/${presetId}/goto`, '')\n }\n\n // ── Supplemental light ────────────────────────────────────────────\n //\n // White-light spotlight (some Hikvision models include it as part\n // of the ColorVu lineup). Modes vary by model — we surface only\n // the on/off + brightness axis here; mode-switching is left to the\n // device's settings UI for advanced configuration.\n\n /**\n * `/ISAPI/Image/channels/{cam}/supplementLight/capabilities` —\n * enumerate supported light modes + brightness ranges. Returns\n * `null` when the camera doesn't have a supplemental light at all.\n */\n async getSupplementLightCapabilities(cameraNumber: number): Promise<HikvisionLightCapabilities | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/supplementLight/capabilities`)\n const modes = parseSupplementLightModes(xml)\n if (modes.length === 0) return null\n return { modes, raw: xml }\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/Image/channels/{cam}/supplementLight` — read current\n * light state (mode, brightness). `mode === 'close'` means the\n * light is off; any non-`close` mode is the \"on\" state.\n */\n async getSupplementLight(cameraNumber: number): Promise<HikvisionLightState | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/supplementLight`)\n return parseSupplementLightState(xml)\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/Image/channels/{cam}/supplementLight` — patch the light\n * config. Read-modify-write so we don't clobber sibling fields the\n * caller didn't explicitly touch (e.g. `mixedLightBrightnessRegulatMode`\n * stays `auto` when only `mode` is patched).\n *\n * - `mode`: `irLight` / `whiteLight` / `colorVuWhiteLight` / `manual` /\n * `close` — depends on the camera's advertised modes.\n * - `whiteLightBrightness` / `irLightBrightness`: 0..100 sliders, only\n * honoured by firmware when the matching mode is active.\n * - `brightnessRegulatorMode`: `auto` / `manual` — most ColorVu / IR\n * models support this knob to drive brightness off the camera-side\n * light sensor (`auto`) or pin to the operator-set brightness\n * (`manual`).\n */\n async setSupplementLight(\n cameraNumber: number,\n patch: {\n mode?: string\n whiteLightBrightness?: number\n irLightBrightness?: number\n brightnessRegulatorMode?: 'auto' | 'manual'\n },\n ): Promise<void> {\n const url = `/ISAPI/Image/channels/${cameraNumber}/supplementLight`\n const current = await this.getText(url)\n const clamp = (v: number): string => String(Math.max(0, Math.min(100, Math.round(v))))\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.mode !== undefined) patches.push({ tag: 'supplementLightMode', value: patch.mode })\n if (patch.whiteLightBrightness !== undefined) patches.push({ tag: 'whiteLightBrightness', value: clamp(patch.whiteLightBrightness) })\n if (patch.irLightBrightness !== undefined) patches.push({ tag: 'irLightBrightness', value: clamp(patch.irLightBrightness) })\n if (patch.brightnessRegulatorMode !== undefined) {\n patches.push({ tag: 'mixedLightBrightnessRegulatMode', value: patch.brightnessRegulatorMode })\n }\n if (patches.length === 0) return\n const next = applyOverlayFieldPatches(current, patches)\n await this.putXml(url, next)\n }\n\n // ── Alarm output / siren (GPIO-triggered) ─────────────────────────\n //\n // Hikvision routes the on-camera siren via the same alarm-input\n // mechanism used by external sensors. The actual audible/visual\n // response (siren tone, strobe) is configured at the camera-side\n // \"Linkage Method\" — this API only flips the input state, the\n // camera fires the linkage. Capability detection: the\n // `/System/IO/inputs` endpoint enumerates available alarm input\n // ports; a non-empty list means the camera has at least one wired\n // input we can flip.\n\n /**\n * `/ISAPI/System/IO/inputs` — enumerate alarm input ports. Returns\n * `null` when the camera has no alarm IO at all (404 or empty list).\n */\n async getAlarmInputs(): Promise<readonly HikvisionAlarmInput[] | null> {\n try {\n const xml = await this.getText('/ISAPI/System/IO/inputs')\n const inputs = parseAlarmInputs(xml)\n return inputs.length > 0 ? inputs : null\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/System/IO/inputs/{port}` — toggle the alarm input. `on=true`\n * fires the linkage configured at the camera; `on=false` releases it.\n * The port number defaults to 1 (the most common GPIO slot for the\n * built-in siren) but can be overridden when the camera has multiple\n * inputs.\n */\n async setAlarmInput(on: boolean, port = 1): Promise<void> {\n const enabled = on ? 'true' : 'false'\n const triggering = on ? 'low' : 'high'\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><IOPortData><enabled>${enabled}</enabled><triggering>${triggering}</triggering></IOPortData>`\n await this.putXml(`/ISAPI/System/IO/inputs/${port}`, xml)\n }\n\n // ── Smart Tracking (autotrack) ────────────────────────────────────\n //\n // Hikvision exposes the on-camera autotrack subsystem as\n // `/ISAPI/PTZCtrl/channels/{cam}/smartTracking`. The shape varies\n // by firmware: the bare `<SmartTracking><enabled/></SmartTracking>`\n // is universal; some firmwares add a `<duration>` (seconds the\n // camera keeps tracking after the target stops moving) and a few\n // expose `<smartTrackingType>` (auto / human / vehicle), but neither\n // is guaranteed.\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/smartTrackingCapabilities` — probe\n * for autotrack support. Returns `null` when the camera doesn't\n * advertise the feature (404 / empty response). Used at boot to\n * decide whether to register the `ptz-autotrack` cap.\n */\n async getSmartTrackingCapabilities(cameraNumber: number): Promise<HikvisionSmartTrackingCapabilities | null> {\n try {\n const xml = await this.getText(`/ISAPI/PTZCtrl/channels/${cameraNumber}/smartTrackingCapabilities`)\n if (!xml || xml.length === 0) return null\n const supportsDuration = /<duration[^>]*opt|<duration[^>]*<min|<duration>\\s*\\d+\\s*<\\/duration>/i.test(xml)\n const supportsType = /smartTrackingType/i.test(xml)\n return { supportsDuration, supportsType, raw: xml }\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/smartTracking` — read current\n * state. Returns `null` when unsupported. The `enabled` value is\n * the canonical on/off; `duration` and `trackingType` are\n * model-dependent.\n */\n async getSmartTracking(cameraNumber: number): Promise<HikvisionSmartTrackingState | null> {\n try {\n const xml = await this.getText(`/ISAPI/PTZCtrl/channels/${cameraNumber}/smartTracking`)\n const enabledTag = /<enabled>(true|false)<\\/enabled>/i.exec(xml)\n const enabledStr = enabledTag?.[1]\n if (!enabledStr) return null\n const durationStr = /<duration>\\s*(\\d+)\\s*<\\/duration>/i.exec(xml)?.[1]\n const typeStr = /<smartTrackingType>([^<]+)<\\/smartTrackingType>/i.exec(xml)?.[1]\n return {\n enabled: enabledStr.toLowerCase() === 'true',\n duration: durationStr !== undefined ? Number.parseInt(durationStr, 10) : null,\n trackingType: typeStr ?? null,\n }\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/PTZCtrl/channels/{cam}/smartTracking` — set autotrack on/off\n * + optional duration / type. Empty `trackingType` and `null` `duration`\n * are omitted from the payload (the camera keeps its current value).\n */\n async setSmartTracking(\n cameraNumber: number,\n params: { enabled: boolean; duration?: number; trackingType?: string },\n ): Promise<void> {\n const lines: string[] = [`<enabled>${params.enabled ? 'true' : 'false'}</enabled>`]\n if (params.duration !== undefined && Number.isFinite(params.duration)) {\n lines.push(`<duration>${Math.max(0, Math.round(params.duration))}</duration>`)\n }\n if (params.trackingType !== undefined && params.trackingType.length > 0) {\n lines.push(`<smartTrackingType>${params.trackingType}</smartTrackingType>`)\n }\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><SmartTracking>${lines.join('')}</SmartTracking>`\n await this.putXml(`/ISAPI/PTZCtrl/channels/${cameraNumber}/smartTracking`, xml)\n }\n\n // ── OSD / Video overlays ──────────────────────────────────────────\n //\n // Hikvision exposes three kinds of overlay under the channel's\n // `/overlays` endpoint:\n //\n // - `dateTimeOverlay` — single firmware-rendered timestamp\n // (text is generated, not editable)\n // - `channelNameOverlay` — single text line bound to the camera's\n // `deviceName`; we expose enable only\n // - `text/{id}` — up to 4-8 free-text overlays the\n // operator can edit\n //\n // Each has a per-element PUT endpoint that accepts only the relevant\n // sub-tree, so updates don't risk clobbering the rest of the config.\n // The cap layer (`osd`) projects these uniformly as `OsdOverlay` items.\n\n /**\n * `/ISAPI/System/Video/inputs/channels/{cam}/overlays` — read the\n * full overlay config in one shot. The cap router uses this to build\n * an `OsdOverlay[]` snapshot per camera. Returns `null` when the\n * camera doesn't expose overlays at all (404).\n */\n async getOverlayConfig(cameraNumber: number): Promise<HikvisionOverlayConfig | null> {\n try {\n const xml = await this.getText(`/ISAPI/System/Video/inputs/channels/${cameraNumber}/overlays`)\n return parseOverlayConfig(xml)\n } catch {\n return null\n }\n }\n\n /**\n * Patch a single text overlay (`/overlays/text/{id}`). Read-modify-\n * write: GETs the current XML, splices in the patched fields, and\n * PUTs the result back so we don't lose firmware-managed siblings\n * (positionX/Y, isPersistentText). `enabled` flips the visibility;\n * `text` rewrites `<displayText>` (Hikvision auto-trims leading/\n * trailing whitespace).\n */\n async setTextOverlay(\n cameraNumber: number,\n id: string,\n patch: { enabled?: boolean; text?: string },\n ): Promise<void> {\n const url = `/ISAPI/System/Video/inputs/channels/${cameraNumber}/overlays/text/${id}`\n const current = await this.getText(url)\n const next = applyOverlayFieldPatches(current, [\n ...(patch.enabled !== undefined ? [{ tag: 'enabled', value: patch.enabled ? 'true' : 'false' }] : []),\n ...(patch.text !== undefined ? [{ tag: 'displayText', value: escapeXml(patch.text) }] : []),\n ])\n await this.putXml(url, next)\n }\n\n /**\n * Patch the channel-name overlay (`/overlays/channelNameOverlay`).\n * Operator can only flip the `enabled` field — the displayed text\n * tracks the camera's `deviceName` and is owned by the camera-side\n * settings, not the overlay endpoint.\n */\n async setChannelNameOverlay(\n cameraNumber: number,\n patch: { enabled?: boolean },\n ): Promise<void> {\n if (patch.enabled === undefined) return\n const url = `/ISAPI/System/Video/inputs/channels/${cameraNumber}/overlays/channelNameOverlay`\n const current = await this.getText(url)\n const next = applyOverlayFieldPatches(current, [\n { tag: 'enabled', value: patch.enabled ? 'true' : 'false' },\n ])\n await this.putXml(url, next)\n }\n\n /**\n * Patch the date-time overlay (`/overlays/dateTimeOverlay`). Only\n * `enabled` is operator-relevant; format strings (`dateStyle`,\n * `timeStyle`) live on the camera-side time settings page and we\n * deliberately don't bind them via the cap.\n */\n async setDateTimeOverlay(\n cameraNumber: number,\n patch: { enabled?: boolean },\n ): Promise<void> {\n if (patch.enabled === undefined) return\n const url = `/ISAPI/System/Video/inputs/channels/${cameraNumber}/overlays/dateTimeOverlay`\n const current = await this.getText(url)\n const next = applyOverlayFieldPatches(current, [\n { tag: 'enabled', value: patch.enabled ? 'true' : 'false' },\n ])\n await this.putXml(url, next)\n }\n\n // ── Motion detection ───────────────────────────────────────────────\n\n /**\n * `/ISAPI/System/Video/inputs/channels/{cam}/motionDetection` — read\n * current motion config: enabled flag + sensitivity level + the\n * vendor-advertised range (min/max/step) so the UI can render a\n * slider with the correct ticks. Returns `null` when the camera\n * doesn't expose motion configuration (rare; covers IP camera firmware\n * variants without VMD).\n */\n async getMotionDetection(cameraNumber: number): Promise<HikvisionMotionDetection | null> {\n try {\n const xml = await this.getText(`/ISAPI/System/Video/inputs/channels/${cameraNumber}/motionDetection`)\n return parseMotionDetection(xml)\n } catch {\n return null\n }\n }\n\n /**\n * Patch motion detection (`enabled` and/or `sensitivityLevel`). Read-\n * modify-write — we splice the new values into the firmware's current\n * XML so siblings (motion-region grid, schedule) survive the round\n * trip. Sensitivity is clamped to the firmware-advertised\n * [min..max] before write.\n */\n async setMotionDetection(\n cameraNumber: number,\n patch: { enabled?: boolean; sensitivityLevel?: number },\n ): Promise<void> {\n const url = `/ISAPI/System/Video/inputs/channels/${cameraNumber}/motionDetection`\n const current = await this.getText(url)\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.enabled !== undefined) {\n patches.push({ tag: 'enabled', value: patch.enabled ? 'true' : 'false' })\n }\n if (patch.sensitivityLevel !== undefined) {\n patches.push({ tag: 'sensitivityLevel', value: String(Math.max(0, Math.min(100, Math.round(patch.sensitivityLevel)))) })\n }\n if (patches.length === 0) return\n const next = applyOverlayFieldPatches(current, patches)\n await this.putXml(url, next)\n }\n\n // ── VCA Resource (third-stream toggle) ─────────────────────────────\n //\n // VCA = Video Content Analysis. Hikvision lets the operator pick how\n // the camera allocates compute between smart-detection (`smart`),\n // face-snap (`facesnap`), or \"third stream available\" (`close`).\n // Switching modes requires a reboot — Scrypted's reference impl does\n // it implicitly; we surface an explicit reboot hook so the operator\n // is in control.\n\n async getVcaResource(cameraNumber: number): Promise<HikvisionVcaResourceMode | null> {\n try {\n const xml = await this.getText(`/ISAPI/System/Video/inputs/channels/${cameraNumber}/VCAResource`)\n const type = extractTag(xml, 'type')\n if (type === 'smart' || type === 'facesnap' || type === 'close') return type\n return null\n } catch {\n return null\n }\n }\n\n /**\n * Set the VCA resource mode. Returns `true` when the mode actually\n * changed (and therefore a reboot is required); `false` when the\n * camera was already in the requested mode (no-op).\n */\n async setVcaResource(cameraNumber: number, mode: HikvisionVcaResourceMode): Promise<boolean> {\n const current = await this.getVcaResource(cameraNumber)\n if (current === mode) return false\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><VCAResource version=\"2.0\" xmlns=\"http://www.hikvision.com/ver20/XMLSchema\"><type>${mode}</type></VCAResource>`\n await this.putXml(`/ISAPI/System/Video/inputs/channels/${cameraNumber}/VCAResource`, xml)\n return true\n }\n\n // ── Streaming channels (codec / bitrate / FPS / GOP) ────────────\n\n /**\n * `/ISAPI/Streaming/channels/{channelId}` — read a single streaming\n * channel's full XML (codec + resolution + bitrate + GOP + quality\n * mode + audio enable). Returns `null` on 404.\n */\n async getStreamingChannel(channelId: string): Promise<HikvisionStreamingChannelConfig | null> {\n try {\n const xml = await this.getText(`/ISAPI/Streaming/channels/${channelId}`)\n return parseStreamingChannelConfig(xml)\n } catch {\n return null\n }\n }\n\n /**\n * `/ISAPI/Streaming/channels/{id}/dynamicCap` — read the firmware's\n * advertised set of valid FPS / resolution / codec combos for this\n * channel. We only surface the union of supported frame rates today\n * (operator-relevant for the FPS select); the rest is available via\n * the raw JSON for future tabs (resolution picker, etc.). Returns\n * `null` on 404 (legacy firmwares without dynamicCap).\n */\n async getStreamingChannelCapabilities(channelId: string): Promise<HikvisionStreamingChannelCaps | null> {\n try {\n const xml = await this.getText(`/ISAPI/Streaming/channels/${channelId}/dynamicCap`)\n return parseStreamingChannelCaps(xml)\n } catch {\n return null\n }\n }\n\n /**\n * Patch a streaming channel's video config. Read-modify-write so\n * fields we don't surface (resolution, audio mux flags) survive.\n * Hikvision's frame-rate is encoded as `1/100s of a frame` (a value\n * of 2500 = 25fps) — pass the human-readable FPS, the helper\n * multiplies by 100 before write.\n */\n async setStreamingChannel(\n channelId: string,\n patch: {\n videoCodecType?: string\n maxFrameRate?: number // human-readable FPS (e.g. 25)\n videoQualityControlType?: 'VBR' | 'CBR'\n vbrUpperCap?: number // kbps\n constantBitRate?: number // kbps\n fixedQuality?: number // 1..100 (VBR quality target)\n govLength?: number // I-frame interval in frames\n smartCodecEnabled?: boolean\n },\n ): Promise<void> {\n const url = `/ISAPI/Streaming/channels/${channelId}`\n const current = await this.getText(url)\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.videoCodecType !== undefined) {\n patches.push({ tag: 'videoCodecType', value: patch.videoCodecType })\n }\n if (patch.maxFrameRate !== undefined) {\n // Hikvision wire encoding: integer FPS × 100.\n patches.push({ tag: 'maxFrameRate', value: String(Math.round(patch.maxFrameRate * 100)) })\n }\n if (patch.videoQualityControlType !== undefined) {\n patches.push({ tag: 'videoQualityControlType', value: patch.videoQualityControlType })\n }\n if (patch.vbrUpperCap !== undefined) {\n patches.push({ tag: 'vbrUpperCap', value: String(patch.vbrUpperCap) })\n }\n if (patch.constantBitRate !== undefined) {\n patches.push({ tag: 'constantBitRate', value: String(patch.constantBitRate) })\n }\n if (patch.fixedQuality !== undefined) {\n patches.push({ tag: 'fixedQuality', value: String(Math.max(1, Math.min(100, Math.round(patch.fixedQuality)))) })\n }\n if (patch.govLength !== undefined) {\n patches.push({ tag: 'GovLength', value: String(patch.govLength) })\n }\n // SmartCodec lives nested under <SmartCodec><enabled>...; the regex\n // helper matches the FIRST <enabled>...</enabled> in the document\n // which would clobber the StreamingChannel-level enabled flag —\n // skip the read-modify-write helper for this tag and patch the\n // nested form directly.\n let next = applyOverlayFieldPatches(current, patches)\n if (patch.smartCodecEnabled !== undefined) {\n const value = patch.smartCodecEnabled ? 'true' : 'false'\n next = next.replace(\n /(<SmartCodec[^>]*>[\\s\\S]*?<enabled[^>]*>)[\\s\\S]*?(<\\/enabled>[\\s\\S]*?<\\/SmartCodec>)/i,\n `$1${value}$2`,\n )\n }\n if (patches.length === 0 && patch.smartCodecEnabled === undefined) return\n await this.putXml(url, next)\n }\n\n // ── VMD event-trigger linkage outputs ─────────────────────────────\n //\n // `/ISAPI/Event/triggers/VMD-{cam}` lists the camera-side reactions\n // wired to motion (beep / white-light flash / IO output / Hikvision\n // notification center / linked recording). Each entry is a\n // `<EventTriggerNotification>` with `notificationMethod`. Supported\n // methods vary by firmware — we only enumerate the four most common:\n // `beep`, `whiteLight`, `IO`, `center`. Operators on richer firmware\n // can extend via the camera UI; we won't clobber their entries.\n\n // ── Image tuning (color / WDR / IR-cut filter) ────────────────────\n //\n // Hikvision groups the operator-tunable image controls under separate\n // sub-resources of `/ISAPI/Image/channels/{cam}`. Each is a small\n // self-contained XML — read-modify-write per endpoint. We surface\n // the three most operator-relevant: `color` (brightness / contrast /\n // saturation / sharpness 0..100 ladders), `WDR` (high-dynamic-range\n // enable + level), `IrcutFilter` (day / night / auto / schedule).\n // Exposure / BLC / HLC are firmware-quirky multi-mode setups — defer\n // to a future pass.\n\n async getImageColor(cameraNumber: number): Promise<HikvisionImageColor | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/color`)\n return parseImageColor(xml)\n } catch {\n return null\n }\n }\n\n async setImageColor(\n cameraNumber: number,\n patch: { brightness?: number; contrast?: number; saturation?: number },\n ): Promise<void> {\n const url = `/ISAPI/Image/channels/${cameraNumber}/color`\n const current = await this.getText(url)\n const clamp = (v: number): string => String(Math.max(0, Math.min(100, Math.round(v))))\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.brightness !== undefined) patches.push({ tag: 'brightnessLevel', value: clamp(patch.brightness) })\n if (patch.contrast !== undefined) patches.push({ tag: 'contrastLevel', value: clamp(patch.contrast) })\n if (patch.saturation !== undefined) patches.push({ tag: 'saturationLevel', value: clamp(patch.saturation) })\n if (patches.length === 0) return\n const next = applyOverlayFieldPatches(current, patches)\n await this.putXml(url, next)\n }\n\n /**\n * `/ISAPI/Image/channels/{cam}/sharpness` — Hikvision puts sharpness\n * on a SEPARATE endpoint from `/color` (PascalCase XML root +\n * `<SharpnessLevel>` tag, capital S). Operator-tunable 0..100.\n * Returns `null` on 404 (some firmwares fold it back into `/color`).\n */\n async getImageSharpness(cameraNumber: number): Promise<number | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/sharpness`)\n const raw = extractTag(xml, 'SharpnessLevel') ?? extractTag(xml, 'sharpnessLevel')\n return raw !== null ? parseIntSafe(raw) : null\n } catch {\n return null\n }\n }\n\n async setImageSharpness(cameraNumber: number, level: number): Promise<void> {\n const url = `/ISAPI/Image/channels/${cameraNumber}/sharpness`\n const current = await this.getText(url)\n const value = String(Math.max(0, Math.min(100, Math.round(level))))\n // Hikvision uses `SharpnessLevel` (capital S) on this endpoint —\n // the regex helper is case-insensitive so either works.\n const next = applyOverlayFieldPatches(current, [{ tag: 'SharpnessLevel', value }])\n await this.putXml(url, next)\n }\n\n async getImageWdr(cameraNumber: number): Promise<HikvisionImageWdr | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/WDR`)\n return parseImageWdr(xml)\n } catch {\n return null\n }\n }\n\n async setImageWdr(\n cameraNumber: number,\n patch: { enabled?: boolean; level?: number },\n ): Promise<void> {\n const url = `/ISAPI/Image/channels/${cameraNumber}/WDR`\n const current = await this.getText(url)\n const patches: Array<{ tag: string; value: string }> = []\n if (patch.enabled !== undefined) {\n // Hikvision uses `mode` (open/close) on WDR — boolean translates\n // to those tokens.\n patches.push({ tag: 'mode', value: patch.enabled ? 'open' : 'close' })\n }\n if (patch.level !== undefined) {\n patches.push({ tag: 'WDRLevel', value: String(Math.max(0, Math.min(100, Math.round(patch.level)))) })\n }\n if (patches.length === 0) return\n const next = applyOverlayFieldPatches(current, patches)\n await this.putXml(url, next)\n }\n\n async getIrcutFilter(cameraNumber: number): Promise<HikvisionIrcutFilter | null> {\n try {\n const xml = await this.getText(`/ISAPI/Image/channels/${cameraNumber}/IrcutFilter`)\n return parseIrcutFilter(xml)\n } catch {\n return null\n }\n }\n\n async setIrcutFilter(\n cameraNumber: number,\n patch: { mode?: HikvisionIrcutMode },\n ): Promise<void> {\n if (patch.mode === undefined) return\n const url = `/ISAPI/Image/channels/${cameraNumber}/IrcutFilter`\n const current = await this.getText(url)\n const next = applyOverlayFieldPatches(current, [{ tag: 'IrcutFilterType', value: patch.mode }])\n await this.putXml(url, next)\n }\n\n async getMotionTriggerLinkage(cameraNumber: number): Promise<HikvisionMotionLinkage | null> {\n try {\n const xml = await this.getText(`/ISAPI/Event/triggers/VMD-${cameraNumber}`)\n return parseMotionTriggerLinkage(xml)\n } catch {\n return null\n }\n }\n\n /**\n * Patch the VMD trigger's notification methods. Adds / removes the\n * listed methods while preserving everything else (other notification\n * types, recording linkages). Pass `true` to enable, `false` to\n * disable; omitted methods are left untouched.\n */\n async setMotionTriggerLinkage(\n cameraNumber: number,\n patch: { beep?: boolean; whiteLight?: boolean; ioOutput?: boolean; center?: boolean },\n ): Promise<void> {\n const url = `/ISAPI/Event/triggers/VMD-${cameraNumber}`\n let xml = await this.getText(url)\n\n const methodMap: ReadonlyArray<{ key: keyof typeof patch; method: string }> = [\n { key: 'beep', method: 'beep' },\n { key: 'whiteLight', method: 'whiteLight' },\n { key: 'ioOutput', method: 'IO' },\n { key: 'center', method: 'center' },\n ]\n\n // Strip every method we manage from the list before re-inserting\n // the wanted ones. Keep methods we don't manage (`record`,\n // `email`, …) verbatim so the operator's other linkages survive.\n const managed = methodMap.map((m) => m.method)\n for (const method of managed) {\n const re = new RegExp(`<EventTriggerNotification[^>]*>[\\\\s\\\\S]*?<notificationMethod>${method}<\\\\/notificationMethod>[\\\\s\\\\S]*?<\\\\/EventTriggerNotification>`, 'gi')\n xml = xml.replace(re, '')\n }\n\n // Read the current state to know which methods stay enabled when\n // not in the patch (caller patches partial — undefined = leave as\n // is). The current state was already in the original XML before we\n // stripped; refetch via a fresh GET would be cleaner, but we can\n // recover from the unstripped copy.\n const original = await this.getText(url)\n const current = parseMotionTriggerLinkage(original)\n const desired: Record<string, boolean> = {\n beep: patch.beep ?? current.beep,\n whiteLight: patch.whiteLight ?? current.whiteLight,\n IO: patch.ioOutput ?? current.ioOutput,\n center: patch.center ?? current.center,\n }\n\n const additions: string[] = []\n for (const m of methodMap) {\n if (desired[m.method] === true) {\n additions.push(`<EventTriggerNotification><id>${m.method}</id><notificationMethod>${m.method}</notificationMethod></EventTriggerNotification>`)\n }\n }\n\n // Inject the new entries inside the existing\n // `<EventTriggerNotificationList>...</EventTriggerNotificationList>`\n // block. If the list doesn't exist yet (rare on barebones firmware),\n // append a fresh one before `</EventTrigger>`.\n if (/<EventTriggerNotificationList\\b/i.test(xml)) {\n xml = xml.replace(\n /(<\\/EventTriggerNotificationList>)/i,\n `${additions.join('')}$1`,\n )\n } else {\n xml = xml.replace(\n /(<\\/EventTrigger>)/i,\n `<EventTriggerNotificationList>${additions.join('')}</EventTriggerNotificationList>$1`,\n )\n }\n await this.putXml(url, xml)\n }\n}\n\nfunction clampSpeed(value: number): number {\n if (!Number.isFinite(value)) return 0\n return Math.max(-100, Math.min(100, Math.round(value)))\n}\n\n// ── Extended types (PTZ / light / alarm) ─────────────────────────\n\nexport interface HikvisionPtzCapabilities {\n readonly hasPan: boolean\n readonly hasTilt: boolean\n readonly hasZoom: boolean\n readonly maxPresetCount: number | null\n /** Raw XML payload — keep for diagnostic dumps. */\n readonly raw: string\n}\n\nexport interface HikvisionPtzPreset {\n readonly id: string\n readonly name: string | null\n readonly enabled: boolean\n}\n\nexport interface HikvisionLightCapabilities {\n /** Modes the camera advertised as available (`auto`, `manual`,\n * `colorVuWhiteLight`, `irLight`, `close`, …). */\n readonly modes: readonly string[]\n readonly raw: string\n}\n\nexport interface HikvisionLightState {\n /** Currently active mode. `close` = off. */\n readonly mode: string\n /** Either white or IR brightness, whichever the firmware reports —\n * kept for backwards compat with the LightAccessory's brightness\n * cap. New consumers should prefer the typed pair below. */\n readonly brightness: number | null\n readonly whiteLightBrightness: number | null\n readonly irLightBrightness: number | null\n /** `auto` (camera-side light sensor drives brightness) / `manual`\n * (operator-set) / `null` when the firmware doesn't expose it. */\n readonly brightnessRegulatorMode: 'auto' | 'manual' | null\n}\n\nexport interface HikvisionAlarmInput {\n /** Hikvision-numbered port id (typically 1-based). */\n readonly id: string\n readonly name: string | null\n readonly enabled: boolean\n}\n\nexport interface HikvisionSmartTrackingCapabilities {\n /** True when the firmware accepts a `<duration>` field — most newer\n * models do, older PTZ models ship enable-only. */\n readonly supportsDuration: boolean\n /** True when the firmware accepts a `<smartTrackingType>` selector\n * (`auto` / `human` / `vehicle`). Varies more than `duration`. */\n readonly supportsType: boolean\n readonly raw: string\n}\n\nexport interface HikvisionSmartTrackingState {\n readonly enabled: boolean\n /** Duration (seconds) the camera keeps following a stationary\n * target before releasing. `null` when the firmware doesn't\n * expose this knob. */\n readonly duration: number | null\n /** Tracking-type selector (`auto` / `human` / `vehicle`). `null`\n * when the firmware doesn't expose this knob. */\n readonly trackingType: string | null\n}\n\nexport interface HikvisionTextOverlay {\n readonly id: string\n readonly enabled: boolean\n readonly text: string | null\n}\n\nexport interface HikvisionOverlayConfig {\n readonly dateTime: { readonly enabled: boolean }\n readonly channelName: { readonly enabled: boolean }\n /** Operator-editable text overlays. Always 4 entries on consumer\n * cameras (firmware allocates the IDs eagerly even when empty);\n * some NVR channels expose 8. */\n readonly textOverlays: readonly HikvisionTextOverlay[]\n}\n\nexport interface HikvisionMotionDetection {\n readonly enabled: boolean\n readonly sensitivityLevel: number\n /** Firmware-advertised slider bounds (min/max/step). Drives the UI\n * slider granularity — older firmwares advertise step=20 (0/20/40/\n * 60/80/100), newer ones advertise step=1 (continuous). */\n readonly sensitivityMin: number\n readonly sensitivityMax: number\n readonly sensitivityStep: number\n}\n\n/**\n * VCA resource modes:\n * - `smart` — camera runs smart-detection algorithms (motion-AI,\n * line crossing, intrusion, …). Disables the third\n * stream slot.\n * - `facesnap` — face capture mode; disables third stream.\n * - `close` — VCA off, third stream slot freed for use.\n *\n * Switching modes requires a camera reboot — the lib's `setVcaResource`\n * returns `true` when the mode actually changed so callers can decide\n * whether to issue the reboot.\n */\nexport type HikvisionVcaResourceMode = 'smart' | 'facesnap' | 'close'\n\nexport interface HikvisionAudioCapabilities {\n /** Codec strings the firmware advertises in the `audioCompressionType`\n * attribute's `opt` list (e.g. `['G.711ulaw', 'G.711alaw',\n * 'G.722.1', 'AAC']`). Empty when the camera doesn't enumerate. */\n readonly audioCodecs: readonly string[]\n /** Input-type strings advertised in `audioInputType@opt` (`MicIn`,\n * `LineIn`, …). Empty when the camera doesn't expose them. */\n readonly audioInputTypes: readonly string[]\n /** Speaker volume range — defaults 0..100 when the camera doesn't\n * advertise tighter bounds. */\n readonly speakerVolumeMin: number\n readonly speakerVolumeMax: number\n /** True when the camera advertises `noisereduce` as togglable. */\n readonly supportsNoiseReduction: boolean\n}\n\nexport interface HikvisionAudioSettings {\n readonly audioCompressionType: string | null\n readonly speakerVolume: number | null\n readonly noiseReduction: boolean\n readonly audioInputType: string | null\n}\n\nexport interface HikvisionStreamingChannelConfig {\n readonly id: string\n readonly channelName: string | null\n readonly enabled: boolean\n readonly videoCodecType: string | null\n readonly resolution: { readonly width: number | null; readonly height: number | null }\n /** Frames per second — already converted from Hikvision's `FPS×100`\n * wire encoding to the human-readable form. */\n readonly maxFrameRate: number | null\n readonly videoQualityControlType: 'VBR' | 'CBR' | null\n /** Bitrate (kbps) used when `videoQualityControlType === 'VBR'`. */\n readonly vbrUpperCap: { readonly value: number | null; readonly min: number; readonly max: number }\n /** Bitrate (kbps) used when `videoQualityControlType === 'CBR'`. */\n readonly constantBitRate: { readonly value: number | null; readonly min: number; readonly max: number }\n /** VBR quality target 1..100. */\n readonly fixedQuality: number | null\n readonly govLength: { readonly value: number | null; readonly min: number; readonly max: number }\n readonly smartCodecEnabled: boolean\n readonly h264Profile: string | null\n readonly h265Profile: string | null\n}\n\nexport interface HikvisionStreamingChannelCaps {\n /** Sorted-descending list of FPS values the firmware advertises across\n * every resolution this channel supports. Already converted from\n * Hikvision's `FPS×100` wire encoding to human-readable integers. */\n readonly supportedFrameRates: readonly number[]\n}\n\nexport interface HikvisionMotionLinkage {\n readonly beep: boolean\n readonly whiteLight: boolean\n readonly ioOutput: boolean\n readonly center: boolean\n}\n\nexport interface HikvisionImageColor {\n readonly brightness: number | null\n readonly contrast: number | null\n readonly saturation: number | null\n readonly sharpness: number | null\n}\n\nexport interface HikvisionImageWdr {\n readonly enabled: boolean\n readonly level: number | null\n}\n\nexport type HikvisionIrcutMode = 'day' | 'night' | 'auto' | 'schedule'\n\nexport interface HikvisionIrcutFilter {\n readonly mode: HikvisionIrcutMode | string\n}\n\nfunction parsePtzCapabilities(xml: string): HikvisionPtzCapabilities | null {\n const block = extractTag(xml, 'PTZChanelCap') ?? extractTag(xml, 'PTZChannelCap') ?? xml\n if (!block) return null\n const hasPan = /<XRange>/i.test(block) || /<PanRange>/i.test(block)\n const hasTilt = /<YRange>/i.test(block) || /<TiltRange>/i.test(block)\n const hasZoom = /<ZRange>/i.test(block) || /<ZoomRange>/i.test(block)\n if (!hasPan && !hasTilt && !hasZoom) return null\n const maxPresetCount = parseIntSafe(extractTag(block, 'maxPresetNum'))\n return { hasPan, hasTilt, hasZoom, maxPresetCount, raw: xml }\n}\n\nfunction parsePtzPresets(xml: string): readonly HikvisionPtzPreset[] {\n const blocks = extractAllBlocks(xml, 'PTZPreset')\n const out: HikvisionPtzPreset[] = []\n for (const b of blocks) {\n const id = extractTag(b, 'id')\n if (!id) continue\n const name = extractTag(b, 'presetName')\n const enabled = (extractTag(b, 'enabled') ?? 'true').toLowerCase() === 'true'\n if (!enabled) continue\n out.push({ id, name, enabled })\n }\n return out\n}\n\nfunction parseSupplementLightModes(xml: string): readonly string[] {\n // Hikvision encodes available modes either as a list of `<supplementLightMode>` tags\n // OR as a single tag with `opt=\"auto,manual,...\"` attribute. Cover both shapes.\n const optMatch = /<supplementLightMode[^>]*\\bopt\\s*=\\s*\"([^\"]*)\"/i.exec(xml)\n if (optMatch) {\n return optMatch[1]!.split(',').map(s => s.trim()).filter(s => s.length > 0)\n }\n const blocks = extractAllBlocks(xml, 'supplementLightMode')\n return blocks\n .map(b => decodeXmlEntities(b.trim()))\n .filter(s => s.length > 0)\n}\n\nfunction parseSupplementLightState(xml: string): HikvisionLightState | null {\n const mode = extractTag(xml, 'supplementLightMode')\n if (!mode) return null\n const whiteLightBrightness = parseIntSafe(extractTag(xml, 'whiteLightBrightness'))\n const irLightBrightness = parseIntSafe(extractTag(xml, 'irLightBrightness'))\n const brightness = whiteLightBrightness ?? irLightBrightness\n const regulatorRaw = extractTag(xml, 'mixedLightBrightnessRegulatMode')\n const brightnessRegulatorMode = regulatorRaw === 'auto' || regulatorRaw === 'manual'\n ? regulatorRaw\n : null\n return { mode, brightness, whiteLightBrightness, irLightBrightness, brightnessRegulatorMode }\n}\n\nfunction parseAlarmInputs(xml: string): readonly HikvisionAlarmInput[] {\n const blocks = extractAllBlocks(xml, 'IOInputPort')\n const out: HikvisionAlarmInput[] = []\n for (const b of blocks) {\n const id = extractTag(b, 'id')\n if (!id) continue\n const name = extractTag(b, 'name')\n const enabled = (extractTag(b, 'enabled') ?? 'true').toLowerCase() === 'true'\n out.push({ id, name, enabled })\n }\n return out\n}\n\nfunction parseOverlayConfig(xml: string): HikvisionOverlayConfig {\n // dateTimeOverlay + channelNameOverlay each appear once at the top\n // level of `<VideoOverlay>` — extract their `<enabled>` directly.\n const dateTimeBlock = extractTag(xml, 'DateTimeOverlay') ?? ''\n const channelBlock = extractTag(xml, 'channelNameOverlay') ?? ''\n const dateTimeEnabled = (extractTag(`<x>${dateTimeBlock}</x>`, 'enabled') ?? 'false').toLowerCase() === 'true'\n const channelEnabled = (extractTag(`<x>${channelBlock}</x>`, 'enabled') ?? 'false').toLowerCase() === 'true'\n\n // TextOverlayList → multiple TextOverlay entries. Iterate via the\n // generic block extractor; firmware allocates IDs 1..N regardless of\n // whether the slot has visible text.\n const textBlocks = extractAllBlocks(xml, 'TextOverlay')\n const textOverlays: HikvisionTextOverlay[] = []\n for (const b of textBlocks) {\n const id = extractTag(`<x>${b}</x>`, 'id')\n if (!id) continue\n const enabled = (extractTag(`<x>${b}</x>`, 'enabled') ?? 'false').toLowerCase() === 'true'\n const text = extractTag(`<x>${b}</x>`, 'displayText')\n textOverlays.push({ id, enabled, text })\n }\n\n return {\n dateTime: { enabled: dateTimeEnabled },\n channelName: { enabled: channelEnabled },\n textOverlays,\n }\n}\n\n/**\n * Splice tag values into an existing XML payload (read-modify-write).\n * Replaces the FIRST occurrence of each requested `<tag>...</tag>`\n * with the new value, leaving the rest of the document untouched.\n * Used for per-element overlay PUTs where the camera demands the\n * complete sub-tree but we only want to flip a couple of fields.\n */\nfunction applyOverlayFieldPatches(\n xml: string,\n patches: ReadonlyArray<{ tag: string; value: string }>,\n): string {\n let out = xml\n for (const p of patches) {\n const re = new RegExp(`(<(?:[a-zA-Z0-9]+:)?${p.tag}[^>]*>)[\\\\s\\\\S]*?(</(?:[a-zA-Z0-9]+:)?${p.tag}>)`, 'i')\n out = out.replace(re, `$1${p.value}$2`)\n }\n return out\n}\n\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\nfunction parseImageColor(xml: string): HikvisionImageColor {\n // `sharpness` lives on a SEPARATE endpoint on most Hikvision\n // firmwares (`/Image/channels/{cam}/sharpness`); the parser only\n // tries the inline tag for the rare firmware that folds it into\n // `/color`. Caller falls back to `getImageSharpness()` on null.\n return {\n brightness: parseIntSafe(extractTag(xml, 'brightnessLevel')),\n contrast: parseIntSafe(extractTag(xml, 'contrastLevel')),\n saturation: parseIntSafe(extractTag(xml, 'saturationLevel')),\n sharpness: parseIntSafe(extractTag(xml, 'sharpnessLevel')),\n }\n}\n\nfunction parseImageWdr(xml: string): HikvisionImageWdr {\n // Hikvision encodes WDR enable as `<mode>open</mode>` /\n // `<mode>close</mode>` (NOT a boolean tag). The level is the\n // `<WDRLevel>` integer. Some firmwares use a slightly different\n // tag (`<wdrLevel>` lowercase) — accept either.\n const mode = (extractTag(xml, 'mode') ?? '').toLowerCase()\n const enabled = mode === 'open'\n const levelRaw = extractTag(xml, 'WDRLevel') ?? extractTag(xml, 'wdrLevel')\n const level = levelRaw !== null ? parseIntSafe(levelRaw) : null\n return { enabled, level }\n}\n\nfunction parseIrcutFilter(xml: string): HikvisionIrcutFilter {\n const mode = extractTag(xml, 'IrcutFilterType') ?? 'auto'\n return { mode }\n}\n\nfunction parseStreamingChannelCaps(xml: string): HikvisionStreamingChannelCaps {\n // The dynamicCap doc has multiple `<ResolutionAvailableDscriptor>`\n // blocks, each with a `<supportedFrameRate>` CSV in Hikvision's\n // `FPS×100` wire format. Union across all resolutions; the\n // operator-facing select doesn't need a per-resolution split today\n // (resolution itself isn't tunable from the form yet).\n const blocks = extractAllBlocks(xml, 'supportedFrameRate')\n const all = new Set<number>()\n for (const csv of blocks) {\n for (const piece of csv.split(',')) {\n const n = parseIntSafe(piece.trim())\n if (n === null || n <= 0) continue\n // Hikvision wire format is FPS×100; sub-1-fps entries (e.g. 50 = 0.5fps)\n // round to 0 and would violate the schema's `.positive()` invariant.\n const fps = Math.round(n / 100)\n if (fps <= 0) continue\n all.add(fps)\n }\n }\n return { supportedFrameRates: [...all].sort((a, b) => b - a) }\n}\n\nfunction parseStreamingChannelConfig(xml: string): HikvisionStreamingChannelConfig {\n const id = extractTag(xml, 'id') ?? ''\n const channelName = extractTag(xml, 'channelName')\n const enabledRaw = extractTag(xml, 'enabled')\n const enabled = (enabledRaw ?? 'false').toLowerCase() === 'true'\n const videoCodecType = extractTag(xml, 'videoCodecType')\n const width = parseIntSafe(extractTag(xml, 'videoResolutionWidth'))\n const height = parseIntSafe(extractTag(xml, 'videoResolutionHeight'))\n // Hikvision stores FPS as `frames × 100` (2500 = 25fps). Convert\n // back to the human-readable integer for display.\n const fpsRaw = parseIntSafe(extractTag(xml, 'maxFrameRate'))\n const maxFrameRate = fpsRaw !== null ? Math.round(fpsRaw / 100) : null\n const qcType = extractTag(xml, 'videoQualityControlType')\n const videoQualityControlType = qcType === 'VBR' || qcType === 'CBR' ? qcType : null\n\n const parseRange = (tag: string, fallbackMax: number): { value: number | null; min: number; max: number } => {\n const value = parseIntSafe(extractTag(xml, tag))\n const tagMatch = new RegExp(`<${tag}\\\\b([^>]*)>`, 'i').exec(xml)\n const attrs = tagMatch ? tagMatch[1] ?? '' : ''\n const min = parseIntSafe((/\\bmin=\"(\\d+)\"/i.exec(attrs) ?? [])[1] ?? null) ?? 0\n const max = parseIntSafe((/\\bmax=\"(\\d+)\"/i.exec(attrs) ?? [])[1] ?? null) ?? fallbackMax\n return { value, min, max }\n }\n const vbrUpperCap = parseRange('vbrUpperCap', 16384)\n const constantBitRate = parseRange('constantBitRate', 16384)\n const govLength = parseRange('GovLength', 400)\n const fixedQuality = parseIntSafe(extractTag(xml, 'fixedQuality'))\n const smartCodecBlock = extractTag(xml, 'SmartCodec') ?? ''\n const smartCodecEnabled = smartCodecBlock.length > 0\n && (extractTag(`<x>${smartCodecBlock}</x>`, 'enabled') ?? 'false').toLowerCase() === 'true'\n const h264Profile = extractTag(xml, 'H264Profile')\n const h265Profile = extractTag(xml, 'H265Profile')\n return {\n id,\n channelName,\n enabled,\n videoCodecType,\n resolution: { width, height },\n maxFrameRate,\n videoQualityControlType,\n vbrUpperCap,\n constantBitRate,\n fixedQuality,\n govLength,\n smartCodecEnabled,\n h264Profile,\n h265Profile,\n }\n}\n\nfunction parseMotionTriggerLinkage(xml: string): HikvisionMotionLinkage {\n // Each `<EventTriggerNotification>` has a `<notificationMethod>` we\n // care about — beep / whiteLight / IO / center. The check is a\n // simple substring scan; the per-block attributes (recurrence, etc.)\n // are irrelevant to whether the linkage is armed.\n const methods = extractAllBlocks(xml, 'notificationMethod').map((m) => m.trim().toLowerCase())\n return {\n beep: methods.includes('beep'),\n whiteLight: methods.includes('whitelight'),\n ioOutput: methods.includes('io'),\n center: methods.includes('center'),\n }\n}\n\nfunction parseAudioCapabilities(xml: string): HikvisionAudioCapabilities {\n // Hikvision encodes both element-level options and attribute-level\n // ranges. Parse defensively — older firmwares omit some, newer ones\n // wrap them under a `TwoWayAudioChannel` root.\n const codecsAttr = /<audioCompressionType\\b[^>]*\\bopt\\s*=\\s*\"([^\"]*)\"/i.exec(xml)\n const audioCodecs = codecsAttr\n ? codecsAttr[1]!.split(',').map(s => s.trim()).filter(s => s.length > 0)\n : []\n const inputAttr = /<audioInputType\\b[^>]*\\bopt\\s*=\\s*\"([^\"]*)\"/i.exec(xml)\n const audioInputTypes = inputAttr\n ? inputAttr[1]!.split(',').map(s => s.trim()).filter(s => s.length > 0)\n : []\n const volMatch = /<speakerVolume\\b([^>]*)>/i.exec(xml)\n const volAttrs = volMatch ? volMatch[1] ?? '' : ''\n const speakerVolumeMin = parseIntSafe((/\\bmin=\"(\\d+)\"/i.exec(volAttrs) ?? [])[1] ?? null) ?? 0\n const speakerVolumeMax = parseIntSafe((/\\bmax=\"(\\d+)\"/i.exec(volAttrs) ?? [])[1] ?? null) ?? 100\n const noiseAttr = /<noisereduce\\b[^>]*\\bopt\\s*=\\s*\"([^\"]*)\"/i.exec(xml)\n const supportsNoiseReduction = noiseAttr ? /true/i.test(noiseAttr[1] ?? '') : false\n return { audioCodecs, audioInputTypes, speakerVolumeMin, speakerVolumeMax, supportsNoiseReduction }\n}\n\nfunction parseAudioSettings(xml: string): HikvisionAudioSettings | null {\n const audioCompressionType = extractTag(xml, 'audioCompressionType')\n const speakerRaw = extractTag(xml, 'speakerVolume')\n const speakerVolume = speakerRaw !== null ? parseIntSafe(speakerRaw) : null\n const noiseRaw = extractTag(xml, 'noisereduce')\n const noiseReduction = (noiseRaw ?? 'false').toLowerCase() === 'true'\n const audioInputType = extractTag(xml, 'audioInputType')\n return { audioCompressionType, speakerVolume, noiseReduction, audioInputType }\n}\n\nfunction parseMotionDetection(xml: string): HikvisionMotionDetection | null {\n const enabled = (extractTag(xml, 'enabled') ?? 'false').toLowerCase() === 'true'\n // Sensitivity level + advertised slider bounds. Older firmwares\n // ship `step=20` (5 discrete levels); newer ones expose the full\n // 0..100 range with step=1. We surface both so the UI form picks\n // the right control.\n const levelRaw = extractTag(xml, 'sensitivityLevel')\n const sensitivityLevel = parseIntSafe(levelRaw) ?? 0\n // Match attributes on the sensitivityLevel tag for min/max/step.\n const attrMatch = /<sensitivityLevel\\b([^>]*)>/i.exec(xml)\n const attrs = attrMatch ? attrMatch[1] ?? '' : ''\n const min = parseIntSafe((/\\bmin=\"(\\d+)\"/i.exec(attrs) ?? [])[1] ?? null) ?? 0\n const max = parseIntSafe((/\\bmax=\"(\\d+)\"/i.exec(attrs) ?? [])[1] ?? null) ?? 100\n const step = parseIntSafe((/\\bstep=\"(\\d+)\"/i.exec(attrs) ?? [])[1] ?? null) ?? 1\n return {\n enabled,\n sensitivityLevel,\n sensitivityMin: min,\n sensitivityMax: max,\n sensitivityStep: step > 0 ? step : 1,\n }\n}\n\n// ── XML helpers ───────────────────────────────────────────────────\n//\n// Hikvision's ISAPI payloads are always small flat XML — parsing with\n// regex avoids pulling in a full XML library for what amounts to \"find\n// this tag's text content\". The patterns are intentionally\n// conservative: they match a single occurrence per tag, ignore\n// attributes, and tolerate optional whitespace + the XML namespace\n// prefix some firmwares emit (`hikvision:`).\n\nfunction extractTag(xml: string, tag: string): string | null {\n const re = new RegExp(`<(?:[a-zA-Z0-9]+:)?${tag}[^>]*>([\\\\s\\\\S]*?)</(?:[a-zA-Z0-9]+:)?${tag}>`, 'i')\n const m = re.exec(xml)\n return m ? decodeXmlEntities(m[1]!.trim()) : null\n}\n\nfunction extractAllBlocks(xml: string, tag: string): readonly string[] {\n const re = new RegExp(`<(?:[a-zA-Z0-9]+:)?${tag}[^>]*>([\\\\s\\\\S]*?)</(?:[a-zA-Z0-9]+:)?${tag}>`, 'gi')\n const out: string[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(xml)) !== null) out.push(m[1]!)\n return out\n}\n\nfunction decodeXmlEntities(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n}\n\nfunction parseChannelsList(xml: string): readonly HikvisionStreamingChannel[] {\n const blocks = extractAllBlocks(xml, 'StreamingChannel')\n const out: HikvisionStreamingChannel[] = []\n for (const b of blocks) {\n const id = extractTag(b, 'id')\n if (!id) continue\n const enabled = (extractTag(b, 'enabled') ?? 'true').toLowerCase() === 'true'\n const codec = extractTag(b, 'videoCodecType')\n const width = parseIntSafe(extractTag(b, 'videoResolutionWidth'))\n const height = parseIntSafe(extractTag(b, 'videoResolutionHeight'))\n const maxBitrateKbps = parseIntSafe(extractTag(b, 'maxBitrate'))\n // Hikvision stores `id` as `${cameraNumber}${streamSlot}` where\n // `streamSlot` is the last digit — split deterministically.\n const cameraNumber = Math.floor(Number(id) / 100) || 1\n const streamSlot = Number(id) - cameraNumber * 100\n out.push({ id, cameraNumber, streamSlot, enabled, codec, width, height, maxBitrateKbps })\n }\n return out\n}\n\nfunction parseIntSafe(s: string | null): number | null {\n if (s === null) return null\n const n = parseInt(s, 10)\n return Number.isFinite(n) ? n : null\n}\n\nfunction parseTwoWayAudioChannels(xml: string): readonly HikvisionTwoWayAudioChannel[] {\n const blocks = extractAllBlocks(xml, 'TwoWayAudioChannel')\n const out: HikvisionTwoWayAudioChannel[] = []\n for (const b of blocks) {\n const id = extractTag(b, 'id')\n if (!id) continue\n const audioCompressionType = extractTag(b, 'audioCompressionType')\n const audioInputType = extractTag(b, 'audioInputType')\n out.push({ id, audioCompressionType, audioInputType })\n }\n return out\n}\n\n// ── Alarm stream parser ───────────────────────────────────────────\n//\n// The alarm stream (`/ISAPI/Event/notification/alertStream`) is HTTP\n// keep-alive multipart. The boundary is announced in the response's\n// `Content-Type: multipart/mixed; boundary=...` header. Each part\n// carries either:\n// - `Content-Type: application/xml` — an EventNotificationAlert\n// describing one event (motion, line-cross, region-entrance, ...).\n// - `Content-Type: image/jpeg` — a cropped snapshot following a\n// smart-detection event. Optional; many firmwares omit it.\n//\n// `subscribeAlarms()` is a streaming consumer: it pulls a Response\n// body's stream, splits on the boundary, parses each part's headers\n// + body, and dispatches an `onEvent(parsed)` callback for every\n// XML alert. The image bytes are passed through opaquely on the\n// next pump cycle paired with the most recent XML event id (Hikvision\n// transmits them in matching order).\n\nexport interface HikvisionAlarmEvent {\n /** Wall-clock ms when the parser produced the event. */\n readonly observedAt: number\n /** `eventType` tag — `VMD`, `linedetection`, `fielddetection`, … */\n readonly type: string\n /** Optional `eventState` — `active` / `inactive`. */\n readonly state: string | null\n /** Channel id reported by the camera (`<channelID>` or `<dynChannelID>`). */\n readonly channelId: string | null\n /** Smart-detection class (`<targetType>` or `<DetectionRegion><className>`). */\n readonly aiClass: string | null\n /** Description / notes — free-text, not always populated. */\n readonly description: string | null\n /** Confidence as a 0-1 float when present. */\n readonly score: number | null\n /** Raw XML payload — kept so consumers can do their own parsing. */\n readonly raw: string\n}\n\nexport interface HikvisionAlarmHandlers {\n readonly onEvent: (event: HikvisionAlarmEvent) => void\n readonly onError: (err: unknown) => void\n /** Optional: invoked once when the stream connects (after the first\n * HTTP 200 response is received). Useful for upstream \"online\"\n * bookkeeping that wants to wait for a real subscription. */\n readonly onConnected?: () => void\n}\n\n/**\n * Subscribe to the camera's alarm stream. Returns an `AbortController`\n * — `controller.abort()` tears the subscription down. Reconnect logic\n * is the caller's responsibility (we keep the parser simple and let\n * the device class own the lifecycle / backoff timing).\n */\nexport function subscribeAlarms(\n client: HikvisionIsapiClient,\n handlers: HikvisionAlarmHandlers,\n): AbortController {\n const controller = new AbortController()\n void (async () => {\n try {\n const res = await client.request('/ISAPI/Event/notification/alertStream', {\n method: 'GET',\n signal: controller.signal,\n timeoutMs: null,\n })\n if (!res.ok || !res.body) {\n handlers.onError(new Error(`alarm stream HTTP ${res.status}`))\n return\n }\n const ct = res.headers.get('content-type') ?? ''\n const boundary = parseBoundary(ct)\n if (!boundary) {\n handlers.onError(new Error(`alarm stream missing multipart boundary in Content-Type: \"${ct}\"`))\n return\n }\n handlers.onConnected?.()\n await pumpAlarmStream(res.body, boundary, handlers)\n } catch (err) {\n if (controller.signal.aborted) return\n handlers.onError(err)\n }\n })()\n return controller\n}\n\nfunction parseBoundary(contentType: string): string | null {\n const m = /boundary\\s*=\\s*\"?([^\";\\s]+)\"?/i.exec(contentType)\n return m ? m[1]!.trim() : null\n}\n\nasync function pumpAlarmStream(\n stream: ReadableStream<Uint8Array>,\n boundary: string,\n handlers: HikvisionAlarmHandlers,\n): Promise<void> {\n const reader = stream.getReader()\n const dashBoundary = `--${boundary}`\n let bufferedBytes: Uint8Array = new Uint8Array(0)\n try {\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n if (!value || value.byteLength === 0) continue\n bufferedBytes = concat(bufferedBytes, value)\n\n // Split the running buffer on the boundary. Each split yields a\n // raw block (headers + body); the trailing fragment goes back\n // into the buffer for the next pump cycle.\n const parts = splitOnBoundary(bufferedBytes, dashBoundary)\n bufferedBytes = parts.tail\n for (const block of parts.blocks) {\n const parsed = parseMultipartBlock(block)\n if (!parsed) continue\n if (parsed.contentType.startsWith('application/xml') || parsed.contentType.startsWith('text/xml')) {\n const ev = parseAlertXml(parsed.bodyText)\n if (ev) handlers.onEvent(ev)\n }\n // Image parts (image/jpeg) are received but not exposed yet —\n // the v0.1 surface intentionally leaves AI-thumbnail forwarding\n // out of scope. Hook lands cleanly here when consumers ask for it.\n }\n }\n } finally {\n try { reader.releaseLock() } catch { /* swallow */ }\n }\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.byteLength + b.byteLength)\n out.set(a, 0)\n out.set(b, a.byteLength)\n return out\n}\n\ninterface MultipartSplit {\n readonly blocks: readonly Uint8Array[]\n readonly tail: Uint8Array\n}\n\nfunction splitOnBoundary(buf: Uint8Array, dashBoundary: string): MultipartSplit {\n const text = Buffer.from(buf).toString('binary')\n const parts = text.split(dashBoundary)\n if (parts.length <= 1) return { blocks: [], tail: buf }\n // Last fragment may be a partial block — keep it in the buffer.\n const tail = Buffer.from(parts.pop()!, 'binary')\n // First fragment is whatever came before the first boundary —\n // discard (preamble per RFC 1521).\n parts.shift()\n const blocks = parts\n .filter(p => p.length > 0 && !p.startsWith('--'))\n .map(p => Buffer.from(p, 'binary'))\n return { blocks, tail }\n}\n\ninterface MultipartBlock {\n readonly contentType: string\n readonly bodyText: string\n}\n\nfunction parseMultipartBlock(block: Uint8Array): MultipartBlock | null {\n // Skip leading CRLF after the boundary marker.\n let start = 0\n while (start < block.length && (block[start] === 0x0d || block[start] === 0x0a)) start++\n const headerEnd = findHeaderEnd(block, start)\n if (headerEnd < 0) return null\n const headerBlob = Buffer.from(block.subarray(start, headerEnd)).toString('utf8')\n const bodyStart = headerEnd + 4 // length of CRLFCRLF\n const bodySlice = block.subarray(bodyStart)\n const headers: Record<string, string> = {}\n for (const line of headerBlob.split(/\\r?\\n/)) {\n const idx = line.indexOf(':')\n if (idx <= 0) continue\n headers[line.slice(0, idx).trim().toLowerCase()] = line.slice(idx + 1).trim()\n }\n const contentType = headers['content-type'] ?? ''\n const bodyText = Buffer.from(bodySlice).toString('utf8').replace(/\\r?\\n--$/m, '').trim()\n return { contentType, bodyText }\n}\n\nfunction findHeaderEnd(buf: Uint8Array, from: number): number {\n for (let i = from; i + 3 < buf.length; i++) {\n if (buf[i] === 0x0d && buf[i + 1] === 0x0a && buf[i + 2] === 0x0d && buf[i + 3] === 0x0a) return i\n }\n return -1\n}\n\nfunction parseAlertXml(xml: string): HikvisionAlarmEvent | null {\n const type = extractTag(xml, 'eventType')\n if (!type) return null\n const state = extractTag(xml, 'eventState')\n const channelId = extractTag(xml, 'dynChannelID') ?? extractTag(xml, 'channelID')\n const description = extractTag(xml, 'eventDescription') ?? extractTag(xml, 'description')\n const aiClass = extractTag(xml, 'targetType') ?? extractTag(xml, 'className')\n const scoreRaw = extractTag(xml, 'targetProb') ?? extractTag(xml, 'similarity')\n const score = scoreRaw === null ? null : (() => {\n const n = parseFloat(scoreRaw)\n if (!Number.isFinite(n)) return null\n return n > 1 ? n / 100 : n\n })()\n return {\n observedAt: Date.now(),\n type: type.toLowerCase(),\n state: state?.toLowerCase() ?? null,\n channelId,\n aiClass,\n description,\n score,\n raw: xml,\n }\n}\n","import { createHash, randomBytes } from 'node:crypto'\n\n/**\n * Minimal HTTP Digest Auth client (RFC 7616, MD5 + qop=auth) — sized\n * for the Hikvision ISAPI surface, NOT a general-purpose auth client.\n * Hikvision cameras advertise `Digest realm=..., nonce=..., qop=auth`\n * in their `WWW-Authenticate` header; SHA-256 / sess variants and\n * Basic fallback are rare in the wild and intentionally unsupported\n * to keep this file under 100 lines.\n *\n * Usage pattern (callers should reuse the returned `DigestState` for\n * the lifetime of a logical session — `nonce`/`opaque` are per-realm\n * and must be remembered across requests; the per-request `nc`\n * counter is the only mutable bit):\n *\n * const state: DigestState = { nc: 0 }\n * // first request — no Authorization header → server returns 401 +\n * // Authenticate; absorb it and retry\n * const auth = buildAuthHeader({state, method: 'GET', uri: '/ISAPI/...',\n * username, password, challenge})\n *\n * The `challenge` is parsed once per 401 via `parseChallenge`. Stale\n * nonces (`stale=true`) trigger a fresh challenge automatically — the\n * caller absorbs the new 401 and retries.\n */\n\nexport interface DigestChallenge {\n readonly realm: string\n readonly nonce: string\n readonly qop: string | null\n readonly opaque: string | null\n readonly algorithm: string | null\n readonly stale: boolean\n}\n\nexport interface DigestState {\n /** Monotonically incremented per request — formatted as 8-hex\n * (`00000001`) and folded into the response hash. */\n nc: number\n /** Last-seen challenge. Reused for subsequent requests against the\n * same realm so we don't trigger a 401 on every call. */\n challenge?: DigestChallenge\n}\n\n/**\n * Parse a `WWW-Authenticate: Digest ...` header. Returns null when the\n * header is absent or not a Digest scheme — caller falls back to a\n * basic 401 (i.e. credentials wrong).\n */\nexport function parseChallenge(headerValue: string | null): DigestChallenge | null {\n if (!headerValue) return null\n // Strip leading scheme token (case-insensitive). Older Hikvision\n // firmwares prepend extra whitespace + a stray comma — the leading\n // `^\\s*Digest\\s+` match handles both.\n const stripped = headerValue.replace(/^\\s*Digest\\s+/i, '')\n if (stripped === headerValue) return null\n\n // Tokens are `key=value` with value optionally quoted. Hikvision\n // doesn't escape quotes, so a non-greedy quoted match is safe.\n const tokens: Record<string, string> = {}\n const re = /(\\w+)\\s*=\\s*(?:\"([^\"]*)\"|([^,]+))/g\n let m: RegExpExecArray | null\n while ((m = re.exec(stripped)) !== null) {\n const k = m[1]!.toLowerCase()\n const v = (m[2] ?? m[3] ?? '').trim()\n tokens[k] = v\n }\n if (!tokens['realm'] || !tokens['nonce']) return null\n return {\n realm: tokens['realm']!,\n nonce: tokens['nonce']!,\n qop: tokens['qop'] ?? null,\n opaque: tokens['opaque'] ?? null,\n algorithm: tokens['algorithm'] ?? null,\n stale: tokens['stale']?.toLowerCase() === 'true',\n }\n}\n\nconst md5 = (input: string): string =>\n createHash('md5').update(input).digest('hex')\n\n/**\n * Build the `Authorization: Digest ...` header value for one request.\n * Mutates `state.nc` (incremented). Returns `null` when no challenge\n * has been seen yet — the caller should send the request without an\n * Authorization header and absorb the 401 + Authenticate response.\n */\nexport function buildAuthHeader(input: {\n readonly state: DigestState\n readonly method: string\n readonly uri: string\n readonly username: string\n readonly password: string\n}): string | null {\n const ch = input.state.challenge\n if (!ch) return null\n const nc = (++input.state.nc).toString(16).padStart(8, '0')\n const cnonce = randomBytes(8).toString('hex')\n const ha1 = md5(`${input.username}:${ch.realm}:${input.password}`)\n const ha2 = md5(`${input.method}:${input.uri}`)\n const qop = ch.qop?.split(',').map(s => s.trim()).find(s => s === 'auth') ?? null\n const response = qop\n ? md5(`${ha1}:${ch.nonce}:${nc}:${cnonce}:${qop}:${ha2}`)\n : md5(`${ha1}:${ch.nonce}:${ha2}`)\n\n const parts: string[] = [\n `username=\"${input.username}\"`,\n `realm=\"${ch.realm}\"`,\n `nonce=\"${ch.nonce}\"`,\n `uri=\"${input.uri}\"`,\n `response=\"${response}\"`,\n ]\n if (qop) {\n parts.push(`qop=${qop}`)\n parts.push(`nc=${nc}`)\n parts.push(`cnonce=\"${cnonce}\"`)\n }\n if (ch.algorithm) parts.push(`algorithm=${ch.algorithm}`)\n if (ch.opaque) parts.push(`opaque=\"${ch.opaque}\"`)\n return `Digest ${parts.join(', ')}`\n}\n","/**\n * Hikvision accessory subsystem barrel — kind-keyed factory + concrete\n * classes. The provider/parent imports `createAccessoryDevice` to\n * instantiate a child by `AccessoryKind`, and never has to know which\n * concrete class implements each kind.\n */\n\nimport type { DeviceContext, IDevice, AccessoryKindValue } from '@camstack/types'\nimport { AccessoryKind } from '@camstack/types'\nimport { HikvisionLightAccessory, type HikvisionLightHost } from './light.js'\nimport { HikvisionSirenAccessory, type HikvisionSirenHost } from './siren.js'\n\nexport {\n HikvisionLightAccessory,\n lightAccessorySchema,\n type LightAccessoryConfig,\n type HikvisionLightHost,\n} from './light.js'\nexport {\n HikvisionSirenAccessory,\n sirenAccessorySchema,\n type SirenAccessoryConfig,\n type HikvisionSirenHost,\n} from './siren.js'\nexport {\n hikvisionAccessoryBaseSchema,\n isHikvisionSupportedAccessoryKind,\n type HikvisionAccessoryHost,\n} from './base.js'\n\n/**\n * Factory — pick the right concrete class for an `AccessoryKindValue`\n * and instantiate it with the shared `(ctx, parent)` pair. The\n * accessory's persisted config has already been written to the DB\n * by the kernel's `spawnAccessoryChild` (for create) or is already\n * there (for restore); the constructor reads it via\n * `ctx.persistedConfig`.\n *\n * The host parameter must satisfy the union of every per-kind host\n * requirement (light wants `lightSupportedModes`, siren wants\n * `alarmInputPorts`). Hikvision's parent camera implements both\n * directly so the same camera object is passed regardless of kind.\n *\n * Throws on `AccessoryKindValue`s Hikvision doesn't model\n * (PIR / Autotrack / Spotlight / Chime / Nightvision / PrivacyMask)\n * — callers should gate with `isHikvisionSupportedAccessoryKind`\n * before invoking the factory in restore paths where saved rows\n * might carry stale or cross-vendor kinds.\n */\nexport function createAccessoryDevice(\n kind: AccessoryKindValue,\n ctx: DeviceContext,\n parent: HikvisionLightHost & HikvisionSirenHost,\n): IDevice {\n switch (kind) {\n case AccessoryKind.Floodlight:\n return new HikvisionLightAccessory(ctx, parent)\n case AccessoryKind.Siren:\n return new HikvisionSirenAccessory(ctx, parent)\n default:\n throw new Error(`Hikvision provider does not implement accessory kind \"${String(kind)}\"`)\n }\n}\n","/**\n * HikvisionLightAccessory — child IDevice for the on-camera supplemental\n * white-LED spotlight (`/ISAPI/Image/channels/{cam}/supplementLight`).\n *\n * Caps:\n * - `switch` — discrete on/off, sends `<supplementLightMode>`\n * `=close` for off, `=<configured mode>` for on\n * - `brightness` — `<whiteLightBrightness>` 0..100; clamped server-\n * side, only honoured by firmware when the light\n * is currently on\n *\n * State model: switch + brightness live in the runtime-state slice; a\n * 30s background poll re-reads firmware so external changes (mobile\n * app, NVR-shared light config edits) surface in the UI without\n * operator action.\n *\n * Settings:\n * - `mode` — which mode the camera should use when\n * switching the light \"on\". Default seeded\n * from the parent's probe; the select renders\n * the full list of probed modes.\n * - `defaultBrightness` — brightness applied on switch-on when the\n * cap caller doesn't specify one.\n */\n\nimport { z } from 'zod'\nimport {\n BaseDevice,\n DeviceType,\n hydrateSchema,\n switchCapability,\n brightnessCapability,\n type SwitchStatus,\n type BrightnessStatus,\n} from '@camstack/types'\nimport type {\n ConfigUISchema,\n ConfigUISchemaWithValues,\n DeviceContext,\n InferNativeProvider,\n} from '@camstack/types'\nimport {\n hikvisionAccessoryBaseSchema,\n type HikvisionAccessoryHost,\n AccessoryKind,\n} from './base.js'\n\nexport const lightAccessorySchema = hikvisionAccessoryBaseSchema.extend({\n kind: z.literal(AccessoryKind.Floodlight),\n /**\n * Mode passed in `<supplementLightMode>` when switching on. Default\n * seeded from the parent's probe; operator can re-pick from the\n * `lightSupportedModes` list in the settings UI.\n */\n mode: z.string().default('colorVuWhiteLight'),\n /**\n * Brightness (`<whiteLightBrightness>`) applied at switch-on when\n * the brightness cap hasn't been explicitly set yet. 0..100 clamp.\n */\n defaultBrightness: z.number().int().min(0).max(100).default(100),\n})\n\nexport type LightAccessoryConfig = z.infer<typeof lightAccessorySchema>\n\n/**\n * Parent-host accessor that exposes the probed light capability surface\n * to this accessory. The base interface (`HikvisionAccessoryHost`)\n * carries the ISAPI client; this wider shape adds the per-light extras\n * the accessory needs without leaking camera internals.\n */\nexport interface HikvisionLightHost extends HikvisionAccessoryHost {\n /** Modes the camera advertised at probe time. Used to render the\n * `mode` select. Empty array on cameras that don't expose any. */\n lightSupportedModes(): readonly string[]\n}\n\nconst POLL_INTERVAL_MS = 30_000\n\nexport class HikvisionLightAccessory extends BaseDevice<typeof lightAccessorySchema> {\n readonly features = []\n\n private pollTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(ctx: DeviceContext, private readonly parent: HikvisionLightHost) {\n super(ctx, lightAccessorySchema, {\n type: DeviceType.Light,\n role: AccessoryKind.Floodlight,\n })\n this.registerSwitchCap()\n this.registerBrightnessCap()\n this.startPolling()\n }\n\n private get cameraNumber(): number { return this.config.get('cameraNumber') ?? 1 }\n\n /** Mode applied when switching ON. Read fresh from config on every\n * call so a settings save takes effect without a restart. */\n private get onMode(): string {\n return this.config.get('mode') ?? 'colorVuWhiteLight'\n }\n\n private switchView(): SwitchStatus {\n return this.getCapSlice(switchCapability) ?? { on: false, lastChangedAt: 0 }\n }\n private brightnessView(): BrightnessStatus {\n return this.getCapSlice(brightnessCapability)\n ?? { percentage: this.config.get('defaultBrightness') ?? 100, lastChangedAt: 0 }\n }\n\n /** Refresh on/off + brightness from firmware. The lib's\n * `getSupplementLight` returns both in one call so we coalesce them\n * here and write both slices atomically. */\n private async refreshFromFirmware(): Promise<void> {\n try {\n const isapi = this.parent.ensureClient()\n const state = await isapi.getSupplementLight(this.cameraNumber)\n if (!state) return\n const on = state.mode !== 'close'\n const ts = Date.now()\n const previousSwitch = this.switchView()\n const switchChangedAt = previousSwitch.on === on ? previousSwitch.lastChangedAt : ts\n this.setCapSlice(switchCapability, { on, lastChangedAt: switchChangedAt })\n if (typeof state.brightness === 'number') {\n const previousBrightness = this.brightnessView()\n const brightChangedAt = previousBrightness.percentage === state.brightness\n ? previousBrightness.lastChangedAt\n : ts\n this.setCapSlice(brightnessCapability, { percentage: state.brightness, lastChangedAt: brightChangedAt })\n }\n } catch (err) {\n this.ctx.logger.debug('hikvision light refresh failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n private async applyOn(on: boolean, brightness: number): Promise<void> {\n const isapi = this.parent.ensureClient()\n const mode = on ? this.onMode : 'close'\n // Push white-light brightness when arming (white-light cameras\n // honour `whiteLightBrightness` only while the corresponding mode\n // is active). Mode-only patch when turning off.\n await isapi.setSupplementLight(\n this.cameraNumber,\n on ? { mode, whiteLightBrightness: brightness } : { mode },\n )\n const ts = Date.now()\n this.setCapSlice(switchCapability, { on, lastChangedAt: ts })\n if (on) this.setCapSlice(brightnessCapability, { percentage: brightness, lastChangedAt: ts })\n }\n\n private startPolling(): void {\n if (this.pollTimer) return\n this.pollTimer = setInterval(() => { void this.refreshFromFirmware() }, POLL_INTERVAL_MS)\n }\n\n override async removeDevice(): Promise<void> {\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n private registerSwitchCap(): void {\n const provider: InferNativeProvider<typeof switchCapability> = {\n // Read straight from the runtime-state slice — the 30s background\n // poll + setState writes keep it fresh. CLAUDE.md anti-pattern:\n // don't refresh-on-every-getStatus; the slice IS the cache.\n getStatus: async (): Promise<SwitchStatus> => this.switchView(),\n setState: async ({ deviceId, on }) => {\n if (deviceId !== this.id) return\n const brightness = this.brightnessView().percentage\n await this.applyOn(on, brightness)\n },\n }\n this.ctx.registerNativeCap(switchCapability, provider)\n this.setCapSlice(switchCapability, { on: false, lastChangedAt: 0 })\n }\n\n private registerBrightnessCap(): void {\n const provider: InferNativeProvider<typeof brightnessCapability> = {\n // Slice-driven; same anti-pattern fix as switch.\n getStatus: async (): Promise<BrightnessStatus> => this.brightnessView(),\n setBrightness: async ({ deviceId, percentage }) => {\n if (deviceId !== this.id) return\n const value = Math.max(0, Math.min(100, percentage))\n // Lib accepts brightness only when the LED is on; otherwise\n // stash so the next setState({on:true}) re-issues with this\n // value baked in.\n if (this.switchView().on) {\n await this.applyOn(true, value)\n } else {\n this.setCapSlice(brightnessCapability, { percentage: value, lastChangedAt: Date.now() })\n }\n },\n }\n this.ctx.registerNativeCap(brightnessCapability, provider)\n this.setCapSlice(brightnessCapability, {\n percentage: this.config.get('defaultBrightness') ?? 100,\n lastChangedAt: 0,\n })\n }\n\n override getSettingsUISchema(): ConfigUISchemaWithValues {\n const supportedModes = this.parent.lightSupportedModes()\n const modeOptions = supportedModes\n .filter((m) => m !== 'close')\n .map((m) => ({ value: m, label: friendlyModeLabel(m) }))\n\n const schema: ConfigUISchema = {\n sections: [{\n id: 'light',\n title: 'Supplemental Light',\n description: 'Mode + default brightness used by the switch and brightness caps. The mode list is what the camera advertised at probe time.',\n columns: 2,\n fields: [\n modeOptions.length > 0\n ? {\n type: 'select',\n key: 'mode',\n label: 'Mode (on)',\n description: 'Sent in `<supplementLightMode>` when switching on. Default picked at probe time.',\n default: 'colorVuWhiteLight',\n options: modeOptions,\n }\n : {\n type: 'text',\n key: 'mode',\n label: 'Mode (on)',\n description: 'No mode list was harvested at probe time — type the mode string the camera expects (e.g. `colorVuWhiteLight`, `manual`).',\n default: 'colorVuWhiteLight',\n },\n {\n type: 'number',\n key: 'defaultBrightness',\n label: 'Default brightness',\n description: 'Brightness 0..100 applied on switch-on; the operator can override via the brightness cap, but a fresh restart starts here.',\n default: 100,\n min: 0,\n max: 100,\n step: 1,\n },\n ],\n }],\n }\n return hydrateSchema(schema, {\n mode: this.config.get('mode') ?? 'colorVuWhiteLight',\n defaultBrightness: this.config.get('defaultBrightness') ?? 100,\n })\n }\n\n override async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n const typed = lightAccessorySchema.partial().parse(patch)\n await this.config.setAll(typed)\n // If the operator changed `defaultBrightness` while OFF, refresh\n // the cached slice so the next read returns the new value.\n if (typed.defaultBrightness !== undefined && !this.switchView().on) {\n this.setCapSlice(brightnessCapability, {\n percentage: typed.defaultBrightness,\n lastChangedAt: Date.now(),\n })\n }\n // Mode change while ON: re-issue setSupplementLight with the new\n // mode + cached brightness so the camera switches the active\n // illumination mode immediately.\n if (typed.mode !== undefined && this.switchView().on) {\n await this.applyOn(true, this.brightnessView().percentage).catch((err: unknown) => {\n this.ctx.logger.warn('hikvision light: live mode switch failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n }\n\n}\n\n/**\n * Render Hikvision's terse mode strings into operator-friendly labels.\n * Unknown modes pass through verbatim — better to surface the raw\n * vendor token than to drop it from the select.\n */\nfunction friendlyModeLabel(mode: string): string {\n switch (mode) {\n case 'colorVuWhiteLight': return 'ColorVu White Light'\n case 'manual': return 'Manual'\n case 'irLight': return 'IR Light'\n case 'mixed': return 'Mixed (IR + White)'\n case 'auto': return 'Auto'\n case 'schedule': return 'Scheduled'\n case 'eventTrigger': return 'Event Trigger'\n default: return mode\n }\n}\n","/**\n * Hikvision accessory base layer — host contract + shared schema bits\n * every per-kind subclass extends.\n *\n * Each accessory kind (Light, Siren) lives in its own file with its\n * own Zod schema, settings UI, and cap registration — they're\n * independent device classes that share only the parent reference and\n * the `cameraNumber` field (Hikvision encodes channels as\n * `{cameraNumber}{streamSlot}` — `1` for single-camera devices,\n * `2`/`3`/... for multi-channel NVRs).\n *\n * Mirrors Reolink's per-file accessory pattern:\n * - Co-located schema + UI + class makes the kind self-contained.\n * - The parent doesn't know which caps each kind exposes — it only\n * declares \"spawn a Siren child\" via `getAccessoryChildren()`.\n * - Adding a new kind (e.g. RelayOutput) is one new file + one\n * factory case, no edits to existing kinds.\n */\n\nimport { z } from 'zod'\nimport {\n AccessoryKind,\n type AccessoryKindValue,\n} from '@camstack/types'\nimport type { HikvisionIsapiClient } from '../hikvision-isapi-client.js'\n\n/**\n * Shared base fields every accessory subclass extends. The\n * `cameraNumber` mirror is the parent-camera ISAPI channel offset\n * (`1` for single-camera devices, `2+` on multi-channel NVRs); siren\n * accessories ignore it (alarm IO is device-wide on Hikvision) but\n * carry it for shape parity.\n *\n * The `kind` field uses the **system-wide** `AccessoryKind` enum —\n * not a Hikvision-specific narrow. Each per-kind subclass schema\n * locks down `kind: z.literal(AccessoryKind.X)` for its own value.\n */\nexport const hikvisionAccessoryBaseSchema = z.object({\n cameraNumber: z.number().int().positive().default(1),\n kind: z.string(),\n})\n\n/**\n * Anything an accessory subclass needs from the parent camera. The\n * subclass receives this rather than a direct `HikvisionCamera`\n * reference so:\n * - The accessory file doesn't import the camera class (avoids a\n * circular import the camera class doesn't need).\n * - Tests can stub the host with a tiny fake instead of standing up\n * a full camera + ISAPI client.\n * - The contract is explicit: subclasses call `ensureClient()` for\n * a typed ISAPI client and read the parent's name for fallback\n * log tags.\n */\nexport interface HikvisionAccessoryHost {\n readonly name: string\n ensureClient(): HikvisionIsapiClient\n}\n\n/** Re-export the system enum + values type so callers (factory) can\n * build their dispatch tables off a single import. */\nexport { AccessoryKind, type AccessoryKindValue }\n\n/**\n * Runtime guard: the two kinds Hikvision currently models out of the\n * full `AccessoryKind` set (Light → supplemental white light,\n * Siren → alarm output). The factory's `switch` is the canonical\n * per-kind dispatch; this helper exists for the restore path, where\n * saved rows can carry stale string values from older revisions and\n * the addon needs a quick \"do we still know how to spawn this?\"\n * check before attempting instantiation.\n */\nexport function isHikvisionSupportedAccessoryKind(value: string): value is AccessoryKindValue {\n return (\n value === AccessoryKind.Floodlight ||\n value === AccessoryKind.Siren\n )\n}\n","/**\n * HikvisionSirenAccessory — child IDevice for the on-camera siren\n * driven via the alarm-input mechanism (`/ISAPI/System/IO/inputs/{port}`).\n *\n * Caps:\n * - `switch` — discrete on/off via `setAlarmInput(on, port)`. The\n * actual audible/visual response is configured on the\n * camera-side \"Linkage Method\" — this cap only flips\n * the input state and the camera fires the linkage.\n *\n * Settings:\n * - `port` — alarm input port id (`/ISAPI/System/IO/inputs/{N}`).\n * Default 1 (the most common GPIO slot for the\n * built-in siren). Multi-input cameras can pick\n * a different port via the settings UI.\n * - `pulseDurationMs` — auto-release the input after N ms (0 =\n * manual release).\n *\n * Notes on the persisted-state model: Hikvision's `getAlarmInput` GET\n * reflects the *configured* state, not the *active* trigger. The\n * runtime-state slice is the authoritative source for operator-driven\n * flips; a 30s background poll re-reads firmware so external changes\n * (camera-side linkage, panel toggle) surface in the UI.\n */\n\nimport { z } from 'zod'\nimport {\n BaseDevice,\n DeviceType,\n hydrateSchema,\n switchCapability,\n type SwitchStatus,\n} from '@camstack/types'\nimport type {\n ConfigUISchema,\n ConfigUISchemaWithValues,\n DeviceContext,\n InferNativeProvider,\n} from '@camstack/types'\nimport {\n hikvisionAccessoryBaseSchema,\n type HikvisionAccessoryHost,\n AccessoryKind,\n} from './base.js'\n\nexport const sirenAccessorySchema = hikvisionAccessoryBaseSchema.extend({\n kind: z.literal(AccessoryKind.Siren),\n /**\n * GPIO port id (`/ISAPI/System/IO/inputs/{port}`). Default 1.\n * Multi-input cameras can pick a different port via the settings UI;\n * the select is populated from the parent's probe.\n */\n port: z.number().int().positive().default(1),\n /**\n * Optional auto-release window. When >0, an `on:true` flip schedules\n * an `on:false` flip after this many milliseconds, mimicking a\n * one-shot pulse. When 0, the operator must flip back manually.\n */\n pulseDurationMs: z.number().int().min(0).max(600_000).default(0),\n})\n\nexport type SirenAccessoryConfig = z.infer<typeof sirenAccessorySchema>\n\n/**\n * Parent-host accessor that exposes the probed alarm-input surface to\n * this accessory. The base interface (`HikvisionAccessoryHost`) carries\n * the ISAPI client; this wider shape adds the per-siren extras the\n * accessory needs without leaking camera internals.\n */\nexport interface HikvisionSirenHost extends HikvisionAccessoryHost {\n /** Alarm input port ids the camera advertised at probe time. Used\n * to render the `port` select. Empty array on single-port devices\n * (the schema default of `1` is then the only valid choice). */\n alarmInputPorts(): readonly number[]\n}\n\nconst POLL_INTERVAL_MS = 30_000\n\nexport class HikvisionSirenAccessory extends BaseDevice<typeof sirenAccessorySchema> {\n readonly features = []\n\n /** Pending auto-release timer fired after `pulseDurationMs`. */\n private pulseTimer: ReturnType<typeof setTimeout> | null = null\n private pollTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(ctx: DeviceContext, private readonly parent: HikvisionSirenHost) {\n super(ctx, sirenAccessorySchema, {\n type: DeviceType.Siren,\n role: AccessoryKind.Siren,\n })\n this.registerSwitchCap()\n this.startPolling()\n }\n\n private get port(): number { return this.config.get('port') ?? 1 }\n\n private cancelPulseTimer(): void {\n if (!this.pulseTimer) return\n clearTimeout(this.pulseTimer)\n this.pulseTimer = null\n }\n\n private switchView(): SwitchStatus {\n return this.getCapSlice(switchCapability) ?? { on: false, lastChangedAt: 0 }\n }\n\n /** Refresh the alarm-input state from firmware. ISAPI's\n * `getAlarmInput` reflects configured state, not the active trigger,\n * so a poll is meaningful only to surface changes made by other\n * clients (panel toggle, ISAPI script). */\n private async refreshFromFirmware(): Promise<void> {\n const isapi = this.parent.ensureClient()\n const reader = (isapi as { getAlarmInput?: (port: number) => Promise<{ active?: boolean } | null> }).getAlarmInput\n if (!reader) return\n try {\n const state = await reader.call(isapi, this.port)\n const on = Boolean(state?.active)\n const previous = this.switchView()\n const lastChangedAt = previous.on === on ? previous.lastChangedAt : Date.now()\n this.setCapSlice(switchCapability, { on, lastChangedAt })\n } catch (err) {\n this.ctx.logger.debug('hikvision siren refresh failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n\n private async pushAlarmInput(on: boolean): Promise<void> {\n const isapi = this.parent.ensureClient()\n await isapi.setAlarmInput(on, this.port)\n this.setCapSlice(switchCapability, { on, lastChangedAt: Date.now() })\n }\n\n private startPolling(): void {\n if (this.pollTimer) return\n this.pollTimer = setInterval(() => { void this.refreshFromFirmware() }, POLL_INTERVAL_MS)\n }\n\n private registerSwitchCap(): void {\n const provider: InferNativeProvider<typeof switchCapability> = {\n // Read straight from the runtime-state slice — the 30s\n // background poll + setState writes keep it fresh. CLAUDE.md\n // anti-pattern: don't refresh-on-every-getStatus; the slice IS\n // the cache.\n getStatus: async (): Promise<SwitchStatus> => this.switchView(),\n setState: async ({ deviceId, on }) => {\n if (deviceId !== this.id) return\n // Cancel any in-flight pulse — a fresh flip supersedes the\n // pending auto-release. Symmetric on both `on:true` (operator\n // re-arms the pulse) and `on:false` (operator manually releases\n // before the timer fires).\n this.cancelPulseTimer()\n await this.pushAlarmInput(on)\n\n const pulseMs = this.config.get('pulseDurationMs') ?? 0\n if (on && pulseMs > 0) {\n this.pulseTimer = setTimeout(() => {\n this.pulseTimer = null\n this.pushAlarmInput(false).catch((err: unknown) => {\n this.ctx.logger.warn('hikvision siren: pulse auto-release failed', {\n meta: {\n port: this.port,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n })\n }, pulseMs)\n }\n },\n }\n this.ctx.registerNativeCap(switchCapability, provider)\n this.setCapSlice(switchCapability, { on: false, lastChangedAt: 0 })\n }\n\n override getSettingsUISchema(): ConfigUISchemaWithValues {\n const probed = this.parent.alarmInputPorts()\n const current = this.config.get('port') ?? 1\n const probedSummary = probed.length > 0\n ? `Detected ports: ${probed.join(', ')}.`\n : 'No probe data (single-input camera).'\n\n const schema: ConfigUISchema = {\n sections: [{\n id: 'siren',\n title: 'Siren / Alarm Output',\n description: 'GPIO port flipped by the switch cap, plus an optional auto-release pulse. The actual siren tone / strobe is configured on the camera-side `Linkage Method` — this cap only fires the input.',\n columns: 2,\n fields: [\n {\n type: 'number',\n key: 'port',\n label: 'Alarm input port',\n description: `GPIO port id (\\`/ISAPI/System/IO/inputs/{port}\\`). Defaults to 1 — change only if your camera has multiple inputs and the built-in siren is wired to a different one. ${probedSummary}`,\n default: 1,\n min: 1,\n max: 32,\n step: 1,\n },\n {\n type: 'number',\n key: 'pulseDurationMs',\n label: 'Auto-release after (ms)',\n description: 'When >0, the switch auto-flips back to off after this many milliseconds, simulating a one-shot pulse. 0 = manual release.',\n default: 0,\n min: 0,\n max: 600_000,\n step: 100,\n },\n ],\n }],\n }\n return hydrateSchema(schema, {\n port: current,\n pulseDurationMs: this.config.get('pulseDurationMs') ?? 0,\n })\n }\n\n override async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n const typed = sirenAccessorySchema.partial().parse(patch)\n const previousPort = this.port\n await this.config.setAll(typed)\n // Port change while currently ON: release the OLD port immediately\n // — leaving it triggered after the operator switched the accessory\n // to a different GPIO is unsafe.\n if (typed.port !== undefined && this.switchView().on && typed.port !== previousPort) {\n try {\n const isapi = this.parent.ensureClient()\n await isapi.setAlarmInput(false, previousPort)\n } catch (err) {\n this.ctx.logger.warn('hikvision siren: post-port-change release failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n this.setCapSlice(switchCapability, { on: false, lastChangedAt: Date.now() })\n this.cancelPulseTimer()\n }\n }\n\n override async removeDevice(): Promise<void> {\n this.cancelPulseTimer()\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n if (this.switchView().on) {\n try {\n await this.pushAlarmInput(false)\n } catch (err) {\n this.ctx.logger.debug('hikvision siren: best-effort release on removeDevice failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n}\n","/**\n * IntercomOrchestrator (Hikvision flavor) — wires WebRTC peer +\n * audio-codec decode + a vendor-specific talk session into a single\n * lifecycle the `intercom` cap router drives.\n *\n * Pipeline:\n *\n * browser ─ Opus RTP ─▶ IntercomWebrtcPeer (werift)\n * │\n * ▼ onOpusFrame(payload)\n * audio-codec cap (decode + resample to 8 kHz mono)\n * │\n * ▼ pullPcm(...)\n * IIntercomTalkSession.feedPcm(buf)\n * │\n * ▼ G.711 encode\n * camera ISAPI sticky `audioData` PUT\n *\n * NOTE — Phase 1 duplication: this orchestrator mirrors\n * `addon-provider-reolink/src/intercom-orchestrator.ts` with the only\n * substantive difference being the `talkSession` is supplied by the\n * caller (instead of being constructed inline as a `ReolinkIntercomSession`).\n * Phase 1.5 will hoist a single shared orchestrator + talk-session\n * interface into a common package; today the two providers each carry\n * their own copy because cross-addon imports are forbidden.\n */\n\nimport type { IScopedLogger } from '@camstack/types'\n\n/** Vendor-neutral talk-session contract. The orchestrator drives any\n * implementation that exposes this surface — Reolink (IMA ADPCM) and\n * Hikvision (G.711 sticky-PUT) both fit. */\nexport interface IIntercomTalkSession {\n /** True once `start()` resolved and `stop()` hasn't been called. */\n readonly isOpen: boolean\n /** Negotiated PCM source rate the caller MUST resample to before\n * calling `feedPcm`. The orchestrator passes this verbatim into\n * `audio-codec.createDecodeSession({ targetSampleRate })`. */\n readonly sampleRate: number\n start(): Promise<void>\n feedPcm(buf: Buffer): void\n stop(): Promise<void>\n}\n\n/** Audio-codec cap façade the orchestrator needs. Mirrors a subset of\n * `audioCodecCapability`'s methods so the call site can pass the full\n * `ctx.api.audioCodec` router straight through. */\nexport interface IntercomAudioCodecApi {\n createDecodeSession(input: {\n codec: string\n sourceSampleRate: number\n sourceChannels: number\n targetSampleRate: number\n targetChannels: number\n /** Output PCM format. Defaults to 'f32le' on the cap side; the\n * intercom orchestrator pins it to 's16le' so the G.711 encoder\n * can read the buffer as Int16Array. */\n targetFormat?: 'f32le' | 's16le'\n tag?: string\n }): Promise<{ sessionId: string; nodeId: string }>\n pushEncodedFrame(input: {\n sessionId: string\n nodeId?: string\n data: Uint8Array\n pts?: number\n }): Promise<void>\n pullPcm(input: {\n sessionId: string\n nodeId?: string\n maxCount: number\n }): Promise<readonly { data: Uint8Array; sampleRate: number; channels: number; pts: number }[]>\n closeSession(input: { sessionId: string; nodeId?: string }): Promise<void>\n}\n\n/** Minimal WebRTC peer contract the orchestrator drives. */\nexport interface IntercomWebrtcPeer {\n createOffer(): Promise<{ sdp: string }>\n setAnswer(sdp: string): Promise<void>\n onOpusFrame(cb: (frame: Buffer, pts: number) => void): void\n close(): Promise<void>\n}\n\n/** Factory passed into the orchestrator so the DI seam stays small. */\nexport type IntercomWebrtcPeerFactory = (deps: { logger: IScopedLogger }) => IntercomWebrtcPeer\n\n/** Factory for the camera-side talk session. Lazy-built per session so\n * the orchestrator can fail-fast on the camera leg before allocating\n * WebRTC + codec resources. */\nexport type IntercomTalkSessionFactory = (deps: { logger: IScopedLogger }) => IIntercomTalkSession\n\nexport interface IntercomOrchestratorOptions {\n readonly deviceTag: string\n readonly logger: IScopedLogger\n readonly audioCodec: IntercomAudioCodecApi\n readonly peerFactory: IntercomWebrtcPeerFactory\n readonly talkSessionFactory: IntercomTalkSessionFactory\n /** Opus codec params the browser ships. Default 48 kHz mono. */\n readonly opusSampleRate?: number\n readonly opusChannels?: number\n}\n\ninterface ActiveSession {\n readonly sessionId: string\n readonly peer: IntercomWebrtcPeer\n readonly talkSession: IIntercomTalkSession\n readonly codec: { sessionId: string; nodeId: string }\n closed: boolean\n readonly startedAtMs: number\n framesPushed: number\n pcmBytesPushed: number\n}\n\nconst DEFAULT_OPUS_SAMPLE_RATE = 48_000\nconst DEFAULT_OPUS_CHANNELS = 1\n\nexport class IntercomOrchestrator {\n private session: ActiveSession | null = null\n\n constructor(private readonly opts: IntercomOrchestratorOptions) {}\n\n get isOpen(): boolean { return this.session !== null && !this.session.closed }\n\n async start(): Promise<{ sessionId: string; sdpOffer: string }> {\n if (this.session && !this.session.closed) {\n await this.stop(this.session.sessionId).catch(() => { /* swallow */ })\n }\n\n const sessionId = generateSessionId()\n const opusRate = this.opts.opusSampleRate ?? DEFAULT_OPUS_SAMPLE_RATE\n const opusChannels = this.opts.opusChannels ?? DEFAULT_OPUS_CHANNELS\n\n // 1. Camera-facing talk session FIRST so we fail fast on a\n // rejecting/unreachable camera before allocating WebRTC + codec.\n const sessionLogger = this.opts.logger.withTags?.({ sessionId }) ?? this.opts.logger\n const talkSession = this.opts.talkSessionFactory({ logger: sessionLogger })\n try {\n await talkSession.start()\n } catch (err) {\n this.opts.logger.warn('intercom: talk session open failed', {\n meta: { error: errMsg(err) },\n })\n throw err\n }\n const targetRate = talkSession.sampleRate\n\n // 2. Audio-codec decode session — Opus → PCM s16le @ camera rate.\n let codec: { sessionId: string; nodeId: string }\n try {\n codec = await this.opts.audioCodec.createDecodeSession({\n codec: 'opus',\n sourceSampleRate: opusRate,\n sourceChannels: opusChannels,\n targetSampleRate: targetRate,\n targetChannels: 1,\n // CRITICAL: explicitly request s16le PCM. The cap defaults to\n // 'f32le' (32-bit float) and the G.711 encoder reads the buffer\n // as Int16Array — mismatch produces pure noise on the camera\n // speaker. See Reolink intercom for the same fix.\n targetFormat: 's16le',\n tag: `intercom:${this.opts.deviceTag}:${sessionId}`,\n })\n } catch (err) {\n await talkSession.stop().catch(() => { /* swallow */ })\n this.opts.logger.warn('intercom: audio-codec createDecodeSession failed', {\n meta: { error: errMsg(err), opusRate, opusChannels, targetRate },\n })\n throw err\n }\n\n // 3. WebRTC peer — last resource opened so cleanup ordering\n // matches the open ordering in reverse.\n const peer = this.opts.peerFactory({ logger: sessionLogger })\n const active: ActiveSession = {\n sessionId,\n peer,\n talkSession,\n codec,\n closed: false,\n startedAtMs: Date.now(),\n framesPushed: 0,\n pcmBytesPushed: 0,\n }\n this.session = active\n\n peer.onOpusFrame((frame, pts) => {\n void this.handleOpusFrame(active, frame, pts).catch((err: unknown) => {\n this.opts.logger.debug('intercom: opus frame pump error (dropped)', {\n meta: { error: errMsg(err) },\n })\n })\n })\n\n let offer: { sdp: string }\n try {\n offer = await peer.createOffer()\n } catch (err) {\n await this.stop(sessionId).catch(() => { /* swallow */ })\n this.opts.logger.warn('intercom: webrtc createOffer failed', {\n meta: { error: errMsg(err) },\n })\n throw err\n }\n\n this.opts.logger.info('intercom session opened', {\n meta: {\n sessionId,\n targetRate,\n codecSessionId: codec.sessionId,\n codecNodeId: codec.nodeId,\n },\n })\n return { sessionId, sdpOffer: offer.sdp }\n }\n\n async handleAnswer(sessionId: string, sdpAnswer: string): Promise<void> {\n const active = this.requireActive(sessionId)\n await active.peer.setAnswer(sdpAnswer)\n this.opts.logger.debug('intercom: SDP answer accepted', {\n meta: { sessionId },\n })\n }\n\n async stop(sessionId: string): Promise<void> {\n const active = this.session\n if (!active || active.sessionId !== sessionId || active.closed) return\n active.closed = true\n this.session = null\n\n try {\n await active.peer.close()\n } catch (err) {\n this.opts.logger.debug('intercom: peer.close error (continuing)', {\n meta: { sessionId, error: errMsg(err) },\n })\n }\n try {\n await this.opts.audioCodec.closeSession({\n sessionId: active.codec.sessionId,\n nodeId: active.codec.nodeId,\n })\n } catch (err) {\n this.opts.logger.debug('intercom: audio-codec.closeSession error (continuing)', {\n meta: { sessionId, error: errMsg(err) },\n })\n }\n try {\n await active.talkSession.stop()\n } catch (err) {\n this.opts.logger.debug('intercom: talk-session.stop error (continuing)', {\n meta: { sessionId, error: errMsg(err) },\n })\n }\n this.opts.logger.info('intercom session closed', {\n meta: {\n sessionId,\n framesPushed: active.framesPushed,\n pcmBytesPushed: active.pcmBytesPushed,\n durationMs: Date.now() - active.startedAtMs,\n },\n })\n }\n\n private async handleOpusFrame(active: ActiveSession, frame: Buffer, pts: number): Promise<void> {\n if (active.closed) return\n active.framesPushed += 1\n await this.opts.audioCodec.pushEncodedFrame({\n sessionId: active.codec.sessionId,\n nodeId: active.codec.nodeId,\n data: frame,\n pts,\n })\n if (active.closed) return\n const chunks = await this.opts.audioCodec.pullPcm({\n sessionId: active.codec.sessionId,\n nodeId: active.codec.nodeId,\n maxCount: 8,\n })\n if (active.closed) return\n for (const chunk of chunks) {\n const buf = Buffer.from(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength)\n active.pcmBytesPushed += buf.length\n active.talkSession.feedPcm(buf)\n }\n }\n\n private requireActive(sessionId: string): ActiveSession {\n const active = this.session\n if (!active || active.closed || active.sessionId !== sessionId) {\n throw new Error(`Hikvision intercom: unknown or closed sessionId ${sessionId}`)\n }\n return active\n }\n}\n\nfunction generateSessionId(): string {\n return `intercom-${Date.now().toString(36)}-${Math.floor(Math.random() * 0xffffff).toString(36)}`\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n","/**\n * G.711 PCM ↔ µ-law / A-law encoders.\n *\n * Hikvision's ISAPI two-way audio channel typically negotiates one of:\n * - `G.711ulaw` → ITU-T G.711 µ-law (PCM_MULAW)\n * - `G.711alaw` → ITU-T G.711 A-law (PCM_ALAW)\n *\n * Wire shape: 8 kHz, mono, 8-bit per sample (one byte per PCM sample).\n * Camera reads the long-running `audioData` PUT body as a contiguous\n * byte stream of these samples — no header, no framing, no length\n * prefix. Just raw bytes written at roughly real-time.\n *\n * The encoders here are pure functions: PCM s16le in, encoded byte\n * buffer out. No I/O, no logging, no allocation beyond the output\n * Buffer. Easy to unit-test against reference vectors.\n */\n\nconst BIAS = 0x84\nconst CLIP = 32635\n\n/**\n * µ-law encode a single 16-bit signed PCM sample. Standard ITU-T G.711\n * lookup-free implementation — branches on sign + segment exponent.\n * Output is the inverted 8-bit code that the wire format expects.\n */\nfunction pcm16ToUlawSample(pcm: number): number {\n let sample = pcm\n let sign = (sample >> 8) & 0x80\n if (sign !== 0) sample = -sample\n if (sample > CLIP) sample = CLIP\n sample = sample + BIAS\n\n let exponent = 7\n for (let mask = 0x4000; (sample & mask) === 0 && exponent > 0; mask >>= 1) {\n exponent--\n }\n const mantissa = (sample >> (exponent + 3)) & 0x0f\n const ulaw = ~(sign | (exponent << 4) | mantissa) & 0xff\n return ulaw\n}\n\n/**\n * A-law encode a single 16-bit signed PCM sample. Mirrors the µ-law\n * pattern but with A-law's segmentation (top bit XOR'd with 0x55 by\n * convention to spread bit-flip errors across the wire).\n */\nfunction pcm16ToAlawSample(pcm: number): number {\n let sample = pcm\n let sign = (~sample >> 8) & 0x80\n if (sign === 0) sample = -sample\n if (sample > CLIP) sample = CLIP\n\n let alaw: number\n if (sample >= 256) {\n let exponent = 7\n for (let mask = 0x4000; (sample & mask) === 0 && exponent > 0; mask >>= 1) {\n exponent--\n }\n const mantissa = (sample >> (exponent + 3)) & 0x0f\n alaw = (exponent << 4) | mantissa\n } else {\n alaw = sample >> 4\n }\n return (alaw ^ sign ^ 0x55) & 0xff\n}\n\n/**\n * Encode a buffer of PCM s16le mono samples to G.711 µ-law. Output\n * length is exactly `pcm.length / 2` bytes (one sample per byte).\n * Input length MUST be even — odd-length buffers throw, since a\n * trailing half-sample would corrupt the wire alignment.\n */\nexport function encodePcm16ToUlaw(pcm: Buffer): Buffer {\n if ((pcm.length & 1) !== 0) {\n throw new Error(`encodePcm16ToUlaw: odd-length PCM buffer (${pcm.length})`)\n }\n const samples = new Int16Array(pcm.buffer, pcm.byteOffset, pcm.length / 2)\n const out = Buffer.allocUnsafe(samples.length)\n for (let i = 0; i < samples.length; i++) {\n out[i] = pcm16ToUlawSample(samples[i]!)\n }\n return out\n}\n\n/**\n * Encode a buffer of PCM s16le mono samples to G.711 A-law. Same\n * shape as `encodePcm16ToUlaw` — odd-length input rejected.\n */\nexport function encodePcm16ToAlaw(pcm: Buffer): Buffer {\n if ((pcm.length & 1) !== 0) {\n throw new Error(`encodePcm16ToAlaw: odd-length PCM buffer (${pcm.length})`)\n }\n const samples = new Int16Array(pcm.buffer, pcm.byteOffset, pcm.length / 2)\n const out = Buffer.allocUnsafe(samples.length)\n for (let i = 0; i < samples.length; i++) {\n out[i] = pcm16ToAlawSample(samples[i]!)\n }\n return out\n}\n\n/**\n * Wire-codec discriminator. Maps the strings Hikvision returns from\n * `/ISAPI/System/TwoWayAudio/channels` (`G.711ulaw`, `G.711alaw`,\n * sometimes spelled `G711U` / `G711A`) onto an internal enum the\n * intercom session uses to pick the encoder. Unknown values fall back\n * to µ-law — Scrypted does the same; G.711 µ-law is the firmware's\n * out-of-the-box default on every Hikvision model we've probed.\n */\nexport type HikvisionAudioCodec = 'g711ulaw' | 'g711alaw'\n\nexport function resolveHikvisionAudioCodec(raw: string | null | undefined): HikvisionAudioCodec {\n if (!raw) return 'g711ulaw'\n const norm = raw.replace(/[\\s.\\-_]/g, '').toLowerCase()\n if (norm.includes('alaw') || norm === 'g711a') return 'g711alaw'\n return 'g711ulaw'\n}\n\nexport function encodePcm16(pcm: Buffer, codec: HikvisionAudioCodec): Buffer {\n return codec === 'g711alaw' ? encodePcm16ToAlaw(pcm) : encodePcm16ToUlaw(pcm)\n}\n","/**\n * HikvisionIntercomSession — wraps Hikvision's ISAPI two-way audio\n * channel (`/ISAPI/System/TwoWayAudio/channels/{id}/...`) with a\n * PCM-in / G.711-out forwarding pump.\n *\n * Wire shape, per Hikvision firmware:\n * 1. GET `/channels` → discover negotiated audio codec\n * 2. PUT `/channels/{id}/open` → arm the channel\n * 3. PUT `/channels/{id}/audioData` (sticky body) → encoded bytes\n * 4. PUT `/channels/{id}/close` → release the channel\n *\n * Lifecycle (mirrors `ReolinkIntercomSession`'s public surface so the\n * shared orchestrator can drive both):\n * - `start()` → discover codec, open channel, open sticky PUT,\n * prime backlog cap.\n * - `feedPcm()` → enqueue PCM s16le @ 8 kHz mono, encode to G.711,\n * write into the sticky PUT body. Backlog clamping\n * drops the oldest samples when the encoder/network\n * can't keep up.\n * - `stop()` → end the sticky PUT body and PUT `/close`.\n * Idempotent.\n *\n * The session deliberately knows NOTHING about WebRTC, audio-codec\n * decode sessions, or the broker. The orchestrator owns those pieces;\n * this class is the camera-facing leg only.\n */\n\nimport type { IScopedLogger } from '@camstack/types'\nimport type { AudioDataStream, HikvisionIsapiClient } from '../hikvision-isapi-client.js'\nimport {\n encodePcm16,\n resolveHikvisionAudioCodec,\n type HikvisionAudioCodec,\n} from './g711-encoder.js'\n\nconst DEFAULT_BACKLOG_MS = 200\nconst MAX_BACKLOG_MS = 5_000\nconst MIN_BACKLOG_MS = 20\n\n/**\n * Hikvision two-way audio is locked to 8 kHz mono on every model we've\n * probed (G.711 µ-law / A-law). The audio-codec cap MUST resample to\n * this rate before feeding PCM into `feedPcm()`; the orchestrator\n * passes the value into `createDecodeSession` as `targetSampleRate`.\n */\nexport const HIKVISION_INTERCOM_SAMPLE_RATE = 8_000\n\nexport interface HikvisionIntercomSessionOptions {\n readonly client: HikvisionIsapiClient\n readonly logger: IScopedLogger\n /** Stable id for diagnostic logging (\"camera-{deviceId}\"). */\n readonly deviceTag: string\n /** Channel id to open. Defaults to `'1'` (single-cam standard). */\n readonly channelId?: string\n /** Bound on PCM backlog → trades latency for stability. Default: 200ms. */\n readonly maxBacklogMs?: number\n /**\n * Force a specific codec, bypassing `/TwoWayAudio/channels` discovery.\n * Useful for older firmwares whose channel list 404s but accept\n * `audioData` writes regardless. Leave unset to auto-discover.\n */\n readonly forcedCodec?: HikvisionAudioCodec\n}\n\nexport class HikvisionIntercomSession {\n private stream: AudioDataStream | null = null\n private channelId: string | null = null\n private codec: HikvisionAudioCodec = 'g711ulaw'\n private pcmBuffer: Buffer = Buffer.alloc(0)\n private maxBacklogBytes = 0\n private bytesPerSecond = 0\n private lastBacklogClampLogAtMs = 0\n private streamErrored = false\n\n constructor(private readonly opts: HikvisionIntercomSessionOptions) {}\n\n /** True once `start()` has resolved and not yet been `stop()`'d. */\n get isOpen(): boolean { return this.stream !== null }\n\n /**\n * Sample rate the camera negotiated. Hikvision's ISAPI two-way audio\n * is fixed at 8 kHz mono regardless of the parent stream's audio\n * config — exposed as a getter to mirror `ReolinkIntercomSession`.\n */\n get sampleRate(): number {\n return HIKVISION_INTERCOM_SAMPLE_RATE\n }\n\n /** Wire codec the session resolved (g711ulaw / g711alaw). */\n get audioCodec(): HikvisionAudioCodec { return this.codec }\n\n async start(): Promise<void> {\n if (this.stream) return\n const desiredChannel = this.opts.channelId ?? '1'\n\n // 1. Discover codec. Older Hikvision firmwares (DS-7600 series) 404\n // on `/TwoWayAudio/channels`; we tolerate that and fall back to\n // µ-law (matches the Scrypted reference impl behavior).\n if (this.opts.forcedCodec) {\n this.codec = this.opts.forcedCodec\n this.channelId = desiredChannel\n } else {\n try {\n const channels = await this.opts.client.listTwoWayAudioChannels()\n const match = channels.find((c) => c.id === desiredChannel) ?? channels[0]\n if (!match) {\n throw new Error('hikvision intercom: no TwoWayAudio channels advertised by camera')\n }\n this.channelId = match.id\n this.codec = resolveHikvisionAudioCodec(match.audioCompressionType)\n } catch (err) {\n this.opts.logger.warn('intercom: TwoWayAudio channel discovery failed — defaulting to g711ulaw', {\n meta: {\n channelId: desiredChannel,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n this.channelId = desiredChannel\n this.codec = 'g711ulaw'\n }\n }\n const channelId = this.channelId\n\n // 2. Arm the channel. Failure here is fatal — the sticky PUT below\n // relies on the camera being in TwoWayAudio mode.\n await this.opts.client.openTwoWayAudio(channelId)\n\n // 3. Open the sticky audioData PUT and wire its lifecycle.\n const stream = this.opts.client.openAudioDataStream(channelId)\n // The PUT typically resolves only once we close it (or the camera\n // tears the connection). Detach the response watcher so an error\n // mid-stream tears down our session cleanly.\n stream.responsePromise\n .then(() => {\n this.opts.logger.debug('intercom: audioData PUT completed', {\n meta: { channelId },\n })\n })\n .catch((err) => {\n if (this.streamErrored) return\n this.streamErrored = true\n this.opts.logger.warn('intercom: audioData stream error — closing session', {\n meta: {\n channelId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n // Best-effort teardown; close hits the camera even if the\n // sticky PUT half-ended.\n void this.stop().catch(() => { /* swallow */ })\n })\n\n const wantedBacklogMs = Math.max(\n MIN_BACKLOG_MS,\n Math.min(MAX_BACKLOG_MS, this.opts.maxBacklogMs ?? DEFAULT_BACKLOG_MS),\n )\n // PCM s16le mono → 2 bytes per sample; sample rate is the cap.\n this.bytesPerSecond = HIKVISION_INTERCOM_SAMPLE_RATE * 2\n this.maxBacklogBytes = Math.max(\n // Always allow at least one full frame's worth (10ms = 80 samples = 160 bytes).\n 160,\n Math.floor((wantedBacklogMs / 1000) * this.bytesPerSecond),\n )\n this.stream = stream\n this.pcmBuffer = Buffer.alloc(0)\n this.streamErrored = false\n this.opts.logger.info('intercom talk session opened', {\n meta: {\n channelId,\n codec: this.codec,\n sampleRate: HIKVISION_INTERCOM_SAMPLE_RATE,\n backlogMs: wantedBacklogMs,\n maxBacklogBytes: this.maxBacklogBytes,\n },\n })\n }\n\n /**\n * Feed a chunk of PCM s16le @ 8 kHz mono. Returns immediately after\n * enqueueing — encode + write happens synchronously on the same\n * tick because G.711 is byte-cheap. Calls before `start()` (or after\n * `stop()`) silently drop the chunk.\n */\n feedPcm(pcm: Buffer): void {\n if (!this.stream) return\n if (pcm.length === 0) return\n if ((pcm.length & 1) !== 0) {\n // Half-sample at the tail would corrupt encode alignment — drop\n // the last byte rather than smear it across the next chunk.\n pcm = pcm.subarray(0, pcm.length - 1)\n if (pcm.length === 0) return\n }\n this.pcmBuffer = this.pcmBuffer.length\n ? Buffer.concat([this.pcmBuffer, pcm])\n : pcm\n\n // Backlog clamping — drop the OLDEST samples once the buffer\n // exceeds the configured window. Aligned to 16-bit sample size\n // so the next encode pass doesn't get a half-sample.\n if (this.pcmBuffer.length > this.maxBacklogBytes) {\n const keep = this.maxBacklogBytes - (this.maxBacklogBytes % 2)\n const dropped = this.pcmBuffer.length - keep\n this.pcmBuffer = this.pcmBuffer.subarray(this.pcmBuffer.length - keep)\n const now = Date.now()\n if (now - this.lastBacklogClampLogAtMs > 2_000) {\n this.lastBacklogClampLogAtMs = now\n this.opts.logger.warn('intercom backlog clamped (dropping PCM)', {\n meta: { droppedBytes: dropped, keptBytes: keep, maxBytes: this.maxBacklogBytes },\n })\n }\n }\n\n // Encode + write synchronously. G.711 is one byte per sample —\n // there's no rate limiting needed; the underlying TCP socket will\n // backpressure via `write()` returning false (we honour neither\n // direction explicitly because the backlog clamp above already\n // bounds memory usage on slow links).\n const encoded = encodePcm16(this.pcmBuffer, this.codec)\n this.pcmBuffer = Buffer.alloc(0)\n if (encoded.length > 0) {\n this.stream.write(encoded)\n }\n }\n\n /**\n * Tear down in reverse order: end the sticky PUT body so the camera\n * stops reading from us, then PUT `/close` to release the channel.\n * Idempotent — calling twice is safe.\n */\n async stop(): Promise<void> {\n const stream = this.stream\n const channelId = this.channelId\n this.stream = null\n this.pcmBuffer = Buffer.alloc(0)\n if (stream) {\n try { stream.end() } catch { /* swallow */ }\n // Wait briefly for the response so the socket gets drained, but\n // never block teardown on a misbehaving camera.\n try {\n await Promise.race([\n stream.responsePromise.then(() => undefined),\n new Promise<void>((r) => setTimeout(r, 250)),\n ])\n } catch { /* swallow */ }\n }\n if (channelId) {\n try {\n await Promise.race([\n this.opts.client.closeTwoWayAudio(channelId),\n new Promise<void>((_, rej) => setTimeout(() => rej(new Error('hikvision close timeout')), 2_000)),\n ])\n } catch (err) {\n this.opts.logger.warn('intercom session close error', {\n meta: {\n channelId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n this.channelId = null\n }\n }\n}\n","/**\n * werift-backed `IntercomWebrtcPeer` impl. Audio-only sendrecv peer:\n * the browser pushes Opus mic up, we push nothing back (the talk-back\n * leg goes via the camera-side ISAPI sticky PUT, not WebRTC).\n *\n * NOTE — Phase 1 duplication: this file mirrors\n * `addon-provider-reolink/src/intercom-webrtc-peer.ts` almost verbatim.\n * The Phase 1.5 cleanup will hoist a single `WeriftIntercomPeer` into a\n * shared package both providers depend on. Cross-addon imports are\n * forbidden today (per CLAUDE.md), so duplicate-and-tag is the\n * pragmatic path until the shared lib lands.\n */\n\nimport type { IScopedLogger } from '@camstack/types'\nimport type { IntercomWebrtcPeer } from './intercom-orchestrator.js'\n\ninterface WeriftRtpPacket {\n payload: Buffer\n header: { timestamp: number; sequenceNumber: number }\n}\ninterface WeriftMediaStreamTrack {\n kind: 'audio' | 'video'\n onReceiveRtp: { subscribe(cb: (pkt: WeriftRtpPacket) => void): { unsubscribe?: () => void } | void }\n}\ninterface WeriftTransceiver {\n onTrack: { subscribe(cb: (track: WeriftMediaStreamTrack) => void): { unsubscribe?: () => void } | void }\n}\ninterface WeriftSessionDescription {\n sdp: string\n type: 'offer' | 'answer' | 'pranswer' | 'rollback'\n}\ninterface WeriftPeerConnection {\n createOffer(): Promise<WeriftSessionDescription>\n setLocalDescription(desc: WeriftSessionDescription): Promise<void>\n setRemoteDescription(desc: WeriftSessionDescription): Promise<void>\n addTransceiver(\n track: WeriftMediaStreamTrack | string,\n options: { direction: 'sendrecv' | 'recvonly' | 'sendonly' | 'inactive' },\n ): WeriftTransceiver\n iceConnectionStateChange: { subscribe(cb: (state: string) => void): { unsubscribe?: () => void } | void }\n iceGatheringStateChange: { subscribe(cb: (state: string) => void): { unsubscribe?: () => void } | void }\n localDescription: WeriftSessionDescription | null\n close(): Promise<void> | void\n}\ninterface WeriftModule {\n RTCPeerConnection: new (options?: WeriftPcOptions) => WeriftPeerConnection\n MediaStreamTrack: new (init: { kind: 'audio' | 'video' }) => WeriftMediaStreamTrack\n}\ninterface WeriftIceServer {\n urls: string\n username?: string\n credential?: string\n}\ninterface WeriftPcOptions {\n iceServers?: WeriftIceServer[]\n iceTransportPolicy?: 'all' | 'relay'\n}\n\nlet _werift: WeriftModule | undefined\n\nasync function loadWerift(): Promise<WeriftModule> {\n if (_werift) return _werift\n try {\n const moduleName = 'werift'\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call -- intentional dynamic import\n _werift = await (Function('m', 'return import(m)')(moduleName) as Promise<WeriftModule>)\n return _werift!\n } catch {\n throw new Error(\n \"The 'werift' package is required for Hikvision intercom support but is not installed. \" +\n 'Install it with: npm install werift',\n )\n }\n}\n\nexport interface IntercomWebrtcPeerOptions {\n readonly logger: IScopedLogger\n readonly iceServers?: readonly WeriftIceServer[]\n}\n\nexport class WeriftIntercomPeer implements IntercomWebrtcPeer {\n private pc: WeriftPeerConnection | null = null\n private opusCallbacks: Array<(frame: Buffer, pts: number) => void> = []\n private rtpUnsubscribe: (() => void) | null = null\n private trackUnsubscribe: (() => void) | null = null\n private closed = false\n private firstRtpTimestamp: number | null = null\n\n constructor(private readonly opts: IntercomWebrtcPeerOptions) {}\n\n onOpusFrame(cb: (frame: Buffer, pts: number) => void): void {\n if (this.closed) return\n this.opusCallbacks.push(cb)\n }\n\n async createOffer(): Promise<{ sdp: string }> {\n if (this.pc) throw new Error('WeriftIntercomPeer: createOffer called twice')\n const werift = await loadWerift()\n const pcOptions: WeriftPcOptions = {}\n if (this.opts.iceServers && this.opts.iceServers.length > 0) {\n pcOptions.iceServers = [...this.opts.iceServers]\n }\n const pc = new werift.RTCPeerConnection(pcOptions)\n this.pc = pc\n\n const localTrack = new werift.MediaStreamTrack({ kind: 'audio' })\n const transceiver = pc.addTransceiver(localTrack, { direction: 'sendrecv' })\n\n const trackSub = transceiver.onTrack.subscribe((track) => {\n if (track.kind !== 'audio') return\n const rtpSub = track.onReceiveRtp.subscribe((pkt) => {\n if (this.closed) return\n const payload = pkt.payload\n if (!payload || payload.length === 0) return\n const ts = pkt.header.timestamp\n if (this.firstRtpTimestamp === null) this.firstRtpTimestamp = ts\n const relTs = (ts - this.firstRtpTimestamp) >>> 0\n // Opus RTP clock is 48 kHz → ms.\n const ptsMs = Math.round(relTs / 48)\n for (const cb of this.opusCallbacks) {\n try { cb(payload, ptsMs) } catch (err) {\n this.opts.logger.debug('intercom-peer: opus callback threw', {\n meta: { error: errMsg(err) },\n })\n }\n }\n })\n if (rtpSub && typeof rtpSub.unsubscribe === 'function') {\n this.rtpUnsubscribe = () => rtpSub.unsubscribe?.()\n }\n })\n if (trackSub && typeof trackSub.unsubscribe === 'function') {\n this.trackUnsubscribe = () => trackSub.unsubscribe?.()\n }\n\n pc.iceConnectionStateChange.subscribe((state) => {\n this.opts.logger.info('intercom-peer: ICE state', { meta: { state } })\n })\n pc.iceGatheringStateChange.subscribe((state) => {\n this.opts.logger.debug('intercom-peer: ICE gathering', { meta: { state } })\n })\n\n const offer = await pc.createOffer()\n await pc.setLocalDescription(offer)\n const localSdp = pc.localDescription?.sdp ?? offer.sdp\n return { sdp: localSdp }\n }\n\n async setAnswer(sdp: string): Promise<void> {\n if (!this.pc) throw new Error('WeriftIntercomPeer: setAnswer called before createOffer')\n if (this.closed) throw new Error('WeriftIntercomPeer: setAnswer called on closed peer')\n await this.pc.setRemoteDescription({ sdp, type: 'answer' })\n }\n\n async close(): Promise<void> {\n if (this.closed) return\n this.closed = true\n this.opusCallbacks = []\n if (this.rtpUnsubscribe) {\n try { this.rtpUnsubscribe() } catch { /* swallow */ }\n this.rtpUnsubscribe = null\n }\n if (this.trackUnsubscribe) {\n try { this.trackUnsubscribe() } catch { /* swallow */ }\n this.trackUnsubscribe = null\n }\n if (this.pc) {\n try { await Promise.resolve(this.pc.close()) } catch { /* swallow */ }\n this.pc = null\n }\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n","/**\n * Hardware-metadata populator for `HikvisionCamera`.\n *\n * Mirrors `addon-provider-reolink/src/metadata-populator.ts`:\n * 1. Pulls every host-level identification probe ISAPI exposes\n * (`/ISAPI/System/deviceInfo` + `/ISAPI/System/Network/interfaces/1`).\n * 2. Patches the device-meta `metadata` blob via the kernel's\n * `device-manager.setMetadata` cap (consumed by the device-detail\n * Hardware sub-tab — read-only for operators).\n * 3. Refreshes the driver's persisted `deviceCache` blob so the\n * durable identifiers (`mac`, `serialNumber`) survive a restart\n * even when the camera is offline and `generateStableId` needs\n * to re-key on next add.\n *\n * Both writes are idempotent — `setMetadata` short-circuits no-diff\n * patches and `DeviceConfig.setAll(partial)` shallow-merges over the\n * current cache. Failure paths are best-effort: a transient ISAPI\n * error leaves whatever the previous probe wrote in place.\n */\n\nimport type { DeviceContext } from '@camstack/types'\nimport type { HikvisionIsapiClient } from './hikvision-isapi-client.js'\n\nexport interface HikvisionMetadataPopulatorTarget {\n readonly id: number\n readonly ctx: DeviceContext\n /** Persist patch helper — same `this.config.setAll(partial)` API the\n * driver already uses for `deviceCache` updates. */\n readonly setDeviceCachePatch: (cachePatch: Record<string, unknown>) => Promise<void>\n}\n\nexport async function populateHikvisionMetadata(\n isapi: HikvisionIsapiClient,\n target: HikvisionMetadataPopulatorTarget,\n): Promise<void> {\n let info: Awaited<ReturnType<HikvisionIsapiClient['getDeviceInfo']>>\n try {\n info = await isapi.getDeviceInfo()\n } catch (err) {\n target.ctx.logger.info('populateHikvisionMetadata: getDeviceInfo failed — skipping populate', {\n tags: { deviceId: target.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n return\n }\n\n let net: Awaited<ReturnType<HikvisionIsapiClient['getNetworkInterfaceInfo']>> | null = null\n try {\n net = await isapi.getNetworkInterfaceInfo()\n } catch (err) {\n target.ctx.logger.debug('populateHikvisionMetadata: getNetworkInterfaceInfo failed (non-fatal)', {\n tags: { deviceId: target.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n\n // ── 1. device-meta `metadata` blob ───────────────────────────────\n const patch: Record<string, unknown> = {\n manufacturer: 'Hikvision',\n ...(info.model ? { model: info.model } : {}),\n ...(info.firmwareVersion ? { firmware: info.firmwareVersion } : {}),\n ...(info.firmwareVersionInfo ? { firmwareDetail: info.firmwareVersionInfo } : {}),\n ...(info.firmwareReleasedDate ? { firmwareReleasedAt: info.firmwareReleasedDate } : {}),\n ...(info.hardwareVersion ? { hardware: info.hardwareVersion } : {}),\n ...(info.serialNumber ? { serialNumber: info.serialNumber } : {}),\n ...(info.deviceType ? { isapiDeviceType: info.deviceType } : {}),\n ...(info.deviceName ? { reportedName: info.deviceName } : {}),\n ...(net?.mac ? { mac: net.mac } : {}),\n ...(net?.ip ? { ip: net.ip } : {}),\n ...(net?.subnetMask ? { subnetMask: net.subnetMask } : {}),\n ...(net?.defaultGateway ? { defaultGateway: net.defaultGateway } : {}),\n ...(net?.addressingType ? { addressingType: net.addressingType } : {}),\n ...(net?.speed ? { linkSpeed: net.speed } : {}),\n ...(net?.mtu ? { mtu: net.mtu } : {}),\n }\n try {\n await (target.ctx.api as { deviceManager?: { setMetadata?: { mutate?: (input: { deviceId: number; patch: Record<string, unknown> }) => Promise<void> } } } | undefined)\n ?.deviceManager?.setMetadata?.mutate?.({ deviceId: target.id, patch })\n target.ctx.logger.info('populateHikvisionMetadata: metadata patched', {\n tags: { deviceId: target.id },\n meta: { keys: Object.keys(patch) },\n })\n } catch (err) {\n target.ctx.logger.warn('populateHikvisionMetadata: setMetadata failed', {\n tags: { deviceId: target.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n\n // ── 2. deviceCache config blob (durable identifiers for stableId)\n const cachePatch: Record<string, unknown> = {}\n if (info.serialNumber) cachePatch['serialNumber'] = info.serialNumber\n if (info.firmwareVersion) cachePatch['firmwareVersion'] = info.firmwareVersion\n if (info.firmwareVersionInfo) cachePatch['firmwareVersionInfo'] = info.firmwareVersionInfo\n if (info.hardwareVersion) cachePatch['hardwareVersion'] = info.hardwareVersion\n if (info.deviceType) cachePatch['isapiDeviceType'] = info.deviceType\n if (net?.mac) cachePatch['mac'] = net.mac\n if (Object.keys(cachePatch).length > 0) {\n try {\n await target.setDeviceCachePatch(cachePatch)\n } catch (err) {\n target.ctx.logger.debug('populateHikvisionMetadata: deviceCache patch failed (non-fatal)', {\n tags: { deviceId: target.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n}\n"],"mappings":"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","names":["import_types","import_types","HttpAgent","HttpsAgent","httpsRequest","httpRequest","import_types","import_zod","import_types","import_zod","import_zod","import_types","POLL_INTERVAL_MS","errMsg"]}
|