@apocaliss92/camstack-js-sdk 0.1.5

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/proxy-client.ts","../src/direct-client.ts","../src/types.ts","../src/client.ts","../src/detection.ts","../src/devices.ts","../src/features.ts"],"sourcesContent":["// ─── Clients ─────────────────────────────────────────────────────────\nexport { CamStackClient, createCamStackClient } from \"./client.js\";\nexport { ProxyClient } from \"./proxy-client.js\";\nexport { DirectClient } from \"./direct-client.js\";\n\n// ─── Client config types ────────────────────────────────────────────\nexport type {\n CamStackClientConfig,\n ProxyClientConfig,\n DirectClientConfig,\n HlsInfo,\n ProviderInfo,\n} from \"./types.js\";\nexport { isProxyConfig } from \"./types.js\";\n\n// ─── Detection classes ──────────────────────────────────────────────\nexport {\n DetectionClass,\n animalClasses,\n personClasses,\n vehicleClasses,\n faceClasses,\n licensePlateClasses,\n motionClasses,\n packageClasses,\n audioClasses,\n audioLabelClasses,\n doorbellClasses,\n sensorLabelClasses,\n detectionClassesDefaultMap,\n isFaceClassname,\n isPlateClassname,\n isAnimalClassname,\n isPersonClassname,\n isVehicleClassname,\n isMotionClassname,\n isDoorbellClassname,\n isPackageClassname,\n isAudioClassname,\n isSensorLabelClassname,\n isLabelDetection,\n getParentClass,\n getParentDetectionClass,\n defaultDetectionClasses,\n DEFAULT_ENABLED_CLASSES,\n TIMELINE_PRESET_CRITICAL,\n TIMELINE_PRESET_IMPORTANT,\n TIMELINE_PRESET_ALL,\n getClassesForTimelinePreset,\n} from \"./detection.js\";\nexport type { TimelineEventPreset } from \"./detection.js\";\n\n// ─── Device types ───────────────────────────────────────────────────\nexport {\n RAW_TO_CANONICAL,\n HA_DOMAIN_TYPE_MAP,\n SCRYPTED_TYPE_TO_CANONICAL,\n getCanonicalDeviceType,\n ELIGIBLE_SCRYPTED_DEVICE_TYPES,\n ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET,\n ELIGIBLE_HA_DOMAINS,\n ELIGIBLE_HA_DOMAINS_SET,\n} from \"./devices.js\";\nexport type {\n CanonicalDeviceType,\n DeviceCommandAction,\n AssociatedDeviceState,\n Device,\n DeviceCommand,\n CommandResult,\n AssociatedDeviceSource,\n EligibleDevice,\n} from \"./devices.js\";\n\n// ─── Timeline & events ─────────────────────────────────────────────\nexport type {\n TimelineEventSeverity,\n MotionFragmentType,\n DetectionEvent,\n MotionItem,\n TimelineArtifact,\n AudioLevelSegment,\n TimelineRecordingSegment,\n CameraDayDataResponse,\n ClusteredDayDataResponse,\n TimelineCluster,\n DetectionGroup,\n ClusteredDayDataQuery,\n GroupedEventsQuery,\n GroupedEventsResult,\n ClusterEventsQuery,\n ClusterEventsResult,\n ReelEvent,\n ReelEventsQuery,\n ReelEventsResult,\n // Backward-compat aliases\n TimelineDetectionEvent,\n TimelineMotionItem,\n TimelineArtifactItem,\n TimelineClusterFromServer,\n} from \"./timeline.js\";\n\n// ─── Camera & PTZ ───────────────────────────────────────────────────\nexport type {\n CameraSourceType,\n StreamMethod,\n PanTiltZoomCommand,\n PanTiltZoomCapabilities,\n CameraAccessorySwitchKind,\n CameraStatusEntry,\n CamerasStatusResult,\n} from \"./camera.js\";\n\n// ─── Feature matrix ─────────────────────────────────────────────────\nexport {\n FEATURE_MATRIX,\n isFeatureAvailable,\n getSourceFeatures,\n getBackendRequiredFeatures,\n} from \"./features.js\";\nexport type {\n FeatureId,\n PlatformId,\n SourceType,\n FeatureEntry,\n} from \"./features.js\";\n\n// ─── NVR types ──────────────────────────────────────────────────────\nexport type {\n StreamOption,\n NvrCamera,\n StreamInfo,\n NormalizedBoundingBox,\n DetectionData,\n EventSnapshotData,\n NvrEvent,\n RecordingSegment,\n MotionBucket,\n LiveEvent,\n LiveEventType,\n VideoClipsQuery,\n NvrVideoClip,\n VideoClipsResult,\n VideoClipUrlResult,\n CameraDayData,\n CameraStatus,\n EventsQuery,\n MotionQuery,\n RecordingsQuery,\n NvrConfig,\n ProviderConfigs,\n} from \"./nvr.js\";\n","/**\n * Proxy client — connects to camstack-js-proxy via tRPC.\n * Full features: queries, mutations, subscriptions, WebRTC, HLS.\n */\n\nimport {\n createTRPCClient,\n httpBatchLink,\n splitLink,\n createWSClient,\n wsLink,\n} from \"@trpc/client\";\nimport superjson from \"superjson\";\nimport type { ProxyClientConfig, HlsInfo, ProviderInfo } from \"./types.js\";\nimport type {\n NvrCamera,\n NvrConfig,\n NvrEvent,\n RecordingSegment,\n MotionBucket,\n StreamInfo,\n LiveEvent,\n EventsQuery,\n MotionQuery,\n RecordingsQuery,\n} from \"./nvr.js\";\nimport type { AudioLevelSegment } from \"./timeline.js\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyRouter = any;\n\nexport class ProxyClient {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private trpc: any;\n private baseUrl: string;\n\n constructor(config: ProxyClientConfig) {\n this.baseUrl = config.proxyUrl.replace(/\\/+$/, \"\");\n const trpcUrl = `${this.baseUrl}/trpc`;\n\n const headers: Record<string, string> = {};\n if (config.token) {\n headers[\"Authorization\"] = `Bearer ${config.token}`;\n }\n\n // WebSocket URL for subscriptions (ws:// or wss:// based on protocol)\n // Browser WebSocket API doesn't support custom headers, so pass token as query param\n const wsProtocol = this.baseUrl.startsWith(\"https\") ? \"wss\" : \"ws\";\n const wsHost = this.baseUrl.replace(/^https?:\\/\\//, \"\");\n const wsUrl = config.token\n ? `${wsProtocol}://${wsHost}/trpc?token=${encodeURIComponent(config.token)}`\n : `${wsProtocol}://${wsHost}/trpc`;\n\n const wsClient = createWSClient({ url: wsUrl });\n\n this.trpc = createTRPCClient<AnyRouter>({\n links: [\n splitLink({\n condition: (op: { type: string }) => op.type === \"subscription\",\n true: wsLink({ client: wsClient, transformer: superjson }),\n false: httpBatchLink({\n url: trpcUrl,\n transformer: superjson,\n headers,\n }),\n }),\n ],\n });\n }\n\n // ─── Providers ───────────────────────────────────────────────────\n\n async listProviders(): Promise<ProviderInfo[]> {\n return this.trpc.providers.list.query() as Promise<ProviderInfo[]>;\n }\n\n // ─── Config ────────────────────────────────────────────────────\n\n async getConfig(providerId?: string): Promise<NvrConfig> {\n return this.trpc.config.getConfig.query({ providerId }) as Promise<NvrConfig>;\n }\n\n async getStreams(providerId?: string): Promise<StreamInfo[]> {\n return this.trpc.config.getStreams.query({ providerId }) as Promise<StreamInfo[]>;\n }\n\n // ─── Cameras ───────────────────────────────────────────────────\n\n async listCameras(providerId?: string): Promise<NvrCamera[]> {\n return this.trpc.cameras.list.query({ providerId }) as Promise<NvrCamera[]>;\n }\n\n async getSnapshot(camera: string, providerId?: string): Promise<string> {\n return this.trpc.cameras.getSnapshot.query({ camera, providerId }) as Promise<string>;\n }\n\n // ─── Events ────────────────────────────────────────────────────\n\n async getEvents(params: EventsQuery & { providerId?: string }): Promise<NvrEvent[]> {\n return this.trpc.events.list.query(params) as Promise<NvrEvent[]>;\n }\n\n async getRecordings(params: RecordingsQuery & { providerId?: string }): Promise<RecordingSegment[]> {\n return this.trpc.events.getRecordings.query(params) as Promise<RecordingSegment[]>;\n }\n\n async getMotionActivity(params: MotionQuery & { providerId?: string }): Promise<MotionBucket[]> {\n return this.trpc.events.getMotionActivity.query(params) as Promise<MotionBucket[]>;\n }\n\n async getAudioSegments(\n camera: string,\n after: number,\n before: number,\n providerId?: string,\n ): Promise<AudioLevelSegment[]> {\n return this.trpc.events.getAudioSegments.query({ camera, after, before, providerId }) as Promise<AudioLevelSegment[]>;\n }\n\n async getEventThumbnail(eventId: string, providerId?: string): Promise<string> {\n return this.trpc.events.getEventThumbnail.query({ eventId, providerId }) as Promise<string>;\n }\n\n async getEventSnapshot(eventId: string, providerId?: string): Promise<string> {\n return this.trpc.events.getEventSnapshot.query({ eventId, providerId }) as Promise<string>;\n }\n\n // ─── WebRTC ────────────────────────────────────────────────────\n\n async webrtcOffer(streamName: string, sdpOffer: string, providerId?: string): Promise<string> {\n const result = await (this.trpc.webrtc.offer.mutate({ streamName, sdpOffer, providerId }) as Promise<{ sdpAnswer: string }>);\n return result.sdpAnswer;\n }\n\n // ─── HLS ───────────────────────────────────────────────────────\n\n async getRecordingHls(\n camera: string,\n startTime: number,\n endTime: number,\n providerId?: string,\n ): Promise<string> {\n const result = await (this.trpc.hls.getRecordingHls.query({\n camera,\n startTime,\n endTime,\n providerId,\n }) as Promise<HlsInfo>);\n return `${this.baseUrl}${result.hlsUrl}`;\n }\n\n async getEventHls(eventId: string, providerId?: string): Promise<string> {\n const result = await (this.trpc.hls.getEventHls.query({ eventId, providerId }) as Promise<HlsInfo>);\n return `${this.baseUrl}${result.hlsUrl}`;\n }\n\n // ─── Live subscriptions ───────────────────────────────────────\n\n subscribeLiveEvents(\n callback: (event: LiveEvent) => void,\n options?: {\n cameras?: string[];\n types?: string[];\n providerId?: string;\n onError?: (err: unknown) => void;\n onStarted?: () => void;\n onStopped?: () => void;\n },\n ): { unsubscribe: () => void } {\n const { onError, onStarted, onStopped, ...subOptions } = options ?? {};\n return this.trpc.live.onEvent.subscribe(subOptions, {\n onData: callback,\n onError: onError ?? ((err: unknown) => console.error(\"[ProxyClient] subscription error:\", err)),\n onStarted: onStarted ?? (() => console.log(\"[ProxyClient] subscription started\")),\n onStopped: onStopped ?? (() => console.log(\"[ProxyClient] subscription stopped\")),\n });\n }\n\n subscribeMotion(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n return this.trpc.live.onMotion.subscribe(\n { camera, providerId },\n { onData: callback },\n );\n }\n\n subscribeDetection(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n return this.trpc.live.onDetection.subscribe(\n { camera, providerId },\n { onData: callback },\n );\n }\n\n subscribeAudioLevel(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n return this.trpc.live.onAudioLevel.subscribe(\n { camera, providerId },\n { onData: callback },\n );\n }\n}\n","/**\n * Direct client — connects to NVR HTTP API without a proxy.\n * Limited functionality: queries only, no subscriptions, no audio tracking.\n */\n\nimport type { DirectClientConfig } from \"./types.js\";\nimport type {\n NvrCamera,\n NvrConfig,\n NvrEvent,\n RecordingSegment,\n MotionBucket,\n StreamInfo,\n EventsQuery,\n MotionQuery,\n RecordingsQuery,\n} from \"./nvr.js\";\nimport type { AudioLevelSegment } from \"./timeline.js\";\n\nexport class DirectClient {\n private baseUrl: string;\n private type: string;\n private username?: string;\n private password?: string;\n private token: string | null = null;\n\n constructor(config: DirectClientConfig) {\n this.baseUrl = config.directUrl.replace(/\\/+$/, \"\");\n this.type = config.type;\n this.username = config.username;\n this.password = config.password;\n }\n\n // ─── HTTP helpers ──────────────────────────────────────────────\n\n private authHeaders(): Record<string, string> {\n if (this.token) return { Authorization: `Bearer ${this.token}` };\n if (this.username && this.password) {\n const basic = btoa(`${this.username}:${this.password}`);\n return { Authorization: `Basic ${basic}` };\n }\n return {};\n }\n\n private async request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n ...this.authHeaders(),\n ...(init?.headers as Record<string, string>),\n };\n const res = await fetch(url, { ...init, headers });\n if (!res.ok) throw new Error(`NVR API ${res.status}: ${path}`);\n return res;\n }\n\n // ─── Config ────────────────────────────────────────────────────\n\n async getConfig(): Promise<NvrConfig> {\n const res = await this.request(\"/api/config\");\n const raw = (await res.json()) as { version?: string; [key: string]: unknown };\n return { type: this.type, version: raw.version, raw };\n }\n\n async getStreams(): Promise<StreamInfo[]> {\n if (this.type !== \"frigate\") return [];\n for (const path of [\"/api/go2rtc/streams\", \"/go2rtc/streams\"]) {\n try {\n const res = await this.request(path);\n const data = (await res.json()) as Record<string, unknown>;\n return Object.keys(data).map((name) => ({\n name,\n camera: name.split(\"_\")[0],\n }));\n } catch {\n /* try next */\n }\n }\n return [];\n }\n\n // ─── Cameras ───────────────────────────────────────────────────\n\n async listCameras(): Promise<NvrCamera[]> {\n const config = await this.getConfig();\n const raw = config.raw as { cameras?: Record<string, any> } | undefined;\n const cameras = raw?.cameras ?? {};\n const streams = await this.getStreams();\n const streamNames = streams.map((s) => s.name);\n\n return Object.entries(cameras).map(([name, cam]) => {\n const matching = streamNames.filter((s) => {\n const sl = s.toLowerCase();\n const cn = name.toLowerCase();\n return sl === cn || sl.startsWith(`${cn}_`) || sl.startsWith(`${cn}.`);\n });\n return {\n name,\n label: name,\n enabled: cam.enabled !== false,\n hasAudio: cam.audio?.enabled === true,\n hasPtz: !!cam.onvif?.host,\n streams: matching,\n streamOptions: [],\n };\n });\n }\n\n async getSnapshot(camera: string): Promise<string> {\n const res = await this.request(\n `/api/${encodeURIComponent(camera)}/latest.jpg`,\n );\n const blob = await res.blob();\n const buffer = await blob.arrayBuffer();\n // Return base64 (consistent with proxy client)\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n }\n\n // ─── Events ────────────────────────────────────────────────────\n\n async getEvents(params: EventsQuery): Promise<NvrEvent[]> {\n const qs = new URLSearchParams();\n if (params.after != null) qs.set(\"after\", String(params.after));\n if (params.before != null) qs.set(\"before\", String(params.before));\n if (params.cameras?.length) qs.set(\"cameras\", params.cameras.join(\",\"));\n if (params.labels?.length) qs.set(\"labels\", params.labels.join(\",\"));\n qs.set(\"limit\", String(params.limit ?? 10000));\n const res = await this.request(`/api/events?${qs}`);\n const raw = (await res.json()) as Array<{\n id: string;\n camera: string;\n label: string;\n start_time: number;\n end_time: number | null;\n severity?: string;\n has_clip?: boolean;\n has_snapshot?: boolean;\n }>;\n return raw.map((e) => ({\n id: e.id,\n camera: e.camera,\n label: e.label,\n startTime: e.start_time * 1000,\n endTime: e.end_time != null ? e.end_time * 1000 : null,\n severity: e.severity,\n hasClip: e.has_clip === true,\n hasSnapshot: e.has_snapshot === true,\n thumbnailPath: `/api/events/${e.id}/thumbnail.jpg`,\n snapshotPath: `/api/events/${e.id}/snapshot.jpg`,\n clipPath: e.has_clip ? `/api/events/${e.id}/clip.mp4` : undefined,\n }));\n }\n\n async getRecordings(params: RecordingsQuery): Promise<RecordingSegment[]> {\n const qs = new URLSearchParams();\n qs.set(\"after\", String(params.after));\n qs.set(\"before\", String(params.before));\n const res = await this.request(\n `/api/${encodeURIComponent(params.camera)}/recordings?${qs}`,\n );\n const data = await res.json();\n const raw = Array.isArray(data) ? data : [];\n return raw.map(\n (r: {\n id?: string;\n start_time: number;\n end_time: number;\n duration: number;\n motion?: number;\n objects?: number;\n dBFS?: number;\n }) => ({\n id: r.id ?? `${params.camera}-${r.start_time}-${r.end_time}`,\n camera: params.camera,\n startTime: r.start_time * 1000,\n endTime: r.end_time * 1000,\n duration: r.duration * 1000,\n motion: r.motion,\n objects: r.objects,\n dBFS: r.dBFS,\n }),\n );\n }\n\n async getMotionActivity(params: MotionQuery): Promise<MotionBucket[]> {\n const qs = new URLSearchParams();\n qs.set(\"after\", String(params.after));\n qs.set(\"before\", String(params.before));\n if (params.scale != null) qs.set(\"scale\", String(params.scale));\n if (params.cameras?.length) qs.set(\"cameras\", params.cameras.join(\",\"));\n const res = await this.request(`/api/review/activity/motion?${qs}`);\n const data = await res.json();\n const raw = Array.isArray(data) ? data : [];\n return raw.map(\n (m: {\n camera: string;\n start_time: number;\n motion?: number;\n audio?: number;\n }) => ({\n camera: m.camera,\n startTime: m.start_time * 1000,\n motion: m.motion,\n audio: m.audio,\n }),\n );\n }\n\n /** Audio segments are not available in direct mode (tracked by proxy only). */\n async getAudioSegments(\n _camera: string,\n _after: number,\n _before: number,\n ): Promise<AudioLevelSegment[]> {\n return [];\n }\n\n async getEventThumbnail(eventId: string): Promise<string> {\n const res = await this.request(\n `/api/events/${encodeURIComponent(eventId)}/thumbnail.jpg`,\n );\n const blob = await res.blob();\n const buffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n }\n\n async getEventSnapshot(eventId: string): Promise<string> {\n const res = await this.request(\n `/api/events/${encodeURIComponent(eventId)}/snapshot.jpg`,\n );\n const blob = await res.blob();\n const buffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n }\n\n // ─── WebRTC (direct WHEP to go2rtc) ──────────────────────────\n\n async webrtcOffer(streamName: string, sdpOffer: string): Promise<string> {\n for (const basePath of [\"/api/go2rtc/webrtc\", \"/go2rtc/api/webrtc\"]) {\n try {\n const qs = new URLSearchParams({ src: streamName });\n const res = await this.request(`${basePath}?${qs}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/sdp\" },\n body: sdpOffer,\n });\n return await res.text();\n } catch {\n /* try next */\n }\n }\n throw new Error(`WebRTC SDP failed for stream: ${streamName}`);\n }\n\n // ─── HLS (direct to Frigate VOD) ─────────────────────────────\n\n getRecordingHlsUrl(\n camera: string,\n startTime: number,\n endTime: number,\n ): string {\n const start = Math.floor(startTime);\n const end = Math.ceil(endTime);\n return `${this.baseUrl}/vod/${encodeURIComponent(camera)}/start/${start}/end/${end}/master.m3u8`;\n }\n\n getEventHlsUrl(eventId: string): string {\n return `${this.baseUrl}/vod/event/${encodeURIComponent(eventId)}/master.m3u8`;\n }\n}\n","/**\n * SDK client configuration types.\n *\n * NVR types are now in nvr.ts (shared across app and proxy).\n */\n\nexport interface HlsInfo {\n hlsUrl: string;\n}\n\n/** Public provider info (returned by providers.list). */\nexport interface ProviderInfo {\n id: string;\n type: string;\n name: string;\n}\n\n// ─── Client config ──────────────────────────────────────────────────\n\nexport interface ProxyClientConfig {\n proxyUrl: string;\n /** Optional auth token for admin endpoints. */\n token?: string;\n}\n\nexport interface DirectClientConfig {\n directUrl: string;\n type: \"frigate\";\n username?: string;\n password?: string;\n}\n\nexport type CamStackClientConfig = ProxyClientConfig | DirectClientConfig;\n\nexport function isProxyConfig(\n config: CamStackClientConfig,\n): config is ProxyClientConfig {\n return \"proxyUrl\" in config;\n}\n","/**\n * CamStackClient — unified API, dual mode (proxy or direct).\n *\n * Usage:\n * const client = createCamStackClient({ proxyUrl: 'http://localhost:4000' });\n * const client = createCamStackClient({ directUrl: 'http://frigate:5000', type: 'frigate' });\n */\n\nimport { ProxyClient } from \"./proxy-client.js\";\nimport { DirectClient } from \"./direct-client.js\";\nimport { isProxyConfig, type CamStackClientConfig, type ProviderInfo } from \"./types.js\";\nimport type {\n NvrCamera,\n NvrConfig,\n NvrEvent,\n RecordingSegment,\n MotionBucket,\n StreamInfo,\n LiveEvent,\n EventsQuery,\n MotionQuery,\n RecordingsQuery,\n} from \"./nvr.js\";\nimport type { AudioLevelSegment } from \"./timeline.js\";\n\nexport class CamStackClient {\n private proxy: ProxyClient | null = null;\n private direct: DirectClient | null = null;\n\n readonly isProxy: boolean;\n\n constructor(config: CamStackClientConfig) {\n if (isProxyConfig(config)) {\n this.proxy = new ProxyClient(config);\n this.isProxy = true;\n } else {\n this.direct = new DirectClient(config);\n this.isProxy = false;\n }\n }\n\n // ─── Providers ───────────────────────────────────────────────────\n\n async listProviders(): Promise<ProviderInfo[]> {\n if (!this.proxy) return [];\n return this.proxy.listProviders();\n }\n\n // ─── Config ────────────────────────────────────────────────────\n\n async getConfig(providerId?: string): Promise<NvrConfig> {\n if (this.proxy) return this.proxy.getConfig(providerId);\n return this.direct!.getConfig();\n }\n\n async getStreams(providerId?: string): Promise<StreamInfo[]> {\n if (this.proxy) return this.proxy.getStreams(providerId);\n return this.direct!.getStreams();\n }\n\n // ─── Cameras ───────────────────────────────────────────────────\n\n async listCameras(providerId?: string): Promise<NvrCamera[]> {\n if (this.proxy) return this.proxy.listCameras(providerId);\n return this.direct!.listCameras();\n }\n\n async getSnapshot(camera: string, providerId?: string): Promise<string> {\n if (this.proxy) return this.proxy.getSnapshot(camera, providerId);\n return this.direct!.getSnapshot(camera);\n }\n\n // ─── Events ────────────────────────────────────────────────────\n\n async getEvents(params: EventsQuery & { providerId?: string }): Promise<NvrEvent[]> {\n if (this.proxy) return this.proxy.getEvents(params);\n return this.direct!.getEvents(params);\n }\n\n async getRecordings(params: RecordingsQuery & { providerId?: string }): Promise<RecordingSegment[]> {\n if (this.proxy) return this.proxy.getRecordings(params);\n return this.direct!.getRecordings(params);\n }\n\n async getMotionActivity(params: MotionQuery & { providerId?: string }): Promise<MotionBucket[]> {\n if (this.proxy) return this.proxy.getMotionActivity(params);\n return this.direct!.getMotionActivity(params);\n }\n\n async getAudioSegments(\n camera: string,\n after: number,\n before: number,\n providerId?: string,\n ): Promise<AudioLevelSegment[]> {\n if (this.proxy) return this.proxy.getAudioSegments(camera, after, before, providerId);\n return this.direct!.getAudioSegments(camera, after, before);\n }\n\n async getEventThumbnail(eventId: string, providerId?: string): Promise<string> {\n if (this.proxy) return this.proxy.getEventThumbnail(eventId, providerId);\n return this.direct!.getEventThumbnail(eventId);\n }\n\n async getEventSnapshot(eventId: string, providerId?: string): Promise<string> {\n if (this.proxy) return this.proxy.getEventSnapshot(eventId, providerId);\n return this.direct!.getEventSnapshot(eventId);\n }\n\n // ─── WebRTC ────────────────────────────────────────────────────\n\n async webrtcOffer(streamName: string, sdpOffer: string, providerId?: string): Promise<string> {\n if (this.proxy) return this.proxy.webrtcOffer(streamName, sdpOffer, providerId);\n return this.direct!.webrtcOffer(streamName, sdpOffer);\n }\n\n // ─── HLS ───────────────────────────────────────────────────────\n\n async getRecordingHlsUrl(\n camera: string,\n startTime: number,\n endTime: number,\n providerId?: string,\n ): Promise<string> {\n if (this.proxy) {\n return this.proxy.getRecordingHls(camera, startTime, endTime, providerId);\n }\n return this.direct!.getRecordingHlsUrl(camera, startTime, endTime);\n }\n\n async getEventHlsUrl(eventId: string, providerId?: string): Promise<string> {\n if (this.proxy) {\n return this.proxy.getEventHls(eventId, providerId);\n }\n return this.direct!.getEventHlsUrl(eventId);\n }\n\n // ─── Live subscriptions (proxy mode only) ─────────────────────\n\n subscribeLiveEvents(\n callback: (event: LiveEvent) => void,\n options?: { cameras?: string[]; types?: string[]; providerId?: string },\n ): { unsubscribe: () => void } {\n if (!this.proxy) {\n throw new Error(\"Live subscriptions require proxy mode.\");\n }\n return this.proxy.subscribeLiveEvents(callback, options);\n }\n\n subscribeMotion(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n if (!this.proxy) throw new Error(\"Live subscriptions require proxy mode.\");\n return this.proxy.subscribeMotion(camera, callback, providerId);\n }\n\n subscribeDetection(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n if (!this.proxy) throw new Error(\"Live subscriptions require proxy mode.\");\n return this.proxy.subscribeDetection(camera, callback, providerId);\n }\n\n subscribeAudioLevel(\n camera: string,\n callback: (event: LiveEvent) => void,\n providerId?: string,\n ): { unsubscribe: () => void } {\n if (!this.proxy) throw new Error(\"Live subscriptions require proxy mode.\");\n return this.proxy.subscribeAudioLevel(camera, callback, providerId);\n }\n}\n\nexport function createCamStackClient(config: CamStackClientConfig): CamStackClient {\n return new CamStackClient(config);\n}\n","/**\n * Detection classes — shared between CamStack app and proxy.\n * Aligned with scrypted-advanced-notifier/src/detectionClasses.ts.\n */\n\n// ---------------------------------------------------------------------------\n// Enum — matches the plugin enum exactly\n// ---------------------------------------------------------------------------\n\nexport enum DetectionClass {\n Motion = \"motion\",\n Person = \"person\",\n Vehicle = \"vehicle\",\n Animal = \"animal\",\n Audio = \"audio\",\n Face = \"face\",\n Plate = \"plate\",\n Package = \"package\",\n Doorbell = \"doorbell\",\n Sensor = \"sensor\",\n}\n\n// ---------------------------------------------------------------------------\n// Sub-class arrays (for classname → parent mapping)\n// ---------------------------------------------------------------------------\n\nexport const animalClasses = [\n DetectionClass.Animal,\n \"dog_cat\", \"dog\", \"cat\", \"horse\", \"sheep\", \"cow\", \"elephant\", \"bear\",\n \"zebra\", \"giraffe\", \"mouse\", \"rabbit\", \"deer\", \"lion\", \"tiger\",\n \"bird\", \"eagle\", \"owl\", \"pigeon\",\n \"fish\", \"whale\", \"dolphin\",\n \"snake\", \"turtle\", \"lizard\",\n];\n\nexport const personClasses = [\n DetectionClass.Person,\n \"people\", \"pedestrian\", \"rider\", \"driver\", \"cyclist\", \"skier\", \"skateboarder\",\n \"face\", \"hand\", \"head\", \"body\",\n];\n\nexport const vehicleClasses = [\n DetectionClass.Vehicle,\n \"car\", \"truck\", \"bus\", \"motorcycle\", \"bicycle\", \"van\",\n \"ambulance\", \"police_car\", \"fire_truck\",\n \"train\", \"subway\", \"tram\",\n \"airplane\", \"boat\", \"ship\", \"helicopter\",\n];\n\nexport const faceClasses = [\n DetectionClass.Face,\n \"eyes\", \"nose\", \"mouth\", \"ears\", \"eyebrows\",\n \"left_eye\", \"right_eye\", \"pupil\", \"iris\", \"eyelid\", \"eye_corner\",\n \"upper_lip\", \"lower_lip\", \"teeth\",\n \"chin\", \"cheek\", \"forehead\", \"jaw\",\n \"glasses\", \"sunglasses\", \"facial_hair\", \"beard\", \"mustache\",\n \"facial_landmark\", \"facial_keypoint\",\n];\n\nexport const licensePlateClasses = [\n DetectionClass.Plate,\n \"license_plate\", \"front_plate\", \"rear_plate\", \"motorcycle_plate\",\n \"temporary_plate\", \"dealer_plate\", \"licensePlate\",\n \"plate_number\", \"plate_character\", \"plate_digit\", \"plate_letter\",\n \"plate_symbol\", \"plate_region\", \"plate_country_identifier\",\n \"plate_frame\", \"plate_bolt\", \"plate_sticker\", \"plate_validation_tag\",\n \"damaged_plate\", \"obscured_plate\", \"dirty_plate\",\n];\n\nexport const motionClasses = [DetectionClass.Motion, \"movement\", \"other\"];\n\nexport const packageClasses = [DetectionClass.Package, \"packet\"];\n\nexport const audioClasses: string[] = [DetectionClass.Audio];\n\n/** Common YAMNet audio labels (advanced-notifier). Parent: Audio. */\nexport const audioLabelClasses = [\n \"speech\", \"scream\", \"babbling\", \"yell\", \"bellow\", \"whoop\", \"whispering\", \"laughter\", \"snicker\",\n \"crying\", \"cry\", \"sigh\", \"singing\", \"choir\", \"chant\", \"mantra\", \"child_singing\", \"rapping\", \"humming\",\n \"groan\", \"grunt\", \"whistling\", \"breathing\", \"wheeze\", \"snoring\", \"gasp\", \"pant\", \"snort\", \"cough\",\n \"throat_clearing\", \"sneeze\", \"sniff\", \"cheering\", \"applause\", \"chatter\", \"crowd\", \"children_playing\",\n \"bark\", \"yip\", \"howl\", \"bow-wow\", \"growling\", \"whimper_dog\", \"purr\", \"meow\", \"hiss\", \"caterwaul\",\n \"pets\", \"livestock\", \"doorbell\", \"ding-dong\", \"door\", \"slam\", \"knock\", \"alarm\", \"telephone\",\n \"music\", \"dog\", \"dogs\",\n];\n\nexport const doorbellClasses = [DetectionClass.Doorbell, \"ring\"];\n\n/** Sensor types (advanced-notifier SupportedSensorType). Parent: Sensor. */\nexport const sensorLabelClasses = [\n \"lock\", \"binary\", \"flood\", \"entry\",\n \"door\", \"leak\", \"door_open\", \"flooded\", \"entry_open\",\n];\n\n// ---------------------------------------------------------------------------\n// Classname → parent DetectionClass map\n// ---------------------------------------------------------------------------\n\nexport const detectionClassesDefaultMap: Record<string, DetectionClass> = {\n ...animalClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Animal }), {} as Record<string, DetectionClass>),\n ...personClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Person }), {} as Record<string, DetectionClass>),\n ...vehicleClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Vehicle }), {} as Record<string, DetectionClass>),\n ...motionClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Motion }), {} as Record<string, DetectionClass>),\n ...packageClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Package }), {} as Record<string, DetectionClass>),\n ...faceClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Face }), {} as Record<string, DetectionClass>),\n ...licensePlateClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Plate }), {} as Record<string, DetectionClass>),\n ...audioClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Audio }), {} as Record<string, DetectionClass>),\n ...audioLabelClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Audio }), {} as Record<string, DetectionClass>),\n ...doorbellClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Doorbell }), {} as Record<string, DetectionClass>),\n ...sensorLabelClasses.reduce((tot, curr) => ({ ...tot, [curr]: DetectionClass.Sensor }), {} as Record<string, DetectionClass>),\n};\n\n// ---------------------------------------------------------------------------\n// Classifier helpers\n// ---------------------------------------------------------------------------\n\nexport const isFaceClassname = (c: string) => faceClasses.includes(c);\nexport const isPlateClassname = (c: string) => licensePlateClasses.includes(c);\nexport const isAnimalClassname = (c: string) => animalClasses.includes(c);\nexport const isPersonClassname = (c: string) => personClasses.includes(c);\nexport const isVehicleClassname = (c: string) => vehicleClasses.includes(c);\nexport const isMotionClassname = (c: string) => motionClasses.includes(c);\nexport const isDoorbellClassname = (c: string) => doorbellClasses.includes(c);\nexport const isPackageClassname = (c: string) => packageClasses.includes(c);\nexport const isAudioClassname = (c: string) =>\n audioClasses.includes(c) || audioLabelClasses.includes(c);\nexport const isSensorLabelClassname = (c: string) => sensorLabelClasses.includes(c);\nexport const isLabelDetection = (c: string) => isFaceClassname(c) || isPlateClassname(c);\n\nexport const getParentClass = (className: string): DetectionClass | undefined =>\n detectionClassesDefaultMap[className];\n\nexport const getParentDetectionClass = (det: { label?: string; className: string }) => {\n const { className } = det;\n const baseMap: Record<string, DetectionClass> = {\n [DetectionClass.Face]: DetectionClass.Person,\n [DetectionClass.Plate]: DetectionClass.Vehicle,\n };\n const parentGroup = detectionClassesDefaultMap[className];\n if (parentGroup && parentGroup !== className) return parentGroup;\n return baseMap[className];\n};\n\n// ---------------------------------------------------------------------------\n// All detection classes (enum values) used in filter UI\n// ---------------------------------------------------------------------------\n\nexport const defaultDetectionClasses = Object.values(DetectionClass);\n\n/** Default enabled classes: all except Motion. */\nexport const DEFAULT_ENABLED_CLASSES = defaultDetectionClasses.filter(\n (c) => c !== DetectionClass.Motion,\n);\n\n// ---------------------------------------------------------------------------\n// Timeline event presets (by severity)\n// ---------------------------------------------------------------------------\n\nexport type TimelineEventPreset = \"all\" | \"important\" | \"critical\" | \"custom\";\n\n/** Classes for \"critical\" preset: security-relevant only (person, doorbell, package). */\nexport const TIMELINE_PRESET_CRITICAL: string[] = [\n DetectionClass.Person,\n DetectionClass.Doorbell,\n DetectionClass.Package,\n];\n\n/** Classes for \"important\" preset: critical + vehicle, animal, audio, face, plate. */\nexport const TIMELINE_PRESET_IMPORTANT: string[] = [\n ...TIMELINE_PRESET_CRITICAL,\n DetectionClass.Vehicle,\n DetectionClass.Animal,\n DetectionClass.Audio,\n DetectionClass.Face,\n DetectionClass.Plate,\n];\n\n/** Classes for \"all\" preset: same as DEFAULT_ENABLED_CLASSES. */\nexport const TIMELINE_PRESET_ALL: string[] = [...DEFAULT_ENABLED_CLASSES];\n\n/** Get enabled classes for a preset. For \"custom\", pass customClasses. */\nexport function getClassesForTimelinePreset(\n preset: TimelineEventPreset,\n customClasses?: string[]\n): string[] {\n switch (preset) {\n case \"critical\":\n return TIMELINE_PRESET_CRITICAL;\n case \"important\":\n return TIMELINE_PRESET_IMPORTANT;\n case \"all\":\n return TIMELINE_PRESET_ALL;\n case \"custom\":\n return customClasses?.length ? customClasses : DEFAULT_ENABLED_CLASSES;\n default:\n return DEFAULT_ENABLED_CLASSES;\n }\n}\n","/**\n * Device types, mappings, and filtering constants — shared between CamStack app and proxy.\n *\n * Covers:\n * - Canonical device type normalization (Scrypted PascalCase + HA domains → canonical)\n * - Eligible device type lists for Scrypted and Home Assistant\n * - Unified Device / DeviceCommand / CommandResult interfaces\n * - AssociatedDeviceState shape\n */\n\n// ---------------------------------------------------------------------------\n// Canonical device type\n// ---------------------------------------------------------------------------\n\nexport type CanonicalDeviceType =\n | \"light\"\n | \"switch\"\n | \"cover\"\n | \"lock\"\n | \"alarm\"\n | \"button\"\n | \"select\"\n | \"siren\"\n | \"sensor\"\n | \"entry\"\n | \"media_player\"\n | \"script\";\n\n// ---------------------------------------------------------------------------\n// Raw type → canonical mapping (Scrypted + Home Assistant)\n// ---------------------------------------------------------------------------\n\n/**\n * Maps raw device types from Scrypted (PascalCase) and Home Assistant (lowercase domains)\n * to canonical CamStack device types.\n */\nexport const RAW_TO_CANONICAL: Record<string, CanonicalDeviceType> = {\n // Scrypted PascalCase\n Light: \"light\",\n Switch: \"switch\",\n WindowCovering: \"cover\",\n Lock: \"lock\",\n SecuritySystem: \"alarm\",\n Buttons: \"button\",\n Select: \"select\",\n Siren: \"siren\",\n Sensor: \"sensor\",\n Entry: \"entry\",\n Program: \"script\",\n MediaPlayer: \"media_player\",\n Outlet: \"switch\",\n // Home Assistant lowercase domains\n light: \"light\",\n switch: \"switch\",\n input_boolean: \"switch\",\n cover: \"cover\",\n lock: \"lock\",\n alarm_control_panel: \"alarm\",\n input_button: \"button\",\n button: \"button\",\n input_select: \"select\",\n select: \"select\",\n siren: \"siren\",\n sensor: \"sensor\",\n media_player: \"media_player\",\n script: \"script\",\n};\n\n/**\n * Extended HA domain → type map (includes non-eligible domains for display/categorization).\n * Superset of RAW_TO_CANONICAL for HA-specific domains.\n */\nexport const HA_DOMAIN_TYPE_MAP: Record<string, string> = {\n light: \"light\",\n switch: \"switch\",\n input_boolean: \"switch\",\n cover: \"cover\",\n lock: \"lock\",\n alarm_control_panel: \"alarm\",\n input_button: \"button\",\n button: \"button\",\n input_select: \"select\",\n select: \"select\",\n siren: \"siren\",\n sensor: \"sensor\",\n binary_sensor: \"sensor\",\n media_player: \"media_player\",\n script: \"script\",\n climate: \"climate\",\n camera: \"camera\",\n fan: \"fan\",\n vacuum: \"vacuum\",\n automation: \"automation\",\n scene: \"scene\",\n input_number: \"sensor\",\n person: \"person\",\n device_tracker: \"tracker\",\n weather: \"weather\",\n water_heater: \"climate\",\n};\n\n/**\n * Extended Scrypted type → canonical map (includes non-eligible types for display).\n * Superset of RAW_TO_CANONICAL for Scrypted-specific types.\n */\nexport const SCRYPTED_TYPE_TO_CANONICAL: Record<string, string> = {\n Light: \"light\",\n Switch: \"switch\",\n WindowCovering: \"cover\",\n Lock: \"lock\",\n SecuritySystem: \"alarm\",\n Buttons: \"button\",\n Select: \"select\",\n Siren: \"siren\",\n Sensor: \"sensor\",\n Entry: \"entry\",\n Program: \"script\",\n MediaPlayer: \"media_player\",\n Camera: \"camera\",\n Doorbell: \"doorbell\",\n Fan: \"fan\",\n Outlet: \"switch\",\n};\n\n/** Normalize raw type (Scrypted or HA) to canonical key for filtering and display. */\nexport function getCanonicalDeviceType(rawType: string): CanonicalDeviceType | null {\n const canonical = RAW_TO_CANONICAL[rawType];\n if (canonical) return canonical;\n const lower = rawType.toLowerCase();\n return RAW_TO_CANONICAL[lower] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Eligible device types (filter lists)\n// ---------------------------------------------------------------------------\n\n/** Scrypted device types eligible for device control in CamStack. PascalCase. */\nexport const ELIGIBLE_SCRYPTED_DEVICE_TYPES = [\n \"Entry\",\n \"Light\",\n \"Switch\",\n \"Lock\",\n \"SecuritySystem\",\n \"Buttons\",\n \"WindowCovering\",\n \"Siren\",\n \"Sensor\",\n \"Select\",\n \"Program\",\n] as const;\n\n/** Set version for O(1) lookup. */\nexport const ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET = new Set<string>(ELIGIBLE_SCRYPTED_DEVICE_TYPES);\n\n/** Home Assistant domains eligible for device control in CamStack. */\nexport const ELIGIBLE_HA_DOMAINS = [\n \"light\",\n \"switch\",\n \"input_boolean\",\n \"cover\",\n \"lock\",\n \"alarm_control_panel\",\n \"input_button\",\n \"button\",\n \"input_select\",\n \"select\",\n \"siren\",\n \"media_player\",\n \"script\",\n] as const;\n\n/** Set version for O(1) lookup. */\nexport const ELIGIBLE_HA_DOMAINS_SET = new Set<string>(ELIGIBLE_HA_DOMAINS);\n\n// ---------------------------------------------------------------------------\n// Device command action type\n// ---------------------------------------------------------------------------\n\n/** All device command actions supported by CamStack. */\nexport type DeviceCommandAction =\n | \"turnOn\"\n | \"turnOff\"\n | \"openEntry\"\n | \"closeEntry\"\n | \"stopEntry\"\n | \"lock\"\n | \"unlock\"\n | \"disarm\"\n | \"arm\"\n | \"pressButton\"\n | \"selectOption\"\n | \"volumeUp\"\n | \"volumeDown\"\n | \"volumeMute\";\n\n// ---------------------------------------------------------------------------\n// Associated device state (unified shape)\n// ---------------------------------------------------------------------------\n\n/**\n * State snapshot shape for an associated device.\n * Used by both app (AssociatedDeviceStateSnapshot) and proxy (Device.state).\n */\nexport interface AssociatedDeviceState {\n name?: string;\n type?: string;\n interfaces?: string[];\n on?: boolean;\n hasBrightnessControl?: boolean;\n brightness?: number;\n lockState?: string;\n securitySystemState?: { mode?: string; supportedModes?: string[] };\n buttons?: string[];\n entryOpen?: boolean | \"jammed\";\n value?: string;\n options?: string[];\n volumeLevel?: number;\n isVolumeMuted?: boolean;\n coverSupportsStop?: boolean;\n coverPosition?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Device interfaces (from proxy integrations/types.ts)\n// ---------------------------------------------------------------------------\n\n/** A device from an automation system (Scrypted, Home Assistant, etc.). */\nexport interface Device {\n id: string;\n name: string;\n /**\n * Canonical device type (lowercase): \"light\", \"switch\", \"cover\", \"lock\",\n * \"alarm\", \"button\", \"select\", \"siren\", \"sensor\", \"entry\", \"media_player\", \"script\".\n */\n type: string;\n /** Original type from the source system (e.g. Scrypted \"WindowCovering\", HA domain \"cover\"). */\n rawType?: string;\n model?: string;\n manufacturer?: string;\n online: boolean;\n interfaces: string[];\n /** Current state (key-value pairs, device-type-dependent). See AssociatedDeviceState. */\n state: Record<string, unknown>;\n}\n\n/** A command to send to a device. */\nexport interface DeviceCommand {\n deviceId: string;\n command: string;\n params?: Record<string, unknown>;\n}\n\n/** Result of a command execution. */\nexport interface CommandResult {\n success: boolean;\n error?: string;\n state?: Record<string, unknown>;\n}\n\n/** Device source type (integration that provides the device). */\nexport type AssociatedDeviceSource = \"scrypted\" | \"ha\";\n\n/** Eligible device summary (for device picker UI). */\nexport interface EligibleDevice {\n id: string;\n name: string;\n type: string;\n source: AssociatedDeviceSource;\n}\n","/**\n * Feature capability matrix — shared between CamStack app and proxy.\n *\n * Maps each app feature to:\n * - Which source types (Frigate, Scrypted, RTSP) support it\n * - Which platforms (iOS, Android, Web, Scrypted embed) it works on\n * - Whether the camstack-js-proxy backend is required\n * - The ICameraSource method that enables the feature\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type FeatureId =\n | \"liveStream\"\n | \"multiResolution\"\n | \"motion\"\n | \"objectDetection\"\n | \"audioVolume\"\n | \"audioVolumes\"\n | \"ptz\"\n | \"intercom\"\n | \"deviceStatus\"\n | \"timeline\"\n | \"clusteredTimeline\"\n | \"detectionClasses\"\n | \"videoClips\"\n | \"nvrPlayback\"\n | \"nvrScrub\"\n | \"recordingThumbnail\"\n | \"nvrSeekToLive\";\n\nexport type PlatformId = \"ios\" | \"android\" | \"web\" | \"scryptedEmbed\";\n\nexport type SourceType = \"frigate\" | \"scrypted\" | \"rtsp\";\n\nexport interface FeatureEntry {\n id: FeatureId;\n label: string;\n /** Which source types support this feature. */\n sources: Partial<Record<SourceType, boolean>>;\n /** Platform availability overrides. When omitted, all platforms are supported. */\n platforms?: Partial<Record<PlatformId, boolean>>;\n /** True when this feature needs the camstack-js-proxy backend. */\n requiresBackend?: boolean;\n /** The ICameraSource method name that enables this feature at runtime. */\n adapterMethod?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Matrix\n// ---------------------------------------------------------------------------\n\nexport const FEATURE_MATRIX: FeatureEntry[] = [\n {\n id: \"liveStream\",\n label: \"Live Stream\",\n sources: { frigate: true, scrypted: true, rtsp: true },\n adapterMethod: \"getLiveStream\",\n },\n {\n id: \"multiResolution\",\n label: \"Multi-Resolution\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getResolutions\",\n },\n {\n id: \"motion\",\n label: \"Motion Detection\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getMotion\",\n },\n {\n id: \"objectDetection\",\n label: \"Object Detection\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getObjectDetections\",\n },\n {\n id: \"audioVolume\",\n label: \"Audio Level\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getAudioVolume\",\n },\n {\n id: \"audioVolumes\",\n label: \"Audio Volumes (dBFS)\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getAudioVolumes\",\n },\n {\n id: \"ptz\",\n label: \"PTZ Control\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getPTZ\",\n },\n {\n id: \"intercom\",\n label: \"Intercom (Mic)\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getIntercomSupport\",\n },\n {\n id: \"deviceStatus\",\n label: \"Device Status\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"getStatus\",\n },\n {\n id: \"timeline\",\n label: \"Detection Timeline\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getCameraDayData\",\n },\n {\n id: \"clusteredTimeline\",\n label: \"Clustered Timeline\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n requiresBackend: true,\n adapterMethod: \"getClusteredDayData\",\n },\n {\n id: \"detectionClasses\",\n label: \"Detection Classes\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getDetectionClasses\",\n },\n {\n id: \"videoClips\",\n label: \"Video Clips\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getVideoClips\",\n },\n {\n id: \"nvrPlayback\",\n label: \"NVR Playback\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getNvrPlaybackSupported\",\n },\n {\n id: \"nvrScrub\",\n label: \"NVR Scrub/Seek\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"seekRecordingStream\",\n },\n {\n id: \"recordingThumbnail\",\n label: \"Recording Thumbnails\",\n sources: { frigate: true, scrypted: true, rtsp: false },\n adapterMethod: \"getRecordingStreamThumbnail\",\n },\n {\n id: \"nvrSeekToLive\",\n label: \"Seek to Live\",\n sources: { frigate: false, scrypted: true, rtsp: false },\n adapterMethod: \"seekNvrToLive\",\n },\n];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Check if a feature is available for a given source type and platform. */\nexport function isFeatureAvailable(\n featureId: FeatureId,\n source: SourceType,\n platform: PlatformId,\n): boolean {\n const entry = FEATURE_MATRIX.find((f) => f.id === featureId);\n if (!entry) return false;\n if (!entry.sources[source]) return false;\n if (entry.platforms && entry.platforms[platform] === false) return false;\n return true;\n}\n\n/** Get all features supported by a source type. */\nexport function getSourceFeatures(source: SourceType): FeatureEntry[] {\n return FEATURE_MATRIX.filter((f) => f.sources[source]);\n}\n\n/** Get all features that require the backend proxy. */\nexport function getBackendRequiredFeatures(): FeatureEntry[] {\n return FEATURE_MATRIX.filter((f) => f.requiresBackend);\n}\n"],"mappings":"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","names":["superjson","DetectionClass"]}