@camstack/addon-provider-onvif 0.1.0 → 0.1.2

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,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <circle cx="12" cy="12" r="10"/>
3
+ <circle cx="12" cy="12" r="6"/>
4
+ <circle cx="12" cy="12" r="2"/>
5
+ </svg>
package/dist/index.d.mts CHANGED
@@ -147,12 +147,16 @@ declare class OnvifProvider implements IDeviceProvider {
147
147
  readonly ctx: CamstackContext;
148
148
  private devices;
149
149
  private readonly clients;
150
+ private lastDiscoveredCameras;
150
151
  constructor(config: OnvifProviderConfig, ctx: CamstackContext);
151
152
  start(): Promise<void>;
152
153
  private addCamera;
154
+ private loadAdoptedCameras;
153
155
  stop(): Promise<void>;
154
156
  getStatus(): ProviderStatus;
155
157
  discoverDevices(): Promise<DiscoveredDevice[]>;
158
+ adoptDevice(externalId: string, config?: Record<string, unknown>): Promise<IDevice>;
159
+ private persistAdoptedCamera;
156
160
  getDevices(): IDevice[];
157
161
  getDeviceConfigSchema(): {
158
162
  id: string;
package/dist/index.d.ts CHANGED
@@ -147,12 +147,16 @@ declare class OnvifProvider implements IDeviceProvider {
147
147
  readonly ctx: CamstackContext;
148
148
  private devices;
149
149
  private readonly clients;
150
+ private lastDiscoveredCameras;
150
151
  constructor(config: OnvifProviderConfig, ctx: CamstackContext);
151
152
  start(): Promise<void>;
152
153
  private addCamera;
154
+ private loadAdoptedCameras;
153
155
  stop(): Promise<void>;
154
156
  getStatus(): ProviderStatus;
155
157
  discoverDevices(): Promise<DiscoveredDevice[]>;
158
+ adoptDevice(externalId: string, config?: Record<string, unknown>): Promise<IDevice>;
159
+ private persistAdoptedCamera;
156
160
  getDevices(): IDevice[];
157
161
  getDeviceConfigSchema(): {
158
162
  id: string;
package/dist/index.js CHANGED
@@ -195,6 +195,7 @@ var OnvifClient = class {
195
195
  };
196
196
 
197
197
  // src/onvif-device.ts
198
+ var import_types = require("@camstack/types");
198
199
  var OnvifDevice = class {
199
200
  constructor(client, config, ctx) {
200
201
  this.client = client;
@@ -212,7 +213,7 @@ var OnvifDevice = class {
212
213
  id;
213
214
  name;
214
215
  providerId;
215
- type = "camera";
216
+ type = import_types.DeviceType.Camera;
216
217
  capabilities;
217
218
  ctx;
218
219
  capabilityMap = /* @__PURE__ */ new Map();
@@ -454,6 +455,7 @@ var OnvifProvider = class {
454
455
  ctx;
455
456
  devices = [];
456
457
  clients = /* @__PURE__ */ new Map();
458
+ lastDiscoveredCameras = [];
457
459
  async start() {
458
460
  if (this.config.discovery?.enabled !== false) {
459
461
  const timeout = this.config.discovery?.timeout ?? 5e3;
@@ -474,6 +476,7 @@ var OnvifProvider = class {
474
476
  for (const cam of this.config.cameras ?? []) {
475
477
  await this.addCamera(cam);
476
478
  }
479
+ await this.loadAdoptedCameras();
477
480
  this.ctx.logger.info(
478
481
  `ONVIF provider started with ${this.devices.length} camera(s)`
479
482
  );
@@ -533,6 +536,26 @@ var OnvifProvider = class {
533
536
  this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`);
534
537
  }
535
538
  }
539
+ async loadAdoptedCameras() {
540
+ const storage = this.ctx.storage;
541
+ if (!storage.structured) return;
542
+ try {
543
+ const records = await storage.structured.query("adopted-cameras", {});
544
+ for (const record of records) {
545
+ const cam = record.data;
546
+ const alreadyLoaded = this.devices.some(
547
+ (d) => d.id === `${this.id}/${cam.id}`
548
+ );
549
+ if (!alreadyLoaded) {
550
+ await this.addCamera(cam);
551
+ }
552
+ }
553
+ if (records.length > 0) {
554
+ this.ctx.logger.info(`Restored ${records.length} adopted camera(s) from storage`);
555
+ }
556
+ } catch {
557
+ }
558
+ }
536
559
  async stop() {
537
560
  for (const client of this.clients.values()) {
538
561
  client.disconnect();
@@ -546,6 +569,7 @@ var OnvifProvider = class {
546
569
  }
547
570
  async discoverDevices() {
548
571
  const cameras = await discoverOnvifCameras();
572
+ this.lastDiscoveredCameras = cameras;
549
573
  return cameras.map((c) => ({
550
574
  externalId: c.host,
551
575
  name: c.name ?? c.host,
@@ -554,6 +578,61 @@ var OnvifProvider = class {
554
578
  metadata: { manufacturer: c.manufacturer, model: c.model }
555
579
  }));
556
580
  }
581
+ async adoptDevice(externalId, config) {
582
+ const existingDevice = this.devices.find(
583
+ (d) => d.id === `${this.id}/${externalId.replace(/\./g, "-")}`
584
+ );
585
+ if (existingDevice) {
586
+ return existingDevice;
587
+ }
588
+ let discovered = this.lastDiscoveredCameras.find((c) => c.host === externalId);
589
+ if (!discovered) {
590
+ this.ctx.logger.info(`Camera ${externalId} not in cache, re-discovering...`);
591
+ const cameras = await discoverOnvifCameras();
592
+ this.lastDiscoveredCameras = cameras;
593
+ discovered = cameras.find((c) => c.host === externalId);
594
+ }
595
+ if (!discovered) {
596
+ throw new Error(`Camera with externalId "${externalId}" not found on network`);
597
+ }
598
+ const cameraId = externalId.replace(/\./g, "-");
599
+ const cameraConfig = {
600
+ id: cameraId,
601
+ name: config?.["name"] ?? discovered.name ?? externalId,
602
+ host: discovered.host,
603
+ port: discovered.port,
604
+ username: config?.["username"] ?? this.config.defaultUsername,
605
+ password: config?.["password"] ?? this.config.defaultPassword
606
+ };
607
+ await this.addCamera(cameraConfig);
608
+ const device = this.devices.find((d) => d.id === `${this.id}/${cameraId}`);
609
+ if (!device) {
610
+ throw new Error(`Failed to create device for camera ${externalId}`);
611
+ }
612
+ await this.persistAdoptedCamera(cameraConfig);
613
+ this.ctx.logger.info(`Adopted camera ${cameraConfig.name} (${externalId})`);
614
+ return device;
615
+ }
616
+ async persistAdoptedCamera(cam) {
617
+ const storage = this.ctx.storage;
618
+ if (!storage.structured) return;
619
+ try {
620
+ const existing = await storage.structured.query("adopted-cameras", {
621
+ where: { id: cam.id },
622
+ limit: 1
623
+ });
624
+ if (existing.length === 0) {
625
+ await storage.structured.insert({
626
+ collection: "adopted-cameras",
627
+ id: cam.id,
628
+ data: { ...cam }
629
+ });
630
+ }
631
+ } catch (err) {
632
+ const message = err instanceof Error ? err.message : String(err);
633
+ this.ctx.logger.warn(`Failed to persist adopted camera ${cam.id}: ${message}`);
634
+ }
635
+ }
557
636
  getDevices() {
558
637
  return [...this.devices];
559
638
  }
@@ -580,6 +659,7 @@ var OnvifProviderAddon = class {
580
659
  id: "provider-onvif",
581
660
  name: "ONVIF Camera Provider",
582
661
  version: "0.1.0",
662
+ description: "Discovery automatica di camere ONVIF sulla rete locale",
583
663
  capabilities: ["device-provider"]
584
664
  };
585
665
  provider = null;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/onvif-client.ts","../src/onvif-device.ts","../src/onvif-discovery.ts","../src/element-config-store.ts","../src/onvif-provider.ts","../src/addon.ts"],"sourcesContent":["export { OnvifProviderAddon } from './addon'\nexport { OnvifClient } from './onvif-client'\nexport { OnvifDevice } from './onvif-device'\nexport { OnvifProvider } from './onvif-provider'\nexport { discoverOnvifCameras } from './onvif-discovery'\nexport type {\n OnvifProviderConfig,\n OnvifCameraConfig,\n DiscoveredOnvifCamera,\n} from './onvif-types'\n","import { Cam } from 'onvif'\nimport type { IScopedLogger } from '@camstack/types'\n\nexport class OnvifClient {\n private cam: InstanceType<typeof Cam> | null = null\n\n constructor(\n private readonly host: string,\n private readonly port: number,\n private readonly username: string,\n private readonly password: string,\n private readonly logger: IScopedLogger,\n ) {}\n\n /** Connect to the camera */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.cam = new Cam(\n {\n hostname: this.host,\n port: this.port,\n username: this.username,\n password: this.password,\n },\n (err: Error | null) => {\n if (err) {\n reject(err)\n } else {\n this.logger.info(`Connected to ONVIF camera at ${this.host}:${this.port}`)\n resolve()\n }\n },\n )\n })\n }\n\n /** Get device info */\n async getDeviceInfo(): Promise<{\n manufacturer: string\n model: string\n firmwareVersion: string\n serialNumber: string\n }> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getDeviceInformation((err: Error | null, info: any) => {\n if (err) reject(err)\n else resolve(info)\n })\n })\n }\n\n /** Get RTSP stream URI */\n async getStreamUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = { protocol: 'RTSP' }\n if (profileToken) options.profileToken = profileToken\n this.cam.getStreamUri(options, (err: Error | null, stream: any) => {\n if (err) reject(err)\n else resolve(stream.uri)\n })\n })\n }\n\n /** Get available media profiles */\n async getProfiles(): Promise<\n Array<{ token: string; name: string; videoWidth?: number; videoHeight?: number }>\n > {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getProfiles((err: Error | null, profiles: any[]) => {\n if (err) reject(err)\n else\n resolve(\n profiles.map((p: any) => ({\n token: p.$.token ?? p.token,\n name: p.name,\n videoWidth: p.videoEncoderConfiguration?.resolution?.width,\n videoHeight: p.videoEncoderConfiguration?.resolution?.height,\n })),\n )\n })\n })\n }\n\n /** Get snapshot URI */\n async getSnapshotUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = {}\n if (profileToken) options.profileToken = profileToken\n this.cam.getSnapshotUri(options, (err: Error | null, res: any) => {\n if (err) reject(err)\n else resolve(res.uri)\n })\n })\n }\n\n /** Check if PTZ is supported */\n hasPtz(): boolean {\n return (this.cam as any)?.ptzUri != null\n }\n\n /** PTZ move (continuous) */\n async ptzMove(options: {\n x?: number\n y?: number\n zoom?: number\n speed?: number\n }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.continuousMove(\n {\n x: options.x ?? 0,\n y: options.y ?? 0,\n zoom: options.zoom ?? 0,\n timeout: 1000,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** PTZ stop */\n async ptzStop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.stop({}, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n /** PTZ absolute move */\n async ptzAbsoluteMove(options: { x: number; y: number; zoom: number }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.absoluteMove(\n {\n x: options.x,\n y: options.y,\n zoom: options.zoom,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** Get PTZ presets */\n async getPtzPresets(): Promise<Array<{ token: string; name: string }>> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getPresets({}, (err: Error | null, presets: any) => {\n if (err) reject(err)\n else {\n const list = Array.isArray(presets) ? presets : [presets]\n resolve(\n list.filter(Boolean).map((p: any) => ({\n token: p.$.token ?? String(p.token),\n name: p.name ?? p.$.token ?? 'Preset',\n })),\n )\n }\n })\n })\n }\n\n /** Go to PTZ preset */\n async gotoPreset(presetToken: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.gotoPreset({ preset: presetToken }, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n disconnect(): void {\n this.cam = null\n }\n}\n","import type {\n IDevice,\n DeviceType,\n DeviceState,\n DeviceMetadata,\n DeviceCapabilityName,\n IDeviceCapability,\n CamstackContext,\n ICamera,\n StreamOption,\n ConnectionMode,\n IPanTiltZoom,\n} from '@camstack/types'\nimport type { OnvifClient } from './onvif-client'\n\nexport interface OnvifDeviceConfig {\n readonly cameraId: string\n readonly cameraName: string\n readonly providerId: string\n readonly rtspUrl: string\n readonly subStreamUrl?: string\n readonly snapshotUrl?: string\n readonly hasPtz: boolean\n readonly profiles: ReadonlyArray<{\n token: string\n name: string\n videoWidth?: number\n videoHeight?: number\n }>\n readonly manufacturer?: string\n readonly model?: string\n readonly firmware?: string\n}\n\nexport class OnvifDevice implements IDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly type: DeviceType = 'camera'\n readonly capabilities: DeviceCapabilityName[]\n readonly ctx: CamstackContext\n\n private readonly capabilityMap = new Map<DeviceCapabilityName, IDeviceCapability>()\n\n constructor(\n private readonly client: OnvifClient,\n private readonly config: OnvifDeviceConfig,\n ctx: CamstackContext,\n ) {\n this.id = `${config.providerId}/${config.cameraId}`\n this.name = config.cameraName\n this.providerId = config.providerId\n this.ctx = ctx\n\n const caps: DeviceCapabilityName[] = ['camera']\n if (config.hasPtz) caps.push('panTiltZoom')\n this.capabilities = caps\n\n this.capabilityMap.set('camera', this.createCamera())\n if (config.hasPtz) this.capabilityMap.set('panTiltZoom', this.createPtz())\n }\n\n getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null {\n return (this.capabilityMap.get(cap) as T) ?? null\n }\n\n hasCapability(cap: DeviceCapabilityName): boolean {\n return this.capabilityMap.has(cap)\n }\n\n getState(): DeviceState {\n return { online: true }\n }\n\n getMetadata(): DeviceMetadata {\n return {\n manufacturer: this.config.manufacturer ?? 'ONVIF',\n model: this.config.model,\n firmware: this.config.firmware,\n }\n }\n\n private createCamera(): ICamera {\n const { config } = this\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (config.snapshotUrl) {\n const res = await fetch(config.snapshotUrl)\n return Buffer.from(await res.arrayBuffer())\n }\n return Buffer.alloc(0)\n },\n\n async getStreamOptions(): Promise<StreamOption[]> {\n const options: StreamOption[] = [\n {\n id: `${config.cameraId}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: config.rtspUrl,\n },\n ]\n if (config.subStreamUrl) {\n options.push({\n id: `${config.cameraId}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: config.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? config.rtspUrl\n },\n\n getConnectionMode(): ConnectionMode {\n return 'on-demand'\n },\n\n async setConnectionMode() {},\n }\n }\n\n private createPtz(): IPanTiltZoom {\n const { client } = this\n return {\n kind: 'panTiltZoom',\n\n async move(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom, speed: cmd.speed })\n },\n\n async continuousMove(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom })\n },\n\n async stop() {\n await client.ptzStop()\n },\n\n async getPresets() {\n const presets = await client.getPtzPresets()\n return presets.map((p) => ({ id: p.token, name: p.name }))\n },\n\n async goToPreset(presetId: string) {\n await client.gotoPreset(presetId)\n },\n\n async goHome() {\n await client.ptzAbsoluteMove({ x: 0, y: 0, zoom: 0 })\n },\n\n async getPosition() {\n return { pan: 0, tilt: 0, zoom: 0 }\n },\n }\n }\n}\n","import { Discovery } from 'onvif'\nimport type { DiscoveredOnvifCamera } from './onvif-types'\n\n/**\n * Discovers ONVIF cameras on the local network via WS-Discovery.\n * Returns a list of found cameras after the given timeout.\n */\nexport async function discoverOnvifCameras(\n timeout: number = 5000,\n): Promise<readonly DiscoveredOnvifCamera[]> {\n return new Promise((resolve) => {\n const cameras: DiscoveredOnvifCamera[] = []\n\n Discovery.on('device', (cam: any) => {\n cameras.push({\n host: cam.hostname,\n port: cam.port ?? 80,\n name: cam.name,\n manufacturer: cam.manufacturer,\n model: cam.model,\n scopes: cam.scopes,\n })\n })\n\n Discovery.probe({ timeout })\n\n setTimeout(() => {\n Discovery.removeAllListeners('device')\n resolve(cameras)\n }, timeout + 500)\n })\n}\n","import type { IElementConfig } from '@camstack/types'\nimport type { IStorageLocation } from '@camstack/types'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n constructor(\n private readonly elementId: string,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n if (!this.storage.structured) {\n this.loaded = true\n return\n }\n\n try {\n const records = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n if (records.length > 0) {\n this.cache = (records[0] as any).data ?? {}\n }\n } catch {\n // Storage might not be ready yet\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n if (!this.storage.structured) return\n\n try {\n const existing = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n\n if (existing.length > 0) {\n await this.storage.structured.update('config', this.elementId, this.cache)\n } else {\n await this.storage.structured.insert({\n collection: 'config',\n id: this.elementId,\n data: this.cache,\n })\n }\n } catch {\n // Storage might not be ready\n }\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[parts[parts.length - 1]!] = value\n}\n","import { OnvifClient } from './onvif-client'\nimport { OnvifDevice } from './onvif-device'\nimport { discoverOnvifCameras } from './onvif-discovery'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n DeviceType,\n DeviceCapabilityName,\n CamstackContext,\n} from '@camstack/types'\nimport type { OnvifProviderConfig, OnvifCameraConfig } from './onvif-types'\n\nexport class OnvifProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'onvif'\n readonly name: string\n readonly discoveryMode: 'both' = 'both'\n readonly ctx: CamstackContext\n\n private devices: OnvifDevice[] = []\n private readonly clients: Map<string, OnvifClient> = new Map()\n\n constructor(\n private readonly config: OnvifProviderConfig,\n ctx: CamstackContext,\n ) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n }\n\n async start(): Promise<void> {\n // 1. Auto-discover cameras on LAN (if enabled)\n if (this.config.discovery?.enabled !== false) {\n const timeout = this.config.discovery?.timeout ?? 5000\n this.ctx.logger.info(`Starting ONVIF discovery (timeout: ${timeout}ms)`)\n\n const discovered = await discoverOnvifCameras(timeout)\n this.ctx.logger.info(`Discovered ${discovered.length} ONVIF camera(s)`)\n\n for (const cam of discovered) {\n await this.addCamera({\n id: cam.host.replace(/\\./g, '-'),\n name: cam.name ?? cam.host,\n host: cam.host,\n port: cam.port,\n username: this.config.defaultUsername,\n password: this.config.defaultPassword,\n })\n }\n }\n\n // 2. Add manually configured cameras\n for (const cam of this.config.cameras ?? []) {\n await this.addCamera(cam)\n }\n\n this.ctx.logger.info(\n `ONVIF provider started with ${this.devices.length} camera(s)`,\n )\n }\n\n private async addCamera(cam: OnvifCameraConfig): Promise<void> {\n try {\n const client = new OnvifClient(\n cam.host,\n cam.port ?? 80,\n cam.username ?? '',\n cam.password ?? '',\n this.ctx.logger.child(cam.name),\n )\n await client.connect()\n\n const info = await client.getDeviceInfo()\n const profiles = await client.getProfiles()\n const mainProfile = profiles[0]\n const subProfile = profiles.length > 1 ? profiles[1] : undefined\n\n const rtspUrl = cam.rtspUrl ?? (await client.getStreamUri(mainProfile?.token))\n const subStreamUrl = subProfile\n ? await client.getStreamUri(subProfile.token)\n : undefined\n\n let snapshotUrl: string | undefined\n try {\n snapshotUrl = await client.getSnapshotUri(mainProfile?.token)\n } catch {\n // Snapshot not supported by this camera\n }\n\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: this.ctx.logger.child(cam.name),\n eventBus: this.ctx.eventBus,\n storage: this.ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage),\n }\n\n const device = new OnvifDevice(\n client,\n {\n cameraId: cam.id,\n cameraName: cam.name,\n providerId: this.id,\n rtspUrl,\n subStreamUrl,\n snapshotUrl,\n hasPtz: client.hasPtz(),\n profiles,\n manufacturer: info.manufacturer,\n model: info.model,\n firmware: info.firmwareVersion,\n },\n deviceCtx,\n )\n\n this.devices = [...this.devices, device]\n this.clients.set(cam.id, client)\n this.ctx.logger.info(\n `Camera ${cam.name} (${cam.host}) connected — PTZ: ${client.hasPtz()}, profiles: ${profiles.length}`,\n )\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`)\n }\n }\n\n async stop(): Promise<void> {\n for (const client of this.clients.values()) {\n client.disconnect()\n }\n this.devices = []\n this.clients.clear()\n this.ctx.logger.info('ONVIF provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n const cameras = await discoverOnvifCameras()\n return cameras.map((c) => ({\n externalId: c.host,\n name: c.name ?? c.host,\n type: 'camera' as DeviceType,\n capabilities: ['camera'] as DeviceCapabilityName[],\n metadata: { manufacturer: c.manufacturer, model: c.model },\n }))\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema() {\n return {\n id: 'string',\n name: 'string',\n host: 'string (IP or hostname)',\n port: 'number (default: 80)',\n username: 'string (optional)',\n password: 'string (optional)',\n rtspUrl: 'string (optional, override ONVIF stream URI)',\n }\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // ONVIF events via pipeline — no native event subscription here\n return () => {}\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { OnvifProvider } from './onvif-provider'\nimport type { OnvifProviderConfig } from './onvif-types'\n\nexport class OnvifProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-onvif',\n name: 'ONVIF Camera Provider',\n version: '0.1.0',\n capabilities: ['device-provider'],\n }\n\n private provider: OnvifProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as OnvifProviderConfig\n\n const providerConfig: OnvifProviderConfig = {\n id: config.id ?? 'onvif-default',\n name: config.name ?? 'ONVIF Cameras',\n discovery: config.discovery,\n cameras: config.cameras,\n defaultUsername: config.defaultUsername,\n defaultPassword: config.defaultPassword,\n }\n\n this.provider = new OnvifProvider(providerConfig, {\n id: context.id,\n logger: context.logger,\n eventBus: context.eventBus,\n storage: context.storage,\n config: context.config,\n })\n\n context.logger.info('ONVIF provider addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.stop()\n this.provider = null\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'device-provider' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'discovery',\n title: 'ONVIF Discovery',\n description: 'Auto-discover ONVIF cameras on the local network',\n columns: 2,\n fields: [\n { type: 'boolean', key: 'discovery.enabled', label: 'Enable Auto-Discovery' },\n { type: 'number', key: 'discovery.timeout', label: 'Discovery Timeout', unit: 'ms', min: 1000, max: 30000, step: 1000 },\n ],\n },\n {\n id: 'credentials',\n title: 'Default Credentials',\n description: 'Default credentials for discovered cameras',\n columns: 2,\n fields: [\n { type: 'text', key: 'defaultUsername', label: 'Default Username' },\n { type: 'password', key: 'defaultPassword', label: 'Default Password', showToggle: true },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // Restart provider with new config\n }\n}\n"],"mappings":"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","names":["import_onvif"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/onvif-client.ts","../src/onvif-device.ts","../src/onvif-discovery.ts","../src/element-config-store.ts","../src/onvif-provider.ts","../src/addon.ts"],"sourcesContent":["export { OnvifProviderAddon } from './addon'\nexport { OnvifClient } from './onvif-client'\nexport { OnvifDevice } from './onvif-device'\nexport { OnvifProvider } from './onvif-provider'\nexport { discoverOnvifCameras } from './onvif-discovery'\nexport type {\n OnvifProviderConfig,\n OnvifCameraConfig,\n DiscoveredOnvifCamera,\n} from './onvif-types'\n","import { Cam } from 'onvif'\nimport type { IScopedLogger } from '@camstack/types'\n\nexport class OnvifClient {\n private cam: InstanceType<typeof Cam> | null = null\n\n constructor(\n private readonly host: string,\n private readonly port: number,\n private readonly username: string,\n private readonly password: string,\n private readonly logger: IScopedLogger,\n ) {}\n\n /** Connect to the camera */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.cam = new Cam(\n {\n hostname: this.host,\n port: this.port,\n username: this.username,\n password: this.password,\n },\n (err: Error | null) => {\n if (err) {\n reject(err)\n } else {\n this.logger.info(`Connected to ONVIF camera at ${this.host}:${this.port}`)\n resolve()\n }\n },\n )\n })\n }\n\n /** Get device info */\n async getDeviceInfo(): Promise<{\n manufacturer: string\n model: string\n firmwareVersion: string\n serialNumber: string\n }> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getDeviceInformation((err: Error | null, info: any) => {\n if (err) reject(err)\n else resolve(info)\n })\n })\n }\n\n /** Get RTSP stream URI */\n async getStreamUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = { protocol: 'RTSP' }\n if (profileToken) options.profileToken = profileToken\n this.cam.getStreamUri(options, (err: Error | null, stream: any) => {\n if (err) reject(err)\n else resolve(stream.uri)\n })\n })\n }\n\n /** Get available media profiles */\n async getProfiles(): Promise<\n Array<{ token: string; name: string; videoWidth?: number; videoHeight?: number }>\n > {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getProfiles((err: Error | null, profiles: any[]) => {\n if (err) reject(err)\n else\n resolve(\n profiles.map((p: any) => ({\n token: p.$.token ?? p.token,\n name: p.name,\n videoWidth: p.videoEncoderConfiguration?.resolution?.width,\n videoHeight: p.videoEncoderConfiguration?.resolution?.height,\n })),\n )\n })\n })\n }\n\n /** Get snapshot URI */\n async getSnapshotUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = {}\n if (profileToken) options.profileToken = profileToken\n this.cam.getSnapshotUri(options, (err: Error | null, res: any) => {\n if (err) reject(err)\n else resolve(res.uri)\n })\n })\n }\n\n /** Check if PTZ is supported */\n hasPtz(): boolean {\n return (this.cam as any)?.ptzUri != null\n }\n\n /** PTZ move (continuous) */\n async ptzMove(options: {\n x?: number\n y?: number\n zoom?: number\n speed?: number\n }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.continuousMove(\n {\n x: options.x ?? 0,\n y: options.y ?? 0,\n zoom: options.zoom ?? 0,\n timeout: 1000,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** PTZ stop */\n async ptzStop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.stop({}, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n /** PTZ absolute move */\n async ptzAbsoluteMove(options: { x: number; y: number; zoom: number }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.absoluteMove(\n {\n x: options.x,\n y: options.y,\n zoom: options.zoom,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** Get PTZ presets */\n async getPtzPresets(): Promise<Array<{ token: string; name: string }>> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getPresets({}, (err: Error | null, presets: any) => {\n if (err) reject(err)\n else {\n const list = Array.isArray(presets) ? presets : [presets]\n resolve(\n list.filter(Boolean).map((p: any) => ({\n token: p.$.token ?? String(p.token),\n name: p.name ?? p.$.token ?? 'Preset',\n })),\n )\n }\n })\n })\n }\n\n /** Go to PTZ preset */\n async gotoPreset(presetToken: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.gotoPreset({ preset: presetToken }, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n disconnect(): void {\n this.cam = null\n }\n}\n","import { DeviceType } from '@camstack/types'\nimport type {\n IDevice,\n DeviceState,\n DeviceMetadata,\n DeviceCapabilityName,\n IDeviceCapability,\n CamstackContext,\n ICamera,\n StreamOption,\n ConnectionMode,\n IPanTiltZoom,\n} from '@camstack/types'\nimport type { OnvifClient } from './onvif-client'\n\nexport interface OnvifDeviceConfig {\n readonly cameraId: string\n readonly cameraName: string\n readonly providerId: string\n readonly rtspUrl: string\n readonly subStreamUrl?: string\n readonly snapshotUrl?: string\n readonly hasPtz: boolean\n readonly profiles: ReadonlyArray<{\n token: string\n name: string\n videoWidth?: number\n videoHeight?: number\n }>\n readonly manufacturer?: string\n readonly model?: string\n readonly firmware?: string\n}\n\nexport class OnvifDevice implements IDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly type: DeviceType = DeviceType.Camera\n readonly capabilities: DeviceCapabilityName[]\n readonly ctx: CamstackContext\n\n private readonly capabilityMap = new Map<DeviceCapabilityName, IDeviceCapability>()\n\n constructor(\n private readonly client: OnvifClient,\n private readonly config: OnvifDeviceConfig,\n ctx: CamstackContext,\n ) {\n this.id = `${config.providerId}/${config.cameraId}`\n this.name = config.cameraName\n this.providerId = config.providerId\n this.ctx = ctx\n\n const caps: DeviceCapabilityName[] = ['camera']\n if (config.hasPtz) caps.push('panTiltZoom')\n this.capabilities = caps\n\n this.capabilityMap.set('camera', this.createCamera())\n if (config.hasPtz) this.capabilityMap.set('panTiltZoom', this.createPtz())\n }\n\n getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null {\n return (this.capabilityMap.get(cap) as T) ?? null\n }\n\n hasCapability(cap: DeviceCapabilityName): boolean {\n return this.capabilityMap.has(cap)\n }\n\n getState(): DeviceState {\n return { online: true }\n }\n\n getMetadata(): DeviceMetadata {\n return {\n manufacturer: this.config.manufacturer ?? 'ONVIF',\n model: this.config.model,\n firmware: this.config.firmware,\n }\n }\n\n private createCamera(): ICamera {\n const { config } = this\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (config.snapshotUrl) {\n const res = await fetch(config.snapshotUrl)\n return Buffer.from(await res.arrayBuffer())\n }\n return Buffer.alloc(0)\n },\n\n async getStreamOptions(): Promise<StreamOption[]> {\n const options: StreamOption[] = [\n {\n id: `${config.cameraId}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: config.rtspUrl,\n },\n ]\n if (config.subStreamUrl) {\n options.push({\n id: `${config.cameraId}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: config.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? config.rtspUrl\n },\n\n getConnectionMode(): ConnectionMode {\n return 'on-demand'\n },\n\n async setConnectionMode() {},\n }\n }\n\n private createPtz(): IPanTiltZoom {\n const { client } = this\n return {\n kind: 'panTiltZoom',\n\n async move(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom, speed: cmd.speed })\n },\n\n async continuousMove(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom })\n },\n\n async stop() {\n await client.ptzStop()\n },\n\n async getPresets() {\n const presets = await client.getPtzPresets()\n return presets.map((p) => ({ id: p.token, name: p.name }))\n },\n\n async goToPreset(presetId: string) {\n await client.gotoPreset(presetId)\n },\n\n async goHome() {\n await client.ptzAbsoluteMove({ x: 0, y: 0, zoom: 0 })\n },\n\n async getPosition() {\n return { pan: 0, tilt: 0, zoom: 0 }\n },\n }\n }\n}\n","import { Discovery } from 'onvif'\nimport type { DiscoveredOnvifCamera } from './onvif-types'\n\n/**\n * Discovers ONVIF cameras on the local network via WS-Discovery.\n * Returns a list of found cameras after the given timeout.\n */\nexport async function discoverOnvifCameras(\n timeout: number = 5000,\n): Promise<readonly DiscoveredOnvifCamera[]> {\n return new Promise((resolve) => {\n const cameras: DiscoveredOnvifCamera[] = []\n\n Discovery.on('device', (cam: any) => {\n cameras.push({\n host: cam.hostname,\n port: cam.port ?? 80,\n name: cam.name,\n manufacturer: cam.manufacturer,\n model: cam.model,\n scopes: cam.scopes,\n })\n })\n\n Discovery.probe({ timeout })\n\n setTimeout(() => {\n Discovery.removeAllListeners('device')\n resolve(cameras)\n }, timeout + 500)\n })\n}\n","import type { IElementConfig } from '@camstack/types'\nimport type { IStorageLocation } from '@camstack/types'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n constructor(\n private readonly elementId: string,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n if (!this.storage.structured) {\n this.loaded = true\n return\n }\n\n try {\n const records = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n if (records.length > 0) {\n this.cache = (records[0] as any).data ?? {}\n }\n } catch {\n // Storage might not be ready yet\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n if (!this.storage.structured) return\n\n try {\n const existing = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n\n if (existing.length > 0) {\n await this.storage.structured.update('config', this.elementId, this.cache)\n } else {\n await this.storage.structured.insert({\n collection: 'config',\n id: this.elementId,\n data: this.cache,\n })\n }\n } catch {\n // Storage might not be ready\n }\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[parts[parts.length - 1]!] = value\n}\n","import { OnvifClient } from './onvif-client'\nimport { OnvifDevice } from './onvif-device'\nimport { discoverOnvifCameras } from './onvif-discovery'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n DeviceType,\n DeviceCapabilityName,\n CamstackContext,\n} from '@camstack/types'\nimport type { OnvifProviderConfig, OnvifCameraConfig, DiscoveredOnvifCamera } from './onvif-types'\n\nexport class OnvifProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'onvif'\n readonly name: string\n readonly discoveryMode: 'both' = 'both'\n readonly ctx: CamstackContext\n\n private devices: OnvifDevice[] = []\n private readonly clients: Map<string, OnvifClient> = new Map()\n private lastDiscoveredCameras: readonly DiscoveredOnvifCamera[] = []\n\n constructor(\n private readonly config: OnvifProviderConfig,\n ctx: CamstackContext,\n ) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n }\n\n async start(): Promise<void> {\n // 1. Auto-discover cameras on LAN (if enabled)\n if (this.config.discovery?.enabled !== false) {\n const timeout = this.config.discovery?.timeout ?? 5000\n this.ctx.logger.info(`Starting ONVIF discovery (timeout: ${timeout}ms)`)\n\n const discovered = await discoverOnvifCameras(timeout)\n this.ctx.logger.info(`Discovered ${discovered.length} ONVIF camera(s)`)\n\n for (const cam of discovered) {\n await this.addCamera({\n id: cam.host.replace(/\\./g, '-'),\n name: cam.name ?? cam.host,\n host: cam.host,\n port: cam.port,\n username: this.config.defaultUsername,\n password: this.config.defaultPassword,\n })\n }\n }\n\n // 2. Add manually configured cameras\n for (const cam of this.config.cameras ?? []) {\n await this.addCamera(cam)\n }\n\n // 3. Restore previously adopted cameras from storage\n await this.loadAdoptedCameras()\n\n this.ctx.logger.info(\n `ONVIF provider started with ${this.devices.length} camera(s)`,\n )\n }\n\n private async addCamera(cam: OnvifCameraConfig): Promise<void> {\n try {\n const client = new OnvifClient(\n cam.host,\n cam.port ?? 80,\n cam.username ?? '',\n cam.password ?? '',\n this.ctx.logger.child(cam.name),\n )\n await client.connect()\n\n const info = await client.getDeviceInfo()\n const profiles = await client.getProfiles()\n const mainProfile = profiles[0]\n const subProfile = profiles.length > 1 ? profiles[1] : undefined\n\n const rtspUrl = cam.rtspUrl ?? (await client.getStreamUri(mainProfile?.token))\n const subStreamUrl = subProfile\n ? await client.getStreamUri(subProfile.token)\n : undefined\n\n let snapshotUrl: string | undefined\n try {\n snapshotUrl = await client.getSnapshotUri(mainProfile?.token)\n } catch {\n // Snapshot not supported by this camera\n }\n\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: this.ctx.logger.child(cam.name),\n eventBus: this.ctx.eventBus,\n storage: this.ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage),\n }\n\n const device = new OnvifDevice(\n client,\n {\n cameraId: cam.id,\n cameraName: cam.name,\n providerId: this.id,\n rtspUrl,\n subStreamUrl,\n snapshotUrl,\n hasPtz: client.hasPtz(),\n profiles,\n manufacturer: info.manufacturer,\n model: info.model,\n firmware: info.firmwareVersion,\n },\n deviceCtx,\n )\n\n this.devices = [...this.devices, device]\n this.clients.set(cam.id, client)\n this.ctx.logger.info(\n `Camera ${cam.name} (${cam.host}) connected — PTZ: ${client.hasPtz()}, profiles: ${profiles.length}`,\n )\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`)\n }\n }\n\n private async loadAdoptedCameras(): Promise<void> {\n const storage = this.ctx.storage\n if (!storage.structured) return\n\n try {\n const records = await storage.structured.query('adopted-cameras', {})\n for (const record of records) {\n const cam = (record as any).data as OnvifCameraConfig\n // Skip if already loaded (e.g. matched by manual config or discovery)\n const alreadyLoaded = this.devices.some(\n (d) => d.id === `${this.id}/${cam.id}`,\n )\n if (!alreadyLoaded) {\n await this.addCamera(cam)\n }\n }\n if (records.length > 0) {\n this.ctx.logger.info(`Restored ${records.length} adopted camera(s) from storage`)\n }\n } catch {\n // Storage might not be ready yet\n }\n }\n\n async stop(): Promise<void> {\n for (const client of this.clients.values()) {\n client.disconnect()\n }\n this.devices = []\n this.clients.clear()\n this.ctx.logger.info('ONVIF provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n const cameras = await discoverOnvifCameras()\n this.lastDiscoveredCameras = cameras\n return cameras.map((c) => ({\n externalId: c.host,\n name: c.name ?? c.host,\n type: 'camera' as DeviceType,\n capabilities: ['camera'] as DeviceCapabilityName[],\n metadata: { manufacturer: c.manufacturer, model: c.model },\n }))\n }\n\n async adoptDevice(externalId: string, config?: Record<string, unknown>): Promise<IDevice> {\n // Check if already adopted\n const existingDevice = this.devices.find(\n (d) => d.id === `${this.id}/${externalId.replace(/\\./g, '-')}`,\n )\n if (existingDevice) {\n return existingDevice\n }\n\n // Find the discovered camera by host (externalId === host)\n let discovered = this.lastDiscoveredCameras.find((c) => c.host === externalId)\n if (!discovered) {\n // Re-discover if not in cache\n this.ctx.logger.info(`Camera ${externalId} not in cache, re-discovering...`)\n const cameras = await discoverOnvifCameras()\n this.lastDiscoveredCameras = cameras\n discovered = cameras.find((c) => c.host === externalId)\n }\n if (!discovered) {\n throw new Error(`Camera with externalId \"${externalId}\" not found on network`)\n }\n\n const cameraId = externalId.replace(/\\./g, '-')\n const cameraConfig: OnvifCameraConfig = {\n id: cameraId,\n name: (config?.['name'] as string) ?? discovered.name ?? externalId,\n host: discovered.host,\n port: discovered.port,\n username: (config?.['username'] as string) ?? this.config.defaultUsername,\n password: (config?.['password'] as string) ?? this.config.defaultPassword,\n }\n\n await this.addCamera(cameraConfig)\n\n const device = this.devices.find((d) => d.id === `${this.id}/${cameraId}`)\n if (!device) {\n throw new Error(`Failed to create device for camera ${externalId}`)\n }\n\n // Persist adopted camera config so it survives restarts\n await this.persistAdoptedCamera(cameraConfig)\n\n this.ctx.logger.info(`Adopted camera ${cameraConfig.name} (${externalId})`)\n return device\n }\n\n private async persistAdoptedCamera(cam: OnvifCameraConfig): Promise<void> {\n const storage = this.ctx.storage\n if (!storage.structured) return\n\n try {\n const existing = await storage.structured.query('adopted-cameras', {\n where: { id: cam.id },\n limit: 1,\n })\n\n if (existing.length === 0) {\n await storage.structured.insert({\n collection: 'adopted-cameras',\n id: cam.id,\n data: { ...cam },\n })\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to persist adopted camera ${cam.id}: ${message}`)\n }\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema() {\n return {\n id: 'string',\n name: 'string',\n host: 'string (IP or hostname)',\n port: 'number (default: 80)',\n username: 'string (optional)',\n password: 'string (optional)',\n rtspUrl: 'string (optional, override ONVIF stream URI)',\n }\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // ONVIF events via pipeline — no native event subscription here\n return () => {}\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { OnvifProvider } from './onvif-provider'\nimport type { OnvifProviderConfig } from './onvif-types'\n\nexport class OnvifProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-onvif',\n name: 'ONVIF Camera Provider',\n version: '0.1.0',\n description: 'Discovery automatica di camere ONVIF sulla rete locale',\n capabilities: ['device-provider'],\n }\n\n private provider: OnvifProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as OnvifProviderConfig\n\n const providerConfig: OnvifProviderConfig = {\n id: config.id ?? 'onvif-default',\n name: config.name ?? 'ONVIF Cameras',\n discovery: config.discovery,\n cameras: config.cameras,\n defaultUsername: config.defaultUsername,\n defaultPassword: config.defaultPassword,\n }\n\n this.provider = new OnvifProvider(providerConfig, {\n id: context.id,\n logger: context.logger,\n eventBus: context.eventBus,\n storage: context.storage,\n config: context.config,\n })\n\n context.logger.info('ONVIF provider addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.stop()\n this.provider = null\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'device-provider' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'discovery',\n title: 'ONVIF Discovery',\n description: 'Auto-discover ONVIF cameras on the local network',\n columns: 2,\n fields: [\n { type: 'boolean', key: 'discovery.enabled', label: 'Enable Auto-Discovery' },\n { type: 'number', key: 'discovery.timeout', label: 'Discovery Timeout', unit: 'ms', min: 1000, max: 30000, step: 1000 },\n ],\n },\n {\n id: 'credentials',\n title: 'Default Credentials',\n description: 'Default credentials for discovered cameras',\n columns: 2,\n fields: [\n { type: 'text', key: 'defaultUsername', label: 'Default Username' },\n { type: 'password', key: 'defaultPassword', label: 'Default Password', showToggle: true },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // Restart provider with new config\n }\n}\n"],"mappings":"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","names":["import_onvif"]}
package/dist/index.mjs CHANGED
@@ -165,6 +165,7 @@ var OnvifClient = class {
165
165
  };
166
166
 
167
167
  // src/onvif-device.ts
168
+ import { DeviceType } from "@camstack/types";
168
169
  var OnvifDevice = class {
169
170
  constructor(client, config, ctx) {
170
171
  this.client = client;
@@ -182,7 +183,7 @@ var OnvifDevice = class {
182
183
  id;
183
184
  name;
184
185
  providerId;
185
- type = "camera";
186
+ type = DeviceType.Camera;
186
187
  capabilities;
187
188
  ctx;
188
189
  capabilityMap = /* @__PURE__ */ new Map();
@@ -424,6 +425,7 @@ var OnvifProvider = class {
424
425
  ctx;
425
426
  devices = [];
426
427
  clients = /* @__PURE__ */ new Map();
428
+ lastDiscoveredCameras = [];
427
429
  async start() {
428
430
  if (this.config.discovery?.enabled !== false) {
429
431
  const timeout = this.config.discovery?.timeout ?? 5e3;
@@ -444,6 +446,7 @@ var OnvifProvider = class {
444
446
  for (const cam of this.config.cameras ?? []) {
445
447
  await this.addCamera(cam);
446
448
  }
449
+ await this.loadAdoptedCameras();
447
450
  this.ctx.logger.info(
448
451
  `ONVIF provider started with ${this.devices.length} camera(s)`
449
452
  );
@@ -503,6 +506,26 @@ var OnvifProvider = class {
503
506
  this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`);
504
507
  }
505
508
  }
509
+ async loadAdoptedCameras() {
510
+ const storage = this.ctx.storage;
511
+ if (!storage.structured) return;
512
+ try {
513
+ const records = await storage.structured.query("adopted-cameras", {});
514
+ for (const record of records) {
515
+ const cam = record.data;
516
+ const alreadyLoaded = this.devices.some(
517
+ (d) => d.id === `${this.id}/${cam.id}`
518
+ );
519
+ if (!alreadyLoaded) {
520
+ await this.addCamera(cam);
521
+ }
522
+ }
523
+ if (records.length > 0) {
524
+ this.ctx.logger.info(`Restored ${records.length} adopted camera(s) from storage`);
525
+ }
526
+ } catch {
527
+ }
528
+ }
506
529
  async stop() {
507
530
  for (const client of this.clients.values()) {
508
531
  client.disconnect();
@@ -516,6 +539,7 @@ var OnvifProvider = class {
516
539
  }
517
540
  async discoverDevices() {
518
541
  const cameras = await discoverOnvifCameras();
542
+ this.lastDiscoveredCameras = cameras;
519
543
  return cameras.map((c) => ({
520
544
  externalId: c.host,
521
545
  name: c.name ?? c.host,
@@ -524,6 +548,61 @@ var OnvifProvider = class {
524
548
  metadata: { manufacturer: c.manufacturer, model: c.model }
525
549
  }));
526
550
  }
551
+ async adoptDevice(externalId, config) {
552
+ const existingDevice = this.devices.find(
553
+ (d) => d.id === `${this.id}/${externalId.replace(/\./g, "-")}`
554
+ );
555
+ if (existingDevice) {
556
+ return existingDevice;
557
+ }
558
+ let discovered = this.lastDiscoveredCameras.find((c) => c.host === externalId);
559
+ if (!discovered) {
560
+ this.ctx.logger.info(`Camera ${externalId} not in cache, re-discovering...`);
561
+ const cameras = await discoverOnvifCameras();
562
+ this.lastDiscoveredCameras = cameras;
563
+ discovered = cameras.find((c) => c.host === externalId);
564
+ }
565
+ if (!discovered) {
566
+ throw new Error(`Camera with externalId "${externalId}" not found on network`);
567
+ }
568
+ const cameraId = externalId.replace(/\./g, "-");
569
+ const cameraConfig = {
570
+ id: cameraId,
571
+ name: config?.["name"] ?? discovered.name ?? externalId,
572
+ host: discovered.host,
573
+ port: discovered.port,
574
+ username: config?.["username"] ?? this.config.defaultUsername,
575
+ password: config?.["password"] ?? this.config.defaultPassword
576
+ };
577
+ await this.addCamera(cameraConfig);
578
+ const device = this.devices.find((d) => d.id === `${this.id}/${cameraId}`);
579
+ if (!device) {
580
+ throw new Error(`Failed to create device for camera ${externalId}`);
581
+ }
582
+ await this.persistAdoptedCamera(cameraConfig);
583
+ this.ctx.logger.info(`Adopted camera ${cameraConfig.name} (${externalId})`);
584
+ return device;
585
+ }
586
+ async persistAdoptedCamera(cam) {
587
+ const storage = this.ctx.storage;
588
+ if (!storage.structured) return;
589
+ try {
590
+ const existing = await storage.structured.query("adopted-cameras", {
591
+ where: { id: cam.id },
592
+ limit: 1
593
+ });
594
+ if (existing.length === 0) {
595
+ await storage.structured.insert({
596
+ collection: "adopted-cameras",
597
+ id: cam.id,
598
+ data: { ...cam }
599
+ });
600
+ }
601
+ } catch (err) {
602
+ const message = err instanceof Error ? err.message : String(err);
603
+ this.ctx.logger.warn(`Failed to persist adopted camera ${cam.id}: ${message}`);
604
+ }
605
+ }
527
606
  getDevices() {
528
607
  return [...this.devices];
529
608
  }
@@ -550,6 +629,7 @@ var OnvifProviderAddon = class {
550
629
  id: "provider-onvif",
551
630
  name: "ONVIF Camera Provider",
552
631
  version: "0.1.0",
632
+ description: "Discovery automatica di camere ONVIF sulla rete locale",
553
633
  capabilities: ["device-provider"]
554
634
  };
555
635
  provider = null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/onvif-client.ts","../src/onvif-device.ts","../src/onvif-discovery.ts","../src/element-config-store.ts","../src/onvif-provider.ts","../src/addon.ts"],"sourcesContent":["import { Cam } from 'onvif'\nimport type { IScopedLogger } from '@camstack/types'\n\nexport class OnvifClient {\n private cam: InstanceType<typeof Cam> | null = null\n\n constructor(\n private readonly host: string,\n private readonly port: number,\n private readonly username: string,\n private readonly password: string,\n private readonly logger: IScopedLogger,\n ) {}\n\n /** Connect to the camera */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.cam = new Cam(\n {\n hostname: this.host,\n port: this.port,\n username: this.username,\n password: this.password,\n },\n (err: Error | null) => {\n if (err) {\n reject(err)\n } else {\n this.logger.info(`Connected to ONVIF camera at ${this.host}:${this.port}`)\n resolve()\n }\n },\n )\n })\n }\n\n /** Get device info */\n async getDeviceInfo(): Promise<{\n manufacturer: string\n model: string\n firmwareVersion: string\n serialNumber: string\n }> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getDeviceInformation((err: Error | null, info: any) => {\n if (err) reject(err)\n else resolve(info)\n })\n })\n }\n\n /** Get RTSP stream URI */\n async getStreamUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = { protocol: 'RTSP' }\n if (profileToken) options.profileToken = profileToken\n this.cam.getStreamUri(options, (err: Error | null, stream: any) => {\n if (err) reject(err)\n else resolve(stream.uri)\n })\n })\n }\n\n /** Get available media profiles */\n async getProfiles(): Promise<\n Array<{ token: string; name: string; videoWidth?: number; videoHeight?: number }>\n > {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getProfiles((err: Error | null, profiles: any[]) => {\n if (err) reject(err)\n else\n resolve(\n profiles.map((p: any) => ({\n token: p.$.token ?? p.token,\n name: p.name,\n videoWidth: p.videoEncoderConfiguration?.resolution?.width,\n videoHeight: p.videoEncoderConfiguration?.resolution?.height,\n })),\n )\n })\n })\n }\n\n /** Get snapshot URI */\n async getSnapshotUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = {}\n if (profileToken) options.profileToken = profileToken\n this.cam.getSnapshotUri(options, (err: Error | null, res: any) => {\n if (err) reject(err)\n else resolve(res.uri)\n })\n })\n }\n\n /** Check if PTZ is supported */\n hasPtz(): boolean {\n return (this.cam as any)?.ptzUri != null\n }\n\n /** PTZ move (continuous) */\n async ptzMove(options: {\n x?: number\n y?: number\n zoom?: number\n speed?: number\n }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.continuousMove(\n {\n x: options.x ?? 0,\n y: options.y ?? 0,\n zoom: options.zoom ?? 0,\n timeout: 1000,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** PTZ stop */\n async ptzStop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.stop({}, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n /** PTZ absolute move */\n async ptzAbsoluteMove(options: { x: number; y: number; zoom: number }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.absoluteMove(\n {\n x: options.x,\n y: options.y,\n zoom: options.zoom,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** Get PTZ presets */\n async getPtzPresets(): Promise<Array<{ token: string; name: string }>> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getPresets({}, (err: Error | null, presets: any) => {\n if (err) reject(err)\n else {\n const list = Array.isArray(presets) ? presets : [presets]\n resolve(\n list.filter(Boolean).map((p: any) => ({\n token: p.$.token ?? String(p.token),\n name: p.name ?? p.$.token ?? 'Preset',\n })),\n )\n }\n })\n })\n }\n\n /** Go to PTZ preset */\n async gotoPreset(presetToken: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.gotoPreset({ preset: presetToken }, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n disconnect(): void {\n this.cam = null\n }\n}\n","import type {\n IDevice,\n DeviceType,\n DeviceState,\n DeviceMetadata,\n DeviceCapabilityName,\n IDeviceCapability,\n CamstackContext,\n ICamera,\n StreamOption,\n ConnectionMode,\n IPanTiltZoom,\n} from '@camstack/types'\nimport type { OnvifClient } from './onvif-client'\n\nexport interface OnvifDeviceConfig {\n readonly cameraId: string\n readonly cameraName: string\n readonly providerId: string\n readonly rtspUrl: string\n readonly subStreamUrl?: string\n readonly snapshotUrl?: string\n readonly hasPtz: boolean\n readonly profiles: ReadonlyArray<{\n token: string\n name: string\n videoWidth?: number\n videoHeight?: number\n }>\n readonly manufacturer?: string\n readonly model?: string\n readonly firmware?: string\n}\n\nexport class OnvifDevice implements IDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly type: DeviceType = 'camera'\n readonly capabilities: DeviceCapabilityName[]\n readonly ctx: CamstackContext\n\n private readonly capabilityMap = new Map<DeviceCapabilityName, IDeviceCapability>()\n\n constructor(\n private readonly client: OnvifClient,\n private readonly config: OnvifDeviceConfig,\n ctx: CamstackContext,\n ) {\n this.id = `${config.providerId}/${config.cameraId}`\n this.name = config.cameraName\n this.providerId = config.providerId\n this.ctx = ctx\n\n const caps: DeviceCapabilityName[] = ['camera']\n if (config.hasPtz) caps.push('panTiltZoom')\n this.capabilities = caps\n\n this.capabilityMap.set('camera', this.createCamera())\n if (config.hasPtz) this.capabilityMap.set('panTiltZoom', this.createPtz())\n }\n\n getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null {\n return (this.capabilityMap.get(cap) as T) ?? null\n }\n\n hasCapability(cap: DeviceCapabilityName): boolean {\n return this.capabilityMap.has(cap)\n }\n\n getState(): DeviceState {\n return { online: true }\n }\n\n getMetadata(): DeviceMetadata {\n return {\n manufacturer: this.config.manufacturer ?? 'ONVIF',\n model: this.config.model,\n firmware: this.config.firmware,\n }\n }\n\n private createCamera(): ICamera {\n const { config } = this\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (config.snapshotUrl) {\n const res = await fetch(config.snapshotUrl)\n return Buffer.from(await res.arrayBuffer())\n }\n return Buffer.alloc(0)\n },\n\n async getStreamOptions(): Promise<StreamOption[]> {\n const options: StreamOption[] = [\n {\n id: `${config.cameraId}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: config.rtspUrl,\n },\n ]\n if (config.subStreamUrl) {\n options.push({\n id: `${config.cameraId}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: config.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? config.rtspUrl\n },\n\n getConnectionMode(): ConnectionMode {\n return 'on-demand'\n },\n\n async setConnectionMode() {},\n }\n }\n\n private createPtz(): IPanTiltZoom {\n const { client } = this\n return {\n kind: 'panTiltZoom',\n\n async move(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom, speed: cmd.speed })\n },\n\n async continuousMove(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom })\n },\n\n async stop() {\n await client.ptzStop()\n },\n\n async getPresets() {\n const presets = await client.getPtzPresets()\n return presets.map((p) => ({ id: p.token, name: p.name }))\n },\n\n async goToPreset(presetId: string) {\n await client.gotoPreset(presetId)\n },\n\n async goHome() {\n await client.ptzAbsoluteMove({ x: 0, y: 0, zoom: 0 })\n },\n\n async getPosition() {\n return { pan: 0, tilt: 0, zoom: 0 }\n },\n }\n }\n}\n","import { Discovery } from 'onvif'\nimport type { DiscoveredOnvifCamera } from './onvif-types'\n\n/**\n * Discovers ONVIF cameras on the local network via WS-Discovery.\n * Returns a list of found cameras after the given timeout.\n */\nexport async function discoverOnvifCameras(\n timeout: number = 5000,\n): Promise<readonly DiscoveredOnvifCamera[]> {\n return new Promise((resolve) => {\n const cameras: DiscoveredOnvifCamera[] = []\n\n Discovery.on('device', (cam: any) => {\n cameras.push({\n host: cam.hostname,\n port: cam.port ?? 80,\n name: cam.name,\n manufacturer: cam.manufacturer,\n model: cam.model,\n scopes: cam.scopes,\n })\n })\n\n Discovery.probe({ timeout })\n\n setTimeout(() => {\n Discovery.removeAllListeners('device')\n resolve(cameras)\n }, timeout + 500)\n })\n}\n","import type { IElementConfig } from '@camstack/types'\nimport type { IStorageLocation } from '@camstack/types'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n constructor(\n private readonly elementId: string,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n if (!this.storage.structured) {\n this.loaded = true\n return\n }\n\n try {\n const records = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n if (records.length > 0) {\n this.cache = (records[0] as any).data ?? {}\n }\n } catch {\n // Storage might not be ready yet\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n if (!this.storage.structured) return\n\n try {\n const existing = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n\n if (existing.length > 0) {\n await this.storage.structured.update('config', this.elementId, this.cache)\n } else {\n await this.storage.structured.insert({\n collection: 'config',\n id: this.elementId,\n data: this.cache,\n })\n }\n } catch {\n // Storage might not be ready\n }\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[parts[parts.length - 1]!] = value\n}\n","import { OnvifClient } from './onvif-client'\nimport { OnvifDevice } from './onvif-device'\nimport { discoverOnvifCameras } from './onvif-discovery'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n DeviceType,\n DeviceCapabilityName,\n CamstackContext,\n} from '@camstack/types'\nimport type { OnvifProviderConfig, OnvifCameraConfig } from './onvif-types'\n\nexport class OnvifProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'onvif'\n readonly name: string\n readonly discoveryMode: 'both' = 'both'\n readonly ctx: CamstackContext\n\n private devices: OnvifDevice[] = []\n private readonly clients: Map<string, OnvifClient> = new Map()\n\n constructor(\n private readonly config: OnvifProviderConfig,\n ctx: CamstackContext,\n ) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n }\n\n async start(): Promise<void> {\n // 1. Auto-discover cameras on LAN (if enabled)\n if (this.config.discovery?.enabled !== false) {\n const timeout = this.config.discovery?.timeout ?? 5000\n this.ctx.logger.info(`Starting ONVIF discovery (timeout: ${timeout}ms)`)\n\n const discovered = await discoverOnvifCameras(timeout)\n this.ctx.logger.info(`Discovered ${discovered.length} ONVIF camera(s)`)\n\n for (const cam of discovered) {\n await this.addCamera({\n id: cam.host.replace(/\\./g, '-'),\n name: cam.name ?? cam.host,\n host: cam.host,\n port: cam.port,\n username: this.config.defaultUsername,\n password: this.config.defaultPassword,\n })\n }\n }\n\n // 2. Add manually configured cameras\n for (const cam of this.config.cameras ?? []) {\n await this.addCamera(cam)\n }\n\n this.ctx.logger.info(\n `ONVIF provider started with ${this.devices.length} camera(s)`,\n )\n }\n\n private async addCamera(cam: OnvifCameraConfig): Promise<void> {\n try {\n const client = new OnvifClient(\n cam.host,\n cam.port ?? 80,\n cam.username ?? '',\n cam.password ?? '',\n this.ctx.logger.child(cam.name),\n )\n await client.connect()\n\n const info = await client.getDeviceInfo()\n const profiles = await client.getProfiles()\n const mainProfile = profiles[0]\n const subProfile = profiles.length > 1 ? profiles[1] : undefined\n\n const rtspUrl = cam.rtspUrl ?? (await client.getStreamUri(mainProfile?.token))\n const subStreamUrl = subProfile\n ? await client.getStreamUri(subProfile.token)\n : undefined\n\n let snapshotUrl: string | undefined\n try {\n snapshotUrl = await client.getSnapshotUri(mainProfile?.token)\n } catch {\n // Snapshot not supported by this camera\n }\n\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: this.ctx.logger.child(cam.name),\n eventBus: this.ctx.eventBus,\n storage: this.ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage),\n }\n\n const device = new OnvifDevice(\n client,\n {\n cameraId: cam.id,\n cameraName: cam.name,\n providerId: this.id,\n rtspUrl,\n subStreamUrl,\n snapshotUrl,\n hasPtz: client.hasPtz(),\n profiles,\n manufacturer: info.manufacturer,\n model: info.model,\n firmware: info.firmwareVersion,\n },\n deviceCtx,\n )\n\n this.devices = [...this.devices, device]\n this.clients.set(cam.id, client)\n this.ctx.logger.info(\n `Camera ${cam.name} (${cam.host}) connected — PTZ: ${client.hasPtz()}, profiles: ${profiles.length}`,\n )\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`)\n }\n }\n\n async stop(): Promise<void> {\n for (const client of this.clients.values()) {\n client.disconnect()\n }\n this.devices = []\n this.clients.clear()\n this.ctx.logger.info('ONVIF provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n const cameras = await discoverOnvifCameras()\n return cameras.map((c) => ({\n externalId: c.host,\n name: c.name ?? c.host,\n type: 'camera' as DeviceType,\n capabilities: ['camera'] as DeviceCapabilityName[],\n metadata: { manufacturer: c.manufacturer, model: c.model },\n }))\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema() {\n return {\n id: 'string',\n name: 'string',\n host: 'string (IP or hostname)',\n port: 'number (default: 80)',\n username: 'string (optional)',\n password: 'string (optional)',\n rtspUrl: 'string (optional, override ONVIF stream URI)',\n }\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // ONVIF events via pipeline — no native event subscription here\n return () => {}\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { OnvifProvider } from './onvif-provider'\nimport type { OnvifProviderConfig } from './onvif-types'\n\nexport class OnvifProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-onvif',\n name: 'ONVIF Camera Provider',\n version: '0.1.0',\n capabilities: ['device-provider'],\n }\n\n private provider: OnvifProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as OnvifProviderConfig\n\n const providerConfig: OnvifProviderConfig = {\n id: config.id ?? 'onvif-default',\n name: config.name ?? 'ONVIF Cameras',\n discovery: config.discovery,\n cameras: config.cameras,\n defaultUsername: config.defaultUsername,\n defaultPassword: config.defaultPassword,\n }\n\n this.provider = new OnvifProvider(providerConfig, {\n id: context.id,\n logger: context.logger,\n eventBus: context.eventBus,\n storage: context.storage,\n config: context.config,\n })\n\n context.logger.info('ONVIF provider addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.stop()\n this.provider = null\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'device-provider' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'discovery',\n title: 'ONVIF Discovery',\n description: 'Auto-discover ONVIF cameras on the local network',\n columns: 2,\n fields: [\n { type: 'boolean', key: 'discovery.enabled', label: 'Enable Auto-Discovery' },\n { type: 'number', key: 'discovery.timeout', label: 'Discovery Timeout', unit: 'ms', min: 1000, max: 30000, step: 1000 },\n ],\n },\n {\n id: 'credentials',\n title: 'Default Credentials',\n description: 'Default credentials for discovered cameras',\n columns: 2,\n fields: [\n { type: 'text', key: 'defaultUsername', label: 'Default Username' },\n { type: 'password', key: 'defaultPassword', label: 'Default Password', showToggle: true },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // Restart provider with new config\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/onvif-client.ts","../src/onvif-device.ts","../src/onvif-discovery.ts","../src/element-config-store.ts","../src/onvif-provider.ts","../src/addon.ts"],"sourcesContent":["import { Cam } from 'onvif'\nimport type { IScopedLogger } from '@camstack/types'\n\nexport class OnvifClient {\n private cam: InstanceType<typeof Cam> | null = null\n\n constructor(\n private readonly host: string,\n private readonly port: number,\n private readonly username: string,\n private readonly password: string,\n private readonly logger: IScopedLogger,\n ) {}\n\n /** Connect to the camera */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.cam = new Cam(\n {\n hostname: this.host,\n port: this.port,\n username: this.username,\n password: this.password,\n },\n (err: Error | null) => {\n if (err) {\n reject(err)\n } else {\n this.logger.info(`Connected to ONVIF camera at ${this.host}:${this.port}`)\n resolve()\n }\n },\n )\n })\n }\n\n /** Get device info */\n async getDeviceInfo(): Promise<{\n manufacturer: string\n model: string\n firmwareVersion: string\n serialNumber: string\n }> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getDeviceInformation((err: Error | null, info: any) => {\n if (err) reject(err)\n else resolve(info)\n })\n })\n }\n\n /** Get RTSP stream URI */\n async getStreamUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = { protocol: 'RTSP' }\n if (profileToken) options.profileToken = profileToken\n this.cam.getStreamUri(options, (err: Error | null, stream: any) => {\n if (err) reject(err)\n else resolve(stream.uri)\n })\n })\n }\n\n /** Get available media profiles */\n async getProfiles(): Promise<\n Array<{ token: string; name: string; videoWidth?: number; videoHeight?: number }>\n > {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getProfiles((err: Error | null, profiles: any[]) => {\n if (err) reject(err)\n else\n resolve(\n profiles.map((p: any) => ({\n token: p.$.token ?? p.token,\n name: p.name,\n videoWidth: p.videoEncoderConfiguration?.resolution?.width,\n videoHeight: p.videoEncoderConfiguration?.resolution?.height,\n })),\n )\n })\n })\n }\n\n /** Get snapshot URI */\n async getSnapshotUri(profileToken?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n const options: Record<string, unknown> = {}\n if (profileToken) options.profileToken = profileToken\n this.cam.getSnapshotUri(options, (err: Error | null, res: any) => {\n if (err) reject(err)\n else resolve(res.uri)\n })\n })\n }\n\n /** Check if PTZ is supported */\n hasPtz(): boolean {\n return (this.cam as any)?.ptzUri != null\n }\n\n /** PTZ move (continuous) */\n async ptzMove(options: {\n x?: number\n y?: number\n zoom?: number\n speed?: number\n }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.continuousMove(\n {\n x: options.x ?? 0,\n y: options.y ?? 0,\n zoom: options.zoom ?? 0,\n timeout: 1000,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** PTZ stop */\n async ptzStop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.stop({}, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n /** PTZ absolute move */\n async ptzAbsoluteMove(options: { x: number; y: number; zoom: number }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.absoluteMove(\n {\n x: options.x,\n y: options.y,\n zoom: options.zoom,\n },\n (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n },\n )\n })\n }\n\n /** Get PTZ presets */\n async getPtzPresets(): Promise<Array<{ token: string; name: string }>> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.getPresets({}, (err: Error | null, presets: any) => {\n if (err) reject(err)\n else {\n const list = Array.isArray(presets) ? presets : [presets]\n resolve(\n list.filter(Boolean).map((p: any) => ({\n token: p.$.token ?? String(p.token),\n name: p.name ?? p.$.token ?? 'Preset',\n })),\n )\n }\n })\n })\n }\n\n /** Go to PTZ preset */\n async gotoPreset(presetToken: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.cam) return reject(new Error('Not connected'))\n this.cam.gotoPreset({ preset: presetToken }, (err: Error | null) => {\n if (err) reject(err)\n else resolve()\n })\n })\n }\n\n disconnect(): void {\n this.cam = null\n }\n}\n","import { DeviceType } from '@camstack/types'\nimport type {\n IDevice,\n DeviceState,\n DeviceMetadata,\n DeviceCapabilityName,\n IDeviceCapability,\n CamstackContext,\n ICamera,\n StreamOption,\n ConnectionMode,\n IPanTiltZoom,\n} from '@camstack/types'\nimport type { OnvifClient } from './onvif-client'\n\nexport interface OnvifDeviceConfig {\n readonly cameraId: string\n readonly cameraName: string\n readonly providerId: string\n readonly rtspUrl: string\n readonly subStreamUrl?: string\n readonly snapshotUrl?: string\n readonly hasPtz: boolean\n readonly profiles: ReadonlyArray<{\n token: string\n name: string\n videoWidth?: number\n videoHeight?: number\n }>\n readonly manufacturer?: string\n readonly model?: string\n readonly firmware?: string\n}\n\nexport class OnvifDevice implements IDevice {\n readonly id: string\n readonly name: string\n readonly providerId: string\n readonly type: DeviceType = DeviceType.Camera\n readonly capabilities: DeviceCapabilityName[]\n readonly ctx: CamstackContext\n\n private readonly capabilityMap = new Map<DeviceCapabilityName, IDeviceCapability>()\n\n constructor(\n private readonly client: OnvifClient,\n private readonly config: OnvifDeviceConfig,\n ctx: CamstackContext,\n ) {\n this.id = `${config.providerId}/${config.cameraId}`\n this.name = config.cameraName\n this.providerId = config.providerId\n this.ctx = ctx\n\n const caps: DeviceCapabilityName[] = ['camera']\n if (config.hasPtz) caps.push('panTiltZoom')\n this.capabilities = caps\n\n this.capabilityMap.set('camera', this.createCamera())\n if (config.hasPtz) this.capabilityMap.set('panTiltZoom', this.createPtz())\n }\n\n getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null {\n return (this.capabilityMap.get(cap) as T) ?? null\n }\n\n hasCapability(cap: DeviceCapabilityName): boolean {\n return this.capabilityMap.has(cap)\n }\n\n getState(): DeviceState {\n return { online: true }\n }\n\n getMetadata(): DeviceMetadata {\n return {\n manufacturer: this.config.manufacturer ?? 'ONVIF',\n model: this.config.model,\n firmware: this.config.firmware,\n }\n }\n\n private createCamera(): ICamera {\n const { config } = this\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (config.snapshotUrl) {\n const res = await fetch(config.snapshotUrl)\n return Buffer.from(await res.arrayBuffer())\n }\n return Buffer.alloc(0)\n },\n\n async getStreamOptions(): Promise<StreamOption[]> {\n const options: StreamOption[] = [\n {\n id: `${config.cameraId}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: config.rtspUrl,\n },\n ]\n if (config.subStreamUrl) {\n options.push({\n id: `${config.cameraId}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: config.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? config.rtspUrl\n },\n\n getConnectionMode(): ConnectionMode {\n return 'on-demand'\n },\n\n async setConnectionMode() {},\n }\n }\n\n private createPtz(): IPanTiltZoom {\n const { client } = this\n return {\n kind: 'panTiltZoom',\n\n async move(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom, speed: cmd.speed })\n },\n\n async continuousMove(cmd) {\n await client.ptzMove({ x: cmd.pan, y: cmd.tilt, zoom: cmd.zoom })\n },\n\n async stop() {\n await client.ptzStop()\n },\n\n async getPresets() {\n const presets = await client.getPtzPresets()\n return presets.map((p) => ({ id: p.token, name: p.name }))\n },\n\n async goToPreset(presetId: string) {\n await client.gotoPreset(presetId)\n },\n\n async goHome() {\n await client.ptzAbsoluteMove({ x: 0, y: 0, zoom: 0 })\n },\n\n async getPosition() {\n return { pan: 0, tilt: 0, zoom: 0 }\n },\n }\n }\n}\n","import { Discovery } from 'onvif'\nimport type { DiscoveredOnvifCamera } from './onvif-types'\n\n/**\n * Discovers ONVIF cameras on the local network via WS-Discovery.\n * Returns a list of found cameras after the given timeout.\n */\nexport async function discoverOnvifCameras(\n timeout: number = 5000,\n): Promise<readonly DiscoveredOnvifCamera[]> {\n return new Promise((resolve) => {\n const cameras: DiscoveredOnvifCamera[] = []\n\n Discovery.on('device', (cam: any) => {\n cameras.push({\n host: cam.hostname,\n port: cam.port ?? 80,\n name: cam.name,\n manufacturer: cam.manufacturer,\n model: cam.model,\n scopes: cam.scopes,\n })\n })\n\n Discovery.probe({ timeout })\n\n setTimeout(() => {\n Discovery.removeAllListeners('device')\n resolve(cameras)\n }, timeout + 500)\n })\n}\n","import type { IElementConfig } from '@camstack/types'\nimport type { IStorageLocation } from '@camstack/types'\n\n/**\n * Persisted config store for a single element.\n * Reads/writes to the element's scoped storage under the 'config' collection.\n * Notifies listeners on every change.\n */\nexport class ElementConfigStore implements IElementConfig {\n private cache: Record<string, unknown> = {}\n private listeners: Set<(config: Record<string, unknown>) => void> = new Set()\n private loaded = false\n\n constructor(\n private readonly elementId: string,\n private readonly storage: IStorageLocation,\n ) {}\n\n /** Load config from storage into cache. Called once on first access. */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n if (!this.storage.structured) {\n this.loaded = true\n return\n }\n\n try {\n const records = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n if (records.length > 0) {\n this.cache = (records[0] as any).data ?? {}\n }\n } catch {\n // Storage might not be ready yet\n }\n this.loaded = true\n }\n\n getAll(): Record<string, unknown> {\n return { ...this.cache }\n }\n\n get<T = unknown>(key: string): T | undefined {\n const parts = key.split('.')\n let current: unknown = this.cache\n for (const part of parts) {\n if (current == null || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current as T | undefined\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await this.ensureLoaded()\n setNestedValue(this.cache, key, value)\n await this.persist()\n this.notifyListeners()\n }\n\n async setAll(config: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n this.cache = { ...config }\n await this.persist()\n this.notifyListeners()\n }\n\n onChange(callback: (config: Record<string, unknown>) => void): () => void {\n this.listeners.add(callback)\n return () => { this.listeners.delete(callback) }\n }\n\n /** Initialize from storage — called by ContextFactory after creation */\n async load(): Promise<void> {\n await this.ensureLoaded()\n }\n\n /** Initialize with default values (doesn't overwrite existing) */\n async loadDefaults(defaults: Record<string, unknown>): Promise<void> {\n await this.ensureLoaded()\n if (Object.keys(this.cache).length === 0) {\n this.cache = { ...defaults }\n await this.persist()\n }\n }\n\n private async persist(): Promise<void> {\n if (!this.storage.structured) return\n\n try {\n const existing = await this.storage.structured.query('config', {\n where: { id: this.elementId },\n limit: 1,\n })\n\n if (existing.length > 0) {\n await this.storage.structured.update('config', this.elementId, this.cache)\n } else {\n await this.storage.structured.insert({\n collection: 'config',\n id: this.elementId,\n data: this.cache,\n })\n }\n } catch {\n // Storage might not be ready\n }\n }\n\n private notifyListeners(): void {\n const snapshot = this.getAll()\n for (const listener of this.listeners) {\n try {\n listener(snapshot)\n } catch {\n // Don't let one bad listener kill others\n }\n }\n }\n}\n\nfunction setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n let current: Record<string, unknown> = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[parts[parts.length - 1]!] = value\n}\n","import { OnvifClient } from './onvif-client'\nimport { OnvifDevice } from './onvif-device'\nimport { discoverOnvifCameras } from './onvif-discovery'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n DeviceType,\n DeviceCapabilityName,\n CamstackContext,\n} from '@camstack/types'\nimport type { OnvifProviderConfig, OnvifCameraConfig, DiscoveredOnvifCamera } from './onvif-types'\n\nexport class OnvifProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'onvif'\n readonly name: string\n readonly discoveryMode: 'both' = 'both'\n readonly ctx: CamstackContext\n\n private devices: OnvifDevice[] = []\n private readonly clients: Map<string, OnvifClient> = new Map()\n private lastDiscoveredCameras: readonly DiscoveredOnvifCamera[] = []\n\n constructor(\n private readonly config: OnvifProviderConfig,\n ctx: CamstackContext,\n ) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n }\n\n async start(): Promise<void> {\n // 1. Auto-discover cameras on LAN (if enabled)\n if (this.config.discovery?.enabled !== false) {\n const timeout = this.config.discovery?.timeout ?? 5000\n this.ctx.logger.info(`Starting ONVIF discovery (timeout: ${timeout}ms)`)\n\n const discovered = await discoverOnvifCameras(timeout)\n this.ctx.logger.info(`Discovered ${discovered.length} ONVIF camera(s)`)\n\n for (const cam of discovered) {\n await this.addCamera({\n id: cam.host.replace(/\\./g, '-'),\n name: cam.name ?? cam.host,\n host: cam.host,\n port: cam.port,\n username: this.config.defaultUsername,\n password: this.config.defaultPassword,\n })\n }\n }\n\n // 2. Add manually configured cameras\n for (const cam of this.config.cameras ?? []) {\n await this.addCamera(cam)\n }\n\n // 3. Restore previously adopted cameras from storage\n await this.loadAdoptedCameras()\n\n this.ctx.logger.info(\n `ONVIF provider started with ${this.devices.length} camera(s)`,\n )\n }\n\n private async addCamera(cam: OnvifCameraConfig): Promise<void> {\n try {\n const client = new OnvifClient(\n cam.host,\n cam.port ?? 80,\n cam.username ?? '',\n cam.password ?? '',\n this.ctx.logger.child(cam.name),\n )\n await client.connect()\n\n const info = await client.getDeviceInfo()\n const profiles = await client.getProfiles()\n const mainProfile = profiles[0]\n const subProfile = profiles.length > 1 ? profiles[1] : undefined\n\n const rtspUrl = cam.rtspUrl ?? (await client.getStreamUri(mainProfile?.token))\n const subStreamUrl = subProfile\n ? await client.getStreamUri(subProfile.token)\n : undefined\n\n let snapshotUrl: string | undefined\n try {\n snapshotUrl = await client.getSnapshotUri(mainProfile?.token)\n } catch {\n // Snapshot not supported by this camera\n }\n\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: this.ctx.logger.child(cam.name),\n eventBus: this.ctx.eventBus,\n storage: this.ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage),\n }\n\n const device = new OnvifDevice(\n client,\n {\n cameraId: cam.id,\n cameraName: cam.name,\n providerId: this.id,\n rtspUrl,\n subStreamUrl,\n snapshotUrl,\n hasPtz: client.hasPtz(),\n profiles,\n manufacturer: info.manufacturer,\n model: info.model,\n firmware: info.firmwareVersion,\n },\n deviceCtx,\n )\n\n this.devices = [...this.devices, device]\n this.clients.set(cam.id, client)\n this.ctx.logger.info(\n `Camera ${cam.name} (${cam.host}) connected — PTZ: ${client.hasPtz()}, profiles: ${profiles.length}`,\n )\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to connect to ${cam.host}: ${message}`)\n }\n }\n\n private async loadAdoptedCameras(): Promise<void> {\n const storage = this.ctx.storage\n if (!storage.structured) return\n\n try {\n const records = await storage.structured.query('adopted-cameras', {})\n for (const record of records) {\n const cam = (record as any).data as OnvifCameraConfig\n // Skip if already loaded (e.g. matched by manual config or discovery)\n const alreadyLoaded = this.devices.some(\n (d) => d.id === `${this.id}/${cam.id}`,\n )\n if (!alreadyLoaded) {\n await this.addCamera(cam)\n }\n }\n if (records.length > 0) {\n this.ctx.logger.info(`Restored ${records.length} adopted camera(s) from storage`)\n }\n } catch {\n // Storage might not be ready yet\n }\n }\n\n async stop(): Promise<void> {\n for (const client of this.clients.values()) {\n client.disconnect()\n }\n this.devices = []\n this.clients.clear()\n this.ctx.logger.info('ONVIF provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n const cameras = await discoverOnvifCameras()\n this.lastDiscoveredCameras = cameras\n return cameras.map((c) => ({\n externalId: c.host,\n name: c.name ?? c.host,\n type: 'camera' as DeviceType,\n capabilities: ['camera'] as DeviceCapabilityName[],\n metadata: { manufacturer: c.manufacturer, model: c.model },\n }))\n }\n\n async adoptDevice(externalId: string, config?: Record<string, unknown>): Promise<IDevice> {\n // Check if already adopted\n const existingDevice = this.devices.find(\n (d) => d.id === `${this.id}/${externalId.replace(/\\./g, '-')}`,\n )\n if (existingDevice) {\n return existingDevice\n }\n\n // Find the discovered camera by host (externalId === host)\n let discovered = this.lastDiscoveredCameras.find((c) => c.host === externalId)\n if (!discovered) {\n // Re-discover if not in cache\n this.ctx.logger.info(`Camera ${externalId} not in cache, re-discovering...`)\n const cameras = await discoverOnvifCameras()\n this.lastDiscoveredCameras = cameras\n discovered = cameras.find((c) => c.host === externalId)\n }\n if (!discovered) {\n throw new Error(`Camera with externalId \"${externalId}\" not found on network`)\n }\n\n const cameraId = externalId.replace(/\\./g, '-')\n const cameraConfig: OnvifCameraConfig = {\n id: cameraId,\n name: (config?.['name'] as string) ?? discovered.name ?? externalId,\n host: discovered.host,\n port: discovered.port,\n username: (config?.['username'] as string) ?? this.config.defaultUsername,\n password: (config?.['password'] as string) ?? this.config.defaultPassword,\n }\n\n await this.addCamera(cameraConfig)\n\n const device = this.devices.find((d) => d.id === `${this.id}/${cameraId}`)\n if (!device) {\n throw new Error(`Failed to create device for camera ${externalId}`)\n }\n\n // Persist adopted camera config so it survives restarts\n await this.persistAdoptedCamera(cameraConfig)\n\n this.ctx.logger.info(`Adopted camera ${cameraConfig.name} (${externalId})`)\n return device\n }\n\n private async persistAdoptedCamera(cam: OnvifCameraConfig): Promise<void> {\n const storage = this.ctx.storage\n if (!storage.structured) return\n\n try {\n const existing = await storage.structured.query('adopted-cameras', {\n where: { id: cam.id },\n limit: 1,\n })\n\n if (existing.length === 0) {\n await storage.structured.insert({\n collection: 'adopted-cameras',\n id: cam.id,\n data: { ...cam },\n })\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn(`Failed to persist adopted camera ${cam.id}: ${message}`)\n }\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema() {\n return {\n id: 'string',\n name: 'string',\n host: 'string (IP or hostname)',\n port: 'number (default: 80)',\n username: 'string (optional)',\n password: 'string (optional)',\n rtspUrl: 'string (optional, override ONVIF stream URI)',\n }\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // ONVIF events via pipeline — no native event subscription here\n return () => {}\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { OnvifProvider } from './onvif-provider'\nimport type { OnvifProviderConfig } from './onvif-types'\n\nexport class OnvifProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-onvif',\n name: 'ONVIF Camera Provider',\n version: '0.1.0',\n description: 'Discovery automatica di camere ONVIF sulla rete locale',\n capabilities: ['device-provider'],\n }\n\n private provider: OnvifProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as OnvifProviderConfig\n\n const providerConfig: OnvifProviderConfig = {\n id: config.id ?? 'onvif-default',\n name: config.name ?? 'ONVIF Cameras',\n discovery: config.discovery,\n cameras: config.cameras,\n defaultUsername: config.defaultUsername,\n defaultPassword: config.defaultPassword,\n }\n\n this.provider = new OnvifProvider(providerConfig, {\n id: context.id,\n logger: context.logger,\n eventBus: context.eventBus,\n storage: context.storage,\n config: context.config,\n })\n\n context.logger.info('ONVIF provider addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.provider?.stop()\n this.provider = null\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'device-provider' && this.provider) {\n return this.provider as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'discovery',\n title: 'ONVIF Discovery',\n description: 'Auto-discover ONVIF cameras on the local network',\n columns: 2,\n fields: [\n { type: 'boolean', key: 'discovery.enabled', label: 'Enable Auto-Discovery' },\n { type: 'number', key: 'discovery.timeout', label: 'Discovery Timeout', unit: 'ms', min: 1000, max: 30000, step: 1000 },\n ],\n },\n {\n id: 'credentials',\n title: 'Default Credentials',\n description: 'Default credentials for discovered cameras',\n columns: 2,\n fields: [\n { type: 'text', key: 'defaultUsername', label: 'Default Username' },\n { type: 'password', key: 'defaultPassword', label: 'Default Password', showToggle: true },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return {}\n }\n\n async onConfigChange(_config: Record<string, unknown>): Promise<void> {\n // Restart provider with new config\n }\n}\n"],"mappings":";AAAA,SAAS,WAAW;AAGb,IAAM,cAAN,MAAkB;AAAA,EAGvB,YACmB,MACA,MACA,UACA,UACA,QACjB;AALiB;AACA;AACA;AACA;AACA;AAAA,EAChB;AAAA,EARK,MAAuC;AAAA;AAAA,EAW/C,MAAM,UAAyB;AAC7B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAK,MAAM,IAAI;AAAA,QACb;AAAA,UACE,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,UACf,UAAU,KAAK;AAAA,QACjB;AAAA,QACA,CAAC,QAAsB;AACrB,cAAI,KAAK;AACP,mBAAO,GAAG;AAAA,UACZ,OAAO;AACL,iBAAK,OAAO,KAAK,gCAAgC,KAAK,IAAI,IAAI,KAAK,IAAI,EAAE;AACzE,oBAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,gBAKH;AACD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI,qBAAqB,CAAC,KAAmB,SAAc;AAC9D,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ,IAAI;AAAA,MACnB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,aAAa,cAAwC;AACzD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,YAAM,UAAmC,EAAE,UAAU,OAAO;AAC5D,UAAI,aAAc,SAAQ,eAAe;AACzC,WAAK,IAAI,aAAa,SAAS,CAAC,KAAmB,WAAgB;AACjE,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ,OAAO,GAAG;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,cAEJ;AACA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI,YAAY,CAAC,KAAmB,aAAoB;AAC3D,YAAI,IAAK,QAAO,GAAG;AAAA;AAEjB;AAAA,YACE,SAAS,IAAI,CAAC,OAAY;AAAA,cACxB,OAAO,EAAE,EAAE,SAAS,EAAE;AAAA,cACtB,MAAM,EAAE;AAAA,cACR,YAAY,EAAE,2BAA2B,YAAY;AAAA,cACrD,aAAa,EAAE,2BAA2B,YAAY;AAAA,YACxD,EAAE;AAAA,UACJ;AAAA,MACJ,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,eAAe,cAAwC;AAC3D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,YAAM,UAAmC,CAAC;AAC1C,UAAI,aAAc,SAAQ,eAAe;AACzC,WAAK,IAAI,eAAe,SAAS,CAAC,KAAmB,QAAa;AAChE,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ,IAAI,GAAG;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAkB;AAChB,WAAQ,KAAK,KAAa,UAAU;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,QAAQ,SAKI;AAChB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI;AAAA,QACP;AAAA,UACE,GAAG,QAAQ,KAAK;AAAA,UAChB,GAAG,QAAQ,KAAK;AAAA,UAChB,MAAM,QAAQ,QAAQ;AAAA,UACtB,SAAS;AAAA,QACX;AAAA,QACA,CAAC,QAAsB;AACrB,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,SAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI,KAAK,CAAC,GAAG,CAAC,QAAsB;AACvC,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ;AAAA,MACf,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,gBAAgB,SAAgE;AACpF,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI;AAAA,QACP;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,GAAG,QAAQ;AAAA,UACX,MAAM,QAAQ;AAAA,QAChB;AAAA,QACA,CAAC,QAAsB;AACrB,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,SAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,gBAAiE;AACrE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI,WAAW,CAAC,GAAG,CAAC,KAAmB,YAAiB;AAC3D,YAAI,IAAK,QAAO,GAAG;AAAA,aACd;AACH,gBAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AACxD;AAAA,YACE,KAAK,OAAO,OAAO,EAAE,IAAI,CAAC,OAAY;AAAA,cACpC,OAAO,EAAE,EAAE,SAAS,OAAO,EAAE,KAAK;AAAA,cAClC,MAAM,EAAE,QAAQ,EAAE,EAAE,SAAS;AAAA,YAC/B,EAAE;AAAA,UACJ;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,WAAW,aAAoC;AACnD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,IAAK,QAAO,OAAO,IAAI,MAAM,eAAe,CAAC;AACvD,WAAK,IAAI,WAAW,EAAE,QAAQ,YAAY,GAAG,CAAC,QAAsB;AAClE,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ;AAAA,MACf,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,aAAmB;AACjB,SAAK,MAAM;AAAA,EACb;AACF;;;AC9LA,SAAS,kBAAkB;AAkCpB,IAAM,cAAN,MAAqC;AAAA,EAU1C,YACmB,QACA,QACjB,KACA;AAHiB;AACA;AAGjB,SAAK,KAAK,GAAG,OAAO,UAAU,IAAI,OAAO,QAAQ;AACjD,SAAK,OAAO,OAAO;AACnB,SAAK,aAAa,OAAO;AACzB,SAAK,MAAM;AAEX,UAAM,OAA+B,CAAC,QAAQ;AAC9C,QAAI,OAAO,OAAQ,MAAK,KAAK,aAAa;AAC1C,SAAK,eAAe;AAEpB,SAAK,cAAc,IAAI,UAAU,KAAK,aAAa,CAAC;AACpD,QAAI,OAAO,OAAQ,MAAK,cAAc,IAAI,eAAe,KAAK,UAAU,CAAC;AAAA,EAC3E;AAAA,EAzBS;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAmB,WAAW;AAAA,EAC9B;AAAA,EACA;AAAA,EAEQ,gBAAgB,oBAAI,IAA6C;AAAA,EAoBlF,cAA2C,KAAqC;AAC9E,WAAQ,KAAK,cAAc,IAAI,GAAG,KAAW;AAAA,EAC/C;AAAA,EAEA,cAAc,KAAoC;AAChD,WAAO,KAAK,cAAc,IAAI,GAAG;AAAA,EACnC;AAAA,EAEA,WAAwB;AACtB,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AAAA,EAEA,cAA8B;AAC5B,WAAO;AAAA,MACL,cAAc,KAAK,OAAO,gBAAgB;AAAA,MAC1C,OAAO,KAAK,OAAO;AAAA,MACnB,UAAU,KAAK,OAAO;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,eAAwB;AAC9B,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MAEN,MAAM,cAAc;AAClB,YAAI,OAAO,aAAa;AACtB,gBAAM,MAAM,MAAM,MAAM,OAAO,WAAW;AAC1C,iBAAO,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AAAA,QAC5C;AACA,eAAO,OAAO,MAAM,CAAC;AAAA,MACvB;AAAA,MAEA,MAAM,mBAA4C;AAChD,cAAM,UAA0B;AAAA,UAC9B;AAAA,YACE,IAAI,GAAG,OAAO,QAAQ;AAAA,YACtB,OAAO;AAAA,YACP,UAAU;AAAA,YACV,SAAS;AAAA,YACT,KAAK,OAAO;AAAA,UACd;AAAA,QACF;AACA,YAAI,OAAO,cAAc;AACvB,kBAAQ,KAAK;AAAA,YACX,IAAI,GAAG,OAAO,QAAQ;AAAA,YACtB,OAAO;AAAA,YACP,UAAU;AAAA,YACV,SAAS;AAAA,YACT,KAAK,OAAO;AAAA,UACd,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAAA,MAEA,MAAM,aAAa,QAAsB;AACvC,eAAO,OAAO,OAAO,OAAO;AAAA,MAC9B;AAAA,MAEA,oBAAoC;AAClC,eAAO;AAAA,MACT;AAAA,MAEA,MAAM,oBAAoB;AAAA,MAAC;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,YAA0B;AAChC,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MAEN,MAAM,KAAK,KAAK;AACd,cAAM,OAAO,QAAQ,EAAE,GAAG,IAAI,KAAK,GAAG,IAAI,MAAM,MAAM,IAAI,MAAM,OAAO,IAAI,MAAM,CAAC;AAAA,MACpF;AAAA,MAEA,MAAM,eAAe,KAAK;AACxB,cAAM,OAAO,QAAQ,EAAE,GAAG,IAAI,KAAK,GAAG,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AAAA,MAClE;AAAA,MAEA,MAAM,OAAO;AACX,cAAM,OAAO,QAAQ;AAAA,MACvB;AAAA,MAEA,MAAM,aAAa;AACjB,cAAM,UAAU,MAAM,OAAO,cAAc;AAC3C,eAAO,QAAQ,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,MAAM,EAAE,KAAK,EAAE;AAAA,MAC3D;AAAA,MAEA,MAAM,WAAW,UAAkB;AACjC,cAAM,OAAO,WAAW,QAAQ;AAAA,MAClC;AAAA,MAEA,MAAM,SAAS;AACb,cAAM,OAAO,gBAAgB,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC;AAAA,MACtD;AAAA,MAEA,MAAM,cAAc;AAClB,eAAO,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF;;;ACpKA,SAAS,iBAAiB;AAO1B,eAAsB,qBACpB,UAAkB,KACyB;AAC3C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,UAAmC,CAAC;AAE1C,cAAU,GAAG,UAAU,CAAC,QAAa;AACnC,cAAQ,KAAK;AAAA,QACX,MAAM,IAAI;AAAA,QACV,MAAM,IAAI,QAAQ;AAAA,QAClB,MAAM,IAAI;AAAA,QACV,cAAc,IAAI;AAAA,QAClB,OAAO,IAAI;AAAA,QACX,QAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAED,cAAU,MAAM,EAAE,QAAQ,CAAC;AAE3B,eAAW,MAAM;AACf,gBAAU,mBAAmB,QAAQ;AACrC,cAAQ,OAAO;AAAA,IACjB,GAAG,UAAU,GAAG;AAAA,EAClB,CAAC;AACH;;;ACvBO,IAAM,qBAAN,MAAmD;AAAA,EAKxD,YACmB,WACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAPK,QAAiC,CAAC;AAAA,EAClC,YAA4D,oBAAI,IAAI;AAAA,EACpE,SAAS;AAAA;AAAA,EAQjB,MAAc,eAA8B;AAC1C,QAAI,KAAK,OAAQ;AACjB,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,WAAK,SAAS;AACd;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,QAAQ,WAAW,MAAM,UAAU;AAAA,QAC5D,OAAO,EAAE,IAAI,KAAK,UAAU;AAAA,QAC5B,OAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ,SAAS,GAAG;AACtB,aAAK,QAAS,QAAQ,CAAC,EAAU,QAAQ,CAAC;AAAA,MAC5C;AAAA,IACF,QAAQ;AAAA,IAER;AACA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,SAAkC;AAChC,WAAO,EAAE,GAAG,KAAK,MAAM;AAAA,EACzB;AAAA,EAEA,IAAiB,KAA4B;AAC3C,UAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAI,UAAmB,KAAK;AAC5B,eAAW,QAAQ,OAAO;AACxB,UAAI,WAAW,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC3D,gBAAW,QAAoC,IAAI;AAAA,IACrD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,KAAa,OAA+B;AACpD,UAAM,KAAK,aAAa;AACxB,mBAAe,KAAK,OAAO,KAAK,KAAK;AACrC,UAAM,KAAK,QAAQ;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO,QAAgD;AAC3D,UAAM,KAAK,aAAa;AACxB,SAAK,QAAQ,EAAE,GAAG,OAAO;AACzB,UAAM,KAAK,QAAQ;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,SAAS,UAAiE;AACxE,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AAAE,WAAK,UAAU,OAAO,QAAQ;AAAA,IAAE;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,OAAsB;AAC1B,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAM,aAAa,UAAkD;AACnE,UAAM,KAAK,aAAa;AACxB,QAAI,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG;AACxC,WAAK,QAAQ,EAAE,GAAG,SAAS;AAC3B,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,QAAI,CAAC,KAAK,QAAQ,WAAY;AAE9B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,QAAQ,WAAW,MAAM,UAAU;AAAA,QAC7D,OAAO,EAAE,IAAI,KAAK,UAAU;AAAA,QAC5B,OAAO;AAAA,MACT,CAAC;AAED,UAAI,SAAS,SAAS,GAAG;AACvB,cAAM,KAAK,QAAQ,WAAW,OAAO,UAAU,KAAK,WAAW,KAAK,KAAK;AAAA,MAC3E,OAAO;AACL,cAAM,KAAK,QAAQ,WAAW,OAAO;AAAA,UACnC,YAAY;AAAA,UACZ,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,UAAM,WAAW,KAAK,OAAO;AAC7B,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI;AACF,iBAAS,QAAQ;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,KAA8B,MAAc,OAAsB;AACxF,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,UAAmC;AACvC,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,EAAE,QAAQ,YAAY,OAAO,QAAQ,IAAI,MAAM,YAAY,QAAQ,IAAI,MAAM,MAAM;AACrF,cAAQ,IAAI,IAAI,CAAC;AAAA,IACnB;AACA,cAAU,QAAQ,IAAI;AAAA,EACxB;AACA,UAAQ,MAAM,MAAM,SAAS,CAAC,CAAE,IAAI;AACtC;;;ACrHO,IAAM,gBAAN,MAA+C;AAAA,EAWpD,YACmB,QACjB,KACA;AAFiB;AAGjB,SAAK,KAAK,OAAO;AACjB,SAAK,OAAO,OAAO;AACnB,SAAK,MAAM;AAAA,EACb;AAAA,EAjBS;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,gBAAwB;AAAA,EACxB;AAAA,EAED,UAAyB,CAAC;AAAA,EACjB,UAAoC,oBAAI,IAAI;AAAA,EACrD,wBAA0D,CAAC;AAAA,EAWnE,MAAM,QAAuB;AAE3B,QAAI,KAAK,OAAO,WAAW,YAAY,OAAO;AAC5C,YAAM,UAAU,KAAK,OAAO,WAAW,WAAW;AAClD,WAAK,IAAI,OAAO,KAAK,sCAAsC,OAAO,KAAK;AAEvE,YAAM,aAAa,MAAM,qBAAqB,OAAO;AACrD,WAAK,IAAI,OAAO,KAAK,cAAc,WAAW,MAAM,kBAAkB;AAEtE,iBAAW,OAAO,YAAY;AAC5B,cAAM,KAAK,UAAU;AAAA,UACnB,IAAI,IAAI,KAAK,QAAQ,OAAO,GAAG;AAAA,UAC/B,MAAM,IAAI,QAAQ,IAAI;AAAA,UACtB,MAAM,IAAI;AAAA,UACV,MAAM,IAAI;AAAA,UACV,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,eAAW,OAAO,KAAK,OAAO,WAAW,CAAC,GAAG;AAC3C,YAAM,KAAK,UAAU,GAAG;AAAA,IAC1B;AAGA,UAAM,KAAK,mBAAmB;AAE9B,SAAK,IAAI,OAAO;AAAA,MACd,+BAA+B,KAAK,QAAQ,MAAM;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,KAAuC;AAC7D,QAAI;AACF,YAAM,SAAS,IAAI;AAAA,QACjB,IAAI;AAAA,QACJ,IAAI,QAAQ;AAAA,QACZ,IAAI,YAAY;AAAA,QAChB,IAAI,YAAY;AAAA,QAChB,KAAK,IAAI,OAAO,MAAM,IAAI,IAAI;AAAA,MAChC;AACA,YAAM,OAAO,QAAQ;AAErB,YAAM,OAAO,MAAM,OAAO,cAAc;AACxC,YAAM,WAAW,MAAM,OAAO,YAAY;AAC1C,YAAM,cAAc,SAAS,CAAC;AAC9B,YAAM,aAAa,SAAS,SAAS,IAAI,SAAS,CAAC,IAAI;AAEvD,YAAM,UAAU,IAAI,WAAY,MAAM,OAAO,aAAa,aAAa,KAAK;AAC5E,YAAM,eAAe,aACjB,MAAM,OAAO,aAAa,WAAW,KAAK,IAC1C;AAEJ,UAAI;AACJ,UAAI;AACF,sBAAc,MAAM,OAAO,eAAe,aAAa,KAAK;AAAA,MAC9D,QAAQ;AAAA,MAER;AAEA,YAAM,YAA6B;AAAA,QACjC,IAAI,UAAU,KAAK,EAAE,IAAI,IAAI,EAAE;AAAA,QAC/B,QAAQ,KAAK,IAAI,OAAO,MAAM,IAAI,IAAI;AAAA,QACtC,UAAU,KAAK,IAAI;AAAA,QACnB,SAAS,KAAK,IAAI;AAAA,QAClB,QAAQ,IAAI,mBAAmB,UAAU,KAAK,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,IAAI,OAAO;AAAA,MAChF;AAEA,YAAM,SAAS,IAAI;AAAA,QACjB;AAAA,QACA;AAAA,UACE,UAAU,IAAI;AAAA,UACd,YAAY,IAAI;AAAA,UAChB,YAAY,KAAK;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ,OAAO,OAAO;AAAA,UACtB;AAAA,UACA,cAAc,KAAK;AAAA,UACnB,OAAO,KAAK;AAAA,UACZ,UAAU,KAAK;AAAA,QACjB;AAAA,QACA;AAAA,MACF;AAEA,WAAK,UAAU,CAAC,GAAG,KAAK,SAAS,MAAM;AACvC,WAAK,QAAQ,IAAI,IAAI,IAAI,MAAM;AAC/B,WAAK,IAAI,OAAO;AAAA,QACd,UAAU,IAAI,IAAI,KAAK,IAAI,IAAI,2BAAsB,OAAO,OAAO,CAAC,eAAe,SAAS,MAAM;AAAA,MACpG;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAK,IAAI,OAAO,KAAK,wBAAwB,IAAI,IAAI,KAAK,OAAO,EAAE;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,MAAc,qBAAoC;AAChD,UAAM,UAAU,KAAK,IAAI;AACzB,QAAI,CAAC,QAAQ,WAAY;AAEzB,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,WAAW,MAAM,mBAAmB,CAAC,CAAC;AACpE,iBAAW,UAAU,SAAS;AAC5B,cAAM,MAAO,OAAe;AAE5B,cAAM,gBAAgB,KAAK,QAAQ;AAAA,UACjC,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,IAAI,IAAI,EAAE;AAAA,QACtC;AACA,YAAI,CAAC,eAAe;AAClB,gBAAM,KAAK,UAAU,GAAG;AAAA,QAC1B;AAAA,MACF;AACA,UAAI,QAAQ,SAAS,GAAG;AACtB,aAAK,IAAI,OAAO,KAAK,YAAY,QAAQ,MAAM,iCAAiC;AAAA,MAClF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAC1B,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,aAAO,WAAW;AAAA,IACpB;AACA,SAAK,UAAU,CAAC;AAChB,SAAK,QAAQ,MAAM;AACnB,SAAK,IAAI,OAAO,KAAK,wBAAwB;AAAA,EAC/C;AAAA,EAEA,YAA4B;AAC1B,WAAO,EAAE,WAAW,MAAM,aAAa,KAAK,QAAQ,OAAO;AAAA,EAC7D;AAAA,EAEA,MAAM,kBAA+C;AACnD,UAAM,UAAU,MAAM,qBAAqB;AAC3C,SAAK,wBAAwB;AAC7B,WAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,MACzB,YAAY,EAAE;AAAA,MACd,MAAM,EAAE,QAAQ,EAAE;AAAA,MAClB,MAAM;AAAA,MACN,cAAc,CAAC,QAAQ;AAAA,MACvB,UAAU,EAAE,cAAc,EAAE,cAAc,OAAO,EAAE,MAAM;AAAA,IAC3D,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,YAAY,YAAoB,QAAoD;AAExF,UAAM,iBAAiB,KAAK,QAAQ;AAAA,MAClC,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,IAAI,WAAW,QAAQ,OAAO,GAAG,CAAC;AAAA,IAC9D;AACA,QAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AAGA,QAAI,aAAa,KAAK,sBAAsB,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAC7E,QAAI,CAAC,YAAY;AAEf,WAAK,IAAI,OAAO,KAAK,UAAU,UAAU,kCAAkC;AAC3E,YAAM,UAAU,MAAM,qBAAqB;AAC3C,WAAK,wBAAwB;AAC7B,mBAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAAA,IACxD;AACA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,2BAA2B,UAAU,wBAAwB;AAAA,IAC/E;AAEA,UAAM,WAAW,WAAW,QAAQ,OAAO,GAAG;AAC9C,UAAM,eAAkC;AAAA,MACtC,IAAI;AAAA,MACJ,MAAO,SAAS,MAAM,KAAgB,WAAW,QAAQ;AAAA,MACzD,MAAM,WAAW;AAAA,MACjB,MAAM,WAAW;AAAA,MACjB,UAAW,SAAS,UAAU,KAAgB,KAAK,OAAO;AAAA,MAC1D,UAAW,SAAS,UAAU,KAAgB,KAAK,OAAO;AAAA,IAC5D;AAEA,UAAM,KAAK,UAAU,YAAY;AAEjC,UAAM,SAAS,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,IAAI,QAAQ,EAAE;AACzE,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,sCAAsC,UAAU,EAAE;AAAA,IACpE;AAGA,UAAM,KAAK,qBAAqB,YAAY;AAE5C,SAAK,IAAI,OAAO,KAAK,kBAAkB,aAAa,IAAI,KAAK,UAAU,GAAG;AAC1E,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBAAqB,KAAuC;AACxE,UAAM,UAAU,KAAK,IAAI;AACzB,QAAI,CAAC,QAAQ,WAAY;AAEzB,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,WAAW,MAAM,mBAAmB;AAAA,QACjE,OAAO,EAAE,IAAI,IAAI,GAAG;AAAA,QACpB,OAAO;AAAA,MACT,CAAC;AAED,UAAI,SAAS,WAAW,GAAG;AACzB,cAAM,QAAQ,WAAW,OAAO;AAAA,UAC9B,YAAY;AAAA,UACZ,IAAI,IAAI;AAAA,UACR,MAAM,EAAE,GAAG,IAAI;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAK,IAAI,OAAO,KAAK,oCAAoC,IAAI,EAAE,KAAK,OAAO,EAAE;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,aAAwB;AACtB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,oBAAoB,WAAmD;AAErE,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACF;;;ACtQO,IAAM,qBAAN,MAAkE;AAAA,EAC9D,WAA0B;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa;AAAA,IACb,cAAc,CAAC,iBAAiB;AAAA,EAClC;AAAA,EAEQ,WAAiC;AAAA,EAEzC,MAAM,WAAW,SAAsC;AACrD,UAAM,SAAS,QAAQ;AAEvB,UAAM,iBAAsC;AAAA,MAC1C,IAAI,OAAO,MAAM;AAAA,MACjB,MAAM,OAAO,QAAQ;AAAA,MACrB,WAAW,OAAO;AAAA,MAClB,SAAS,OAAO;AAAA,MAChB,iBAAiB,OAAO;AAAA,MACxB,iBAAiB,OAAO;AAAA,IAC1B;AAEA,SAAK,WAAW,IAAI,cAAc,gBAAgB;AAAA,MAChD,IAAI,QAAQ;AAAA,MACZ,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,YAAQ,OAAO,KAAK,kCAAkC;AAAA,EACxD;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,UAAU,KAAK;AAC1B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,sBACE,MACiC;AACjC,QAAI,SAAS,qBAAqB,KAAK,UAAU;AAC/C,aAAO,KAAK;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,kBAAkC;AAChC,WAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,YACN,EAAE,MAAM,WAAW,KAAK,qBAAqB,OAAO,wBAAwB;AAAA,YAC5E,EAAE,MAAM,UAAU,KAAK,qBAAqB,OAAO,qBAAqB,MAAM,MAAM,KAAK,KAAM,KAAK,KAAO,MAAM,IAAK;AAAA,UACxH;AAAA,QACF;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,YACN,EAAE,MAAM,QAAQ,KAAK,mBAAmB,OAAO,mBAAmB;AAAA,YAClE,EAAE,MAAM,YAAY,KAAK,mBAAmB,OAAO,oBAAoB,YAAY,KAAK;AAAA,UAC1F;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAqC;AACnC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAM,eAAe,SAAiD;AAAA,EAEtE;AACF;","names":[]}
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-onvif",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "ONVIF camera device provider addon for CamStack",
5
- "keywords": ["camstack", "addon", "camstack-addon", "onvif", "provider", "camera", "ip-camera"],
5
+ "keywords": [
6
+ "camstack",
7
+ "addon",
8
+ "camstack-addon",
9
+ "onvif",
10
+ "provider",
11
+ "camera",
12
+ "ip-camera"
13
+ ],
6
14
  "license": "MIT",
7
15
  "repository": {
8
16
  "type": "git",
@@ -12,21 +20,36 @@
12
20
  "module": "./dist/index.mjs",
13
21
  "types": "./dist/index.d.ts",
14
22
  "exports": {
15
- ".": { "import": "./dist/index.mjs", "require": "./dist/index.js", "types": "./dist/index.d.ts" }
23
+ ".": {
24
+ "import": "./dist/index.mjs",
25
+ "require": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ },
28
+ "./package.json": "./package.json"
16
29
  },
17
30
  "camstack": {
31
+ "displayName": "ONVIF Provider",
18
32
  "addons": [
19
33
  {
20
34
  "id": "provider-onvif",
21
35
  "entry": "./dist/addon.js",
22
36
  "slot": "provider",
37
+ "icon": "assets/icon.svg",
38
+ "color": "#06b6d4",
39
+ "instanceMode": "unique",
23
40
  "capabilities": [
24
- { "name": "device-provider", "mode": "collection" }
41
+ {
42
+ "name": "device-provider",
43
+ "mode": "collection"
44
+ }
25
45
  ]
26
46
  }
27
47
  ]
28
48
  },
29
- "files": ["dist"],
49
+ "files": [
50
+ "dist",
51
+ "assets"
52
+ ],
30
53
  "scripts": {
31
54
  "build": "tsup",
32
55
  "dev": "tsup --watch",