@camstack/addon-provider-rtsp 0.1.6 → 0.1.9

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,14 @@
1
+ import { ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
2
+
3
+ declare class RtspProviderAddon implements ICamstackAddon, IConfigurable {
4
+ readonly manifest: AddonManifest;
5
+ private provider;
6
+ initialize(context: AddonContext): Promise<void>;
7
+ shutdown(): Promise<void>;
8
+ getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
9
+ getConfigSchema(): ConfigUISchema;
10
+ getConfig(): Record<string, unknown>;
11
+ onConfigChange(_config: Record<string, unknown>): Promise<void>;
12
+ }
13
+
14
+ export { RtspProviderAddon };
@@ -0,0 +1,14 @@
1
+ import { ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
2
+
3
+ declare class RtspProviderAddon implements ICamstackAddon, IConfigurable {
4
+ readonly manifest: AddonManifest;
5
+ private provider;
6
+ initialize(context: AddonContext): Promise<void>;
7
+ shutdown(): Promise<void>;
8
+ getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
9
+ getConfigSchema(): ConfigUISchema;
10
+ getConfig(): Record<string, unknown>;
11
+ onConfigChange(_config: Record<string, unknown>): Promise<void>;
12
+ }
13
+
14
+ export { RtspProviderAddon };
package/dist/addon.js CHANGED
@@ -344,6 +344,7 @@ var RtspProviderAddon = class {
344
344
  };
345
345
  provider = null;
346
346
  async initialize(context) {
347
+ console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`);
347
348
  const config = context.addonConfig;
348
349
  const providerConfig = {
349
350
  id: config.id ?? "rtsp-default",
@@ -364,6 +365,7 @@ var RtspProviderAddon = class {
364
365
  this.provider = null;
365
366
  }
366
367
  getCapabilityProvider(name) {
368
+ console.log(`[RtspProviderAddon] getCapabilityProvider("${name}"), provider=${this.provider ? "exists" : "null"}`);
367
369
  if (name === "device-provider" && this.provider) {
368
370
  return this.provider;
369
371
  }
package/dist/addon.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/addon.ts","../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts"],"sourcesContent":["import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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","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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/addon.ts","../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts"],"sourcesContent":["import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`)\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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 console.log(`[RtspProviderAddon] getCapabilityProvider(\"${name}\"), provider=${this.provider ? 'exists' : '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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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","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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n"],"mappings":"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","names":[]}
package/dist/addon.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RtspProviderAddon
3
- } from "./chunk-FL4PRY24.mjs";
3
+ } from "./chunk-4KD2JX6K.mjs";
4
4
  export {
5
5
  RtspProviderAddon
6
6
  };
@@ -318,6 +318,7 @@ var RtspProviderAddon = class {
318
318
  };
319
319
  provider = null;
320
320
  async initialize(context) {
321
+ console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`);
321
322
  const config = context.addonConfig;
322
323
  const providerConfig = {
323
324
  id: config.id ?? "rtsp-default",
@@ -338,6 +339,7 @@ var RtspProviderAddon = class {
338
339
  this.provider = null;
339
340
  }
340
341
  getCapabilityProvider(name) {
342
+ console.log(`[RtspProviderAddon] getCapabilityProvider("${name}"), provider=${this.provider ? "exists" : "null"}`);
341
343
  if (name === "device-provider" && this.provider) {
342
344
  return this.provider;
343
345
  }
@@ -377,4 +379,4 @@ export {
377
379
  RtspProvider,
378
380
  RtspProviderAddon
379
381
  };
380
- //# sourceMappingURL=chunk-FL4PRY24.mjs.map
382
+ //# sourceMappingURL=chunk-4KD2JX6K.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts","../src/addon.ts"],"sourcesContent":["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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts","../src/addon.ts"],"sourcesContent":["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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`)\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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 console.log(`[RtspProviderAddon] getCapabilityProvider(\"${name}\"), provider=${this.provider ? 'exists' : '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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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,kBAAkB;AAcpB,IAAM,aAAN,MAAoC;AAAA,EAUzC,YACmB,QACjB,YACA,KACA;AAHiB;AAIjB,SAAK,KAAK,GAAG,UAAU,IAAI,OAAO,EAAE;AACpC,SAAK,OAAO,OAAO;AACnB,SAAK,aAAa;AAClB,SAAK,MAAM;AAKX,SAAK,eAAe,CAAC,QAAQ;AAE7B,SAAK,cAAc,IAAI,UAAU,KAAK,aAAa,CAAC;AAAA,EACtD;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,EAAE,cAAc,eAAe;AAAA,EACxC;AAAA;AAAA,EAGA,kBAAoC;AAClC,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAEQ,eAAwB;AAC9B,UAAM,MAAM,KAAK;AACjB,WAAO;AAAA,MACL,MAAM;AAAA,MAEN,MAAM,cAAc;AAClB,YAAI,IAAI,aAAa;AACnB,gBAAM,MAAM,MAAM,MAAM,IAAI,WAAW;AACvC,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,IAAI,EAAE;AAAA,YACb,OAAO;AAAA,YACP,UAAU;AAAA,YACV,SAAS;AAAA,YACT,KAAK,IAAI;AAAA,UACX;AAAA,QACF;AACA,YAAI,IAAI,cAAc;AACpB,kBAAQ,KAAK;AAAA,YACX,IAAI,GAAG,IAAI,EAAE;AAAA,YACb,OAAO;AAAA,YACP,UAAU;AAAA,YACV,SAAS;AAAA,YACT,KAAK,IAAI;AAAA,UACX,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAAA,MAEA,MAAM,aAAa,QAAsB;AACvC,eAAO,OAAO,OAAO,IAAI;AAAA,MAC3B;AAAA,MAEA,oBAAoC;AAClC,eAAO;AAAA,MACT;AAAA,MAEA,MAAM,oBAAoB;AAAA,MAAC;AAAA,IAC7B;AAAA,EACF;AACF;;;ACrGO,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;;;ACxHO,IAAM,eAAN,MAA8C;AAAA,EAC1C;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,gBAA0B;AAAA,EAC1B;AAAA,EAEQ,UAAwB,CAAC;AAAA,EAE1C,YAAY,QAA4B,KAAsB;AAC5D,SAAK,KAAK,OAAO;AACjB,SAAK,OAAO,OAAO;AACnB,SAAK,MAAM;AAGX,eAAW,OAAO,OAAO,SAAS;AAChC,WAAK,QAAQ,KAAK,KAAK,YAAY,GAAG,CAAC;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAE3B,UAAM,YAAY,KAAK,IAAI,OAAO,IAAiC,SAAS;AAC5E,QAAI,aAAa,MAAM,QAAQ,SAAS,GAAG;AACzC,iBAAW,OAAO,WAAW;AAC3B,cAAM,gBAAgB,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,IAAI,IAAI,EAAE,EAAE;AAC9E,YAAI,CAAC,eAAe;AAClB,eAAK,QAAQ,KAAK,KAAK,YAAY,GAAG,CAAC;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,SAAK,IAAI,OAAO,KAAK,8BAA8B,KAAK,QAAQ,MAAM,UAAU;AAAA,EAClF;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,IAAI,OAAO,KAAK,uBAAuB;AAAA,EAC9C;AAAA,EAEA,YAA4B;AAC1B,WAAO,EAAE,WAAW,MAAM,aAAa,KAAK,QAAQ,OAAO;AAAA,EAC7D;AAAA,EAEA,MAAM,kBAA+C;AAEnD,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,aAAwB;AACtB,WAAO,CAAC,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,wBAAwC;AACtC,WAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,SAAS;AAAA,UACT,QAAQ;AAAA,YACN;AAAA,cACE,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,UAAU;AAAA,YACZ;AAAA,YACA;AAAA,cACE,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,WAAW;AAAA,cACX,aAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,QAAmD;AACpE,UAAM,OAAO,OAAO,OAAO,QAAQ,EAAE;AACrC,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,UAAM,UAAU,MAAM,QAAQ,OAAO,OAAO,IAAI,OAAO,QAAQ,OAAO,OAAO,EAAE,IAAI,MAAM,IAAI,CAAC;AAC9F,UAAM,cAAc,OAAO,cAAc,OAAO,OAAO,WAAW,IAAI;AAEtE,UAAM,gBAAgB,QAAQ,CAAC;AAC/B,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,UAAM,WAAW,QAAQ,KAAK,IAAI,CAAC;AAEnC,UAAM,YAA8B;AAAA,MAClC,IAAI;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,MACL,cAAc,QAAQ,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,YAAY,SAAS;AACzC,SAAK,QAAQ,KAAK,MAAM;AAGxB,UAAM,KAAK,qBAAqB;AAEhC,SAAK,IAAI,OAAO,KAAK,wBAAwB,OAAO,EAAE,KAAK,IAAI,GAAG;AAElE,WAAO;AAAA,EACT;AAAA,EAEA,oBAAoB,WAAmD;AAErE,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAAA,EAEQ,YAAY,KAAmC;AACrD,UAAM,YAA6B;AAAA,MACjC,IAAI,UAAU,KAAK,EAAE,IAAI,IAAI,EAAE;AAAA,MAC/B,QAAQ,KAAK,IAAI,OAAO,MAAM,IAAI,IAAI;AAAA,MACtC,UAAU,KAAK,IAAI;AAAA,MACnB,SAAS,KAAK,IAAI;AAAA,MAClB,QAAQ,IAAI,mBAAmB,UAAU,KAAK,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,IAAI,OAAO;AAAA,IAChF;AACA,WAAO,IAAI,WAAW,KAAK,KAAK,IAAI,SAAS;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,uBAAsC;AAClD,UAAM,UAA8B,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,gBAAgB,CAAC;AACzF,UAAM,KAAK,IAAI,OAAO,IAAI,WAAW,OAAO;AAAA,EAC9C;AACF;;;AC/IO,IAAM,oBAAN,MAAiE;AAAA,EAC7D,WAA0B;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa;AAAA,IACb,cAAc,CAAC,iBAAiB;AAAA,EAClC;AAAA,EAEQ,WAAgC;AAAA,EAExC,MAAM,WAAW,SAAsC;AACrD,YAAQ,IAAI,qDAAqD,QAAQ,EAAE,YAAY,KAAK,UAAU,QAAQ,WAAW,CAAC,EAAE;AAC5H,UAAM,SAAS,QAAQ;AAIvB,UAAM,iBAAqC;AAAA,MACzC,IAAI,OAAO,MAAM;AAAA,MACjB,MAAM,OAAO,QAAQ;AAAA,MACrB,SAAS,OAAO,WAAW,CAAC;AAAA,IAC9B;AAEA,SAAK,WAAW,IAAI,aAAa,gBAAgB;AAAA,MAC/C,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,iCAAiC;AAAA,EACvD;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,UAAU,KAAK;AAC1B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,sBACE,MACiC;AACjC,YAAQ,IAAI,8CAA8C,IAAI,gBAAgB,KAAK,WAAW,WAAW,MAAM,EAAE;AACjH,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,QAAQ,KAAK,QAAQ,OAAO,iBAAiB,aAAa,eAAe;AAAA,YACjF;AAAA,cACE,MAAM;AAAA,cACN,KAAK;AAAA,cACL,OAAO;AAAA,cACP,SAAS;AAAA,cACT,SAAS;AAAA,YACX;AAAA,UACF;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":[]}
@@ -0,0 +1,63 @@
1
+ export { RtspProviderAddon } from './addon.mjs';
2
+ import { IDevice, DeviceType, DeviceCapabilityName, CamstackContext, IDeviceCapability, DeviceState, DeviceMetadata, IDeviceProvider, ProviderStatus, DiscoveredDevice, ConfigUISchema, LiveEvent } from '@camstack/types';
3
+
4
+ interface RtspCameraConfig {
5
+ readonly id: string;
6
+ readonly name: string;
7
+ readonly url: string;
8
+ readonly subStreamUrl?: string;
9
+ readonly snapshotUrl?: string;
10
+ readonly audioEnabled?: boolean;
11
+ readonly width?: number;
12
+ readonly height?: number;
13
+ }
14
+ interface RtspProviderConfig {
15
+ readonly id: string;
16
+ readonly name: string;
17
+ readonly cameras: readonly RtspCameraConfig[];
18
+ }
19
+
20
+ declare class RtspDevice implements IDevice {
21
+ private readonly config;
22
+ readonly id: string;
23
+ readonly name: string;
24
+ readonly providerId: string;
25
+ readonly type: DeviceType;
26
+ readonly capabilities: DeviceCapabilityName[];
27
+ readonly ctx: CamstackContext;
28
+ private readonly capabilityMap;
29
+ constructor(config: RtspCameraConfig, providerId: string, ctx: CamstackContext);
30
+ getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null;
31
+ hasCapability(cap: DeviceCapabilityName): boolean;
32
+ getState(): DeviceState;
33
+ getMetadata(): DeviceMetadata;
34
+ /** Return the camera config for persistence */
35
+ getCameraConfig(): RtspCameraConfig;
36
+ private createCamera;
37
+ }
38
+
39
+ declare class RtspProvider implements IDeviceProvider {
40
+ readonly id: string;
41
+ readonly type = "rtsp";
42
+ readonly name: string;
43
+ readonly discoveryMode: 'manual';
44
+ readonly ctx: CamstackContext;
45
+ private readonly devices;
46
+ constructor(config: RtspProviderConfig, ctx: CamstackContext);
47
+ start(): Promise<void>;
48
+ stop(): Promise<void>;
49
+ getStatus(): ProviderStatus;
50
+ discoverDevices(): Promise<DiscoveredDevice[]>;
51
+ getDevices(): IDevice[];
52
+ getDeviceConfigSchema(): ConfigUISchema;
53
+ createDevice(config: Record<string, unknown>): Promise<IDevice>;
54
+ subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void;
55
+ private buildDevice;
56
+ /**
57
+ * Persist all dynamically-created device configs so they survive restarts.
58
+ * Saves the full list of camera configs under the 'devices' key in the provider's config.
59
+ */
60
+ private persistDeviceConfigs;
61
+ }
62
+
63
+ export { type RtspCameraConfig, RtspDevice, RtspProvider, type RtspProviderConfig };
@@ -0,0 +1,63 @@
1
+ export { RtspProviderAddon } from './addon.js';
2
+ import { IDevice, DeviceType, DeviceCapabilityName, CamstackContext, IDeviceCapability, DeviceState, DeviceMetadata, IDeviceProvider, ProviderStatus, DiscoveredDevice, ConfigUISchema, LiveEvent } from '@camstack/types';
3
+
4
+ interface RtspCameraConfig {
5
+ readonly id: string;
6
+ readonly name: string;
7
+ readonly url: string;
8
+ readonly subStreamUrl?: string;
9
+ readonly snapshotUrl?: string;
10
+ readonly audioEnabled?: boolean;
11
+ readonly width?: number;
12
+ readonly height?: number;
13
+ }
14
+ interface RtspProviderConfig {
15
+ readonly id: string;
16
+ readonly name: string;
17
+ readonly cameras: readonly RtspCameraConfig[];
18
+ }
19
+
20
+ declare class RtspDevice implements IDevice {
21
+ private readonly config;
22
+ readonly id: string;
23
+ readonly name: string;
24
+ readonly providerId: string;
25
+ readonly type: DeviceType;
26
+ readonly capabilities: DeviceCapabilityName[];
27
+ readonly ctx: CamstackContext;
28
+ private readonly capabilityMap;
29
+ constructor(config: RtspCameraConfig, providerId: string, ctx: CamstackContext);
30
+ getCapability<T extends IDeviceCapability>(cap: DeviceCapabilityName): T | null;
31
+ hasCapability(cap: DeviceCapabilityName): boolean;
32
+ getState(): DeviceState;
33
+ getMetadata(): DeviceMetadata;
34
+ /** Return the camera config for persistence */
35
+ getCameraConfig(): RtspCameraConfig;
36
+ private createCamera;
37
+ }
38
+
39
+ declare class RtspProvider implements IDeviceProvider {
40
+ readonly id: string;
41
+ readonly type = "rtsp";
42
+ readonly name: string;
43
+ readonly discoveryMode: 'manual';
44
+ readonly ctx: CamstackContext;
45
+ private readonly devices;
46
+ constructor(config: RtspProviderConfig, ctx: CamstackContext);
47
+ start(): Promise<void>;
48
+ stop(): Promise<void>;
49
+ getStatus(): ProviderStatus;
50
+ discoverDevices(): Promise<DiscoveredDevice[]>;
51
+ getDevices(): IDevice[];
52
+ getDeviceConfigSchema(): ConfigUISchema;
53
+ createDevice(config: Record<string, unknown>): Promise<IDevice>;
54
+ subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void;
55
+ private buildDevice;
56
+ /**
57
+ * Persist all dynamically-created device configs so they survive restarts.
58
+ * Saves the full list of camera configs under the 'devices' key in the provider's config.
59
+ */
60
+ private persistDeviceConfigs;
61
+ }
62
+
63
+ export { type RtspCameraConfig, RtspDevice, RtspProvider, type RtspProviderConfig };
package/dist/index.js CHANGED
@@ -346,6 +346,7 @@ var RtspProviderAddon = class {
346
346
  };
347
347
  provider = null;
348
348
  async initialize(context) {
349
+ console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`);
349
350
  const config = context.addonConfig;
350
351
  const providerConfig = {
351
352
  id: config.id ?? "rtsp-default",
@@ -366,6 +367,7 @@ var RtspProviderAddon = class {
366
367
  this.provider = null;
367
368
  }
368
369
  getCapabilityProvider(name) {
370
+ console.log(`[RtspProviderAddon] getCapabilityProvider("${name}"), provider=${this.provider ? "exists" : "null"}`);
369
371
  if (name === "device-provider" && this.provider) {
370
372
  return this.provider;
371
373
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts","../src/addon.ts"],"sourcesContent":["export { RtspProviderAddon } from './addon'\nexport { RtspDevice } from './rtsp-device'\nexport { RtspProvider } from './rtsp-provider'\nexport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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/index.ts","../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts","../src/addon.ts"],"sourcesContent":["export { RtspProviderAddon } from './addon'\nexport { RtspDevice } from './rtsp-device'\nexport { RtspProvider } from './rtsp-provider'\nexport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\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} from '@camstack/types'\nimport type { RtspCameraConfig } from './rtsp-types'\n\nexport class RtspDevice 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 config: RtspCameraConfig,\n providerId: string,\n ctx: CamstackContext,\n ) {\n this.id = `${providerId}/${config.id}`\n this.name = config.name\n this.providerId = providerId\n this.ctx = ctx\n\n // Only camera capability is native to RTSP device.\n // motionSensor and objectDetector are provided by pipeline addons\n // via capability binding (not native to RTSP device).\n this.capabilities = ['camera']\n\n this.capabilityMap.set('camera', this.createCamera())\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 { manufacturer: 'Generic RTSP' }\n }\n\n /** Return the camera config for persistence */\n getCameraConfig(): RtspCameraConfig {\n return { ...this.config }\n }\n\n private createCamera(): ICamera {\n const cfg = this.config\n return {\n kind: 'camera',\n\n async getSnapshot() {\n if (cfg.snapshotUrl) {\n const res = await fetch(cfg.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: `${cfg.id}_main`,\n label: 'Main',\n protocol: 'rtsp',\n quality: 'main',\n url: cfg.url,\n },\n ]\n if (cfg.subStreamUrl) {\n options.push({\n id: `${cfg.id}_sub`,\n label: 'Sub',\n protocol: 'rtsp',\n quality: 'sub',\n url: cfg.subStreamUrl,\n })\n }\n return options\n },\n\n async getStreamUrl(option: StreamOption) {\n return option.url ?? cfg.url\n },\n\n getConnectionMode(): ConnectionMode {\n return 'always-on'\n },\n\n async setConnectionMode() {},\n }\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 { RtspDevice } from './rtsp-device'\nimport { ElementConfigStore } from './element-config-store'\nimport type {\n IDeviceProvider,\n ProviderStatus,\n DiscoveredDevice,\n LiveEvent,\n IDevice,\n CamstackContext,\n ConfigUISchema,\n} from '@camstack/types'\nimport type { RtspCameraConfig, RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProvider implements IDeviceProvider {\n readonly id: string\n readonly type = 'rtsp'\n readonly name: string\n readonly discoveryMode: 'manual' = 'manual'\n readonly ctx: CamstackContext\n\n private readonly devices: RtspDevice[] = []\n\n constructor(config: RtspProviderConfig, ctx: CamstackContext) {\n this.id = config.id\n this.name = config.name\n this.ctx = ctx\n\n // Create devices from static config (initial seed)\n for (const cam of config.cameras) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n\n async start(): Promise<void> {\n // Load any dynamically-created devices from persisted config\n const persisted = this.ctx.config.get<readonly RtspCameraConfig[]>('devices')\n if (persisted && Array.isArray(persisted)) {\n for (const cam of persisted) {\n const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`)\n if (!alreadyExists) {\n this.devices.push(this.buildDevice(cam))\n }\n }\n }\n\n this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`)\n }\n\n async stop(): Promise<void> {\n this.ctx.logger.info('RTSP provider stopped')\n }\n\n getStatus(): ProviderStatus {\n return { connected: true, deviceCount: this.devices.length }\n }\n\n async discoverDevices(): Promise<DiscoveredDevice[]> {\n // Manual-only provider — no auto-discovery\n return []\n }\n\n getDevices(): IDevice[] {\n return [...this.devices]\n }\n\n getDeviceConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'device-info',\n title: 'Device Configuration',\n description: 'Configure the RTSP camera connection',\n columns: 1,\n fields: [\n {\n type: 'text',\n key: 'name',\n label: 'Device Name',\n placeholder: 'e.g. Front Door Camera',\n required: true,\n },\n {\n type: 'text',\n key: 'snapshotUrl',\n label: 'Snapshot URL',\n placeholder: 'http://192.168.1.100/snapshot.jpg',\n inputType: 'url',\n description: 'HTTP URL for fetching JPEG snapshots (optional)',\n },\n ],\n },\n ],\n }\n }\n\n async createDevice(config: Record<string, unknown>): Promise<IDevice> {\n const name = String(config.name ?? '')\n if (!name.trim()) {\n throw new Error('Device name is required')\n }\n\n const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : []\n const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : undefined\n\n const mainStreamUrl = streams[0]\n if (!mainStreamUrl) {\n throw new Error('At least one stream URL is required')\n }\n\n const deviceId = `rtsp-${Date.now()}`\n\n const camConfig: RtspCameraConfig = {\n id: deviceId,\n name,\n url: mainStreamUrl,\n subStreamUrl: streams[1],\n snapshotUrl,\n }\n\n const device = this.buildDevice(camConfig)\n this.devices.push(device)\n\n // Persist the dynamically-created device configs\n await this.persistDeviceConfigs()\n\n this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`)\n\n return device\n }\n\n subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void {\n // RTSP provider has no native events — motion/detection come from pipeline\n return () => {}\n }\n\n private buildDevice(cam: RtspCameraConfig): RtspDevice {\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 return new RtspDevice(cam, this.id, deviceCtx)\n }\n\n /**\n * Persist all dynamically-created device configs so they survive restarts.\n * Saves the full list of camera configs under the 'devices' key in the provider's config.\n */\n private async persistDeviceConfigs(): Promise<void> {\n const configs: RtspCameraConfig[] = this.devices.map((device) => device.getCameraConfig())\n await this.ctx.config.set('devices', configs)\n }\n}\n","import type {\n ICamstackAddon,\n AddonManifest,\n AddonContext,\n CapabilityProviderMap,\n ConfigUISchema,\n IConfigurable,\n} from '@camstack/types'\nimport { RtspProvider } from './rtsp-provider'\nimport type { RtspProviderConfig } from './rtsp-types'\n\nexport class RtspProviderAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'provider-rtsp',\n name: 'RTSP Camera Provider',\n version: '0.1.0',\n description: 'Connessione diretta a camere via URL RTSP',\n capabilities: ['device-provider'],\n }\n\n private provider: RtspProvider | null = null\n\n async initialize(context: AddonContext): Promise<void> {\n console.log(`[RtspProviderAddon] initialize called, context.id=${context.id}, config=${JSON.stringify(context.addonConfig)}`)\n const config = context.addonConfig as unknown as Partial<RtspProviderConfig>\n\n // Always create the provider even with zero cameras —\n // users add devices manually via createDevice()\n const providerConfig: RtspProviderConfig = {\n id: config.id ?? 'rtsp-default',\n name: config.name ?? 'RTSP Cameras',\n cameras: config.cameras ?? [],\n }\n\n this.provider = new RtspProvider(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('RTSP 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 console.log(`[RtspProviderAddon] getCapabilityProvider(\"${name}\"), provider=${this.provider ? 'exists' : '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: 'general',\n title: 'RTSP Provider',\n description: 'Configure generic RTSP camera connections',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Provider Name', placeholder: 'RTSP Cameras' },\n {\n type: 'info',\n key: 'info',\n label: 'Camera Configuration',\n content: 'Individual cameras are configured via the device management interface after adding this provider.',\n variant: 'info',\n },\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":[]}
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  RtspDevice,
3
3
  RtspProvider,
4
4
  RtspProviderAddon
5
- } from "./chunk-FL4PRY24.mjs";
5
+ } from "./chunk-4KD2JX6K.mjs";
6
6
  export {
7
7
  RtspDevice,
8
8
  RtspProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",
@@ -21,9 +21,9 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "exports": {
23
23
  ".": {
24
+ "types": "./dist/index.d.ts",
24
25
  "import": "./dist/index.mjs",
25
- "require": "./dist/index.js",
26
- "types": "./dist/index.d.ts"
26
+ "require": "./dist/index.js"
27
27
  },
28
28
  "./package.json": "./package.json"
29
29
  },