@camstack/addon-provider-rtsp 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/icon.svg +6 -0
- package/dist/index.d.mts +10 -7
- package/dist/index.d.ts +10 -7
- package/dist/index.js +87 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +28 -5
package/assets/icon.svg
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#78716c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
|
|
3
|
+
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
|
|
4
|
+
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
|
|
5
|
+
<line x1="12" y1="20" x2="12.01" y2="20"/>
|
|
6
|
+
</svg>
|
package/dist/index.d.mts
CHANGED
|
@@ -41,6 +41,8 @@ declare class RtspDevice implements IDevice {
|
|
|
41
41
|
hasCapability(cap: DeviceCapabilityName): boolean;
|
|
42
42
|
getState(): DeviceState;
|
|
43
43
|
getMetadata(): DeviceMetadata;
|
|
44
|
+
/** Return the camera config for persistence */
|
|
45
|
+
getCameraConfig(): RtspCameraConfig;
|
|
44
46
|
private createCamera;
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -57,14 +59,15 @@ declare class RtspProvider implements IDeviceProvider {
|
|
|
57
59
|
getStatus(): ProviderStatus;
|
|
58
60
|
discoverDevices(): Promise<DiscoveredDevice[]>;
|
|
59
61
|
getDevices(): IDevice[];
|
|
60
|
-
getDeviceConfigSchema():
|
|
61
|
-
|
|
62
|
-
name: string;
|
|
63
|
-
url: string;
|
|
64
|
-
subStreamUrl: string;
|
|
65
|
-
snapshotUrl: string;
|
|
66
|
-
};
|
|
62
|
+
getDeviceConfigSchema(): ConfigUISchema;
|
|
63
|
+
createDevice(config: Record<string, unknown>): Promise<IDevice>;
|
|
67
64
|
subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void;
|
|
65
|
+
private buildDevice;
|
|
66
|
+
/**
|
|
67
|
+
* Persist all dynamically-created device configs so they survive restarts.
|
|
68
|
+
* Saves the full list of camera configs under the 'devices' key in the provider's config.
|
|
69
|
+
*/
|
|
70
|
+
private persistDeviceConfigs;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
export { type RtspCameraConfig, RtspDevice, RtspProvider, RtspProviderAddon, type RtspProviderConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ declare class RtspDevice implements IDevice {
|
|
|
41
41
|
hasCapability(cap: DeviceCapabilityName): boolean;
|
|
42
42
|
getState(): DeviceState;
|
|
43
43
|
getMetadata(): DeviceMetadata;
|
|
44
|
+
/** Return the camera config for persistence */
|
|
45
|
+
getCameraConfig(): RtspCameraConfig;
|
|
44
46
|
private createCamera;
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -57,14 +59,15 @@ declare class RtspProvider implements IDeviceProvider {
|
|
|
57
59
|
getStatus(): ProviderStatus;
|
|
58
60
|
discoverDevices(): Promise<DiscoveredDevice[]>;
|
|
59
61
|
getDevices(): IDevice[];
|
|
60
|
-
getDeviceConfigSchema():
|
|
61
|
-
|
|
62
|
-
name: string;
|
|
63
|
-
url: string;
|
|
64
|
-
subStreamUrl: string;
|
|
65
|
-
snapshotUrl: string;
|
|
66
|
-
};
|
|
62
|
+
getDeviceConfigSchema(): ConfigUISchema;
|
|
63
|
+
createDevice(config: Record<string, unknown>): Promise<IDevice>;
|
|
67
64
|
subscribeLiveEvents(_callback: (event: LiveEvent) => void): () => void;
|
|
65
|
+
private buildDevice;
|
|
66
|
+
/**
|
|
67
|
+
* Persist all dynamically-created device configs so they survive restarts.
|
|
68
|
+
* Saves the full list of camera configs under the 'devices' key in the provider's config.
|
|
69
|
+
*/
|
|
70
|
+
private persistDeviceConfigs;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
export { type RtspCameraConfig, RtspDevice, RtspProvider, RtspProviderAddon, type RtspProviderConfig };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
29
|
// src/rtsp-device.ts
|
|
30
|
+
var import_types = require("@camstack/types");
|
|
30
31
|
var RtspDevice = class {
|
|
31
32
|
constructor(config, providerId, ctx) {
|
|
32
33
|
this.config = config;
|
|
@@ -40,7 +41,7 @@ var RtspDevice = class {
|
|
|
40
41
|
id;
|
|
41
42
|
name;
|
|
42
43
|
providerId;
|
|
43
|
-
type =
|
|
44
|
+
type = import_types.DeviceType.Camera;
|
|
44
45
|
capabilities;
|
|
45
46
|
ctx;
|
|
46
47
|
capabilityMap = /* @__PURE__ */ new Map();
|
|
@@ -56,6 +57,10 @@ var RtspDevice = class {
|
|
|
56
57
|
getMetadata() {
|
|
57
58
|
return { manufacturer: "Generic RTSP" };
|
|
58
59
|
}
|
|
60
|
+
/** Return the camera config for persistence */
|
|
61
|
+
getCameraConfig() {
|
|
62
|
+
return { ...this.config };
|
|
63
|
+
}
|
|
59
64
|
createCamera() {
|
|
60
65
|
const cfg = this.config;
|
|
61
66
|
return {
|
|
@@ -219,25 +224,25 @@ var RtspProvider = class {
|
|
|
219
224
|
name;
|
|
220
225
|
discoveryMode = "manual";
|
|
221
226
|
ctx;
|
|
222
|
-
devices;
|
|
227
|
+
devices = [];
|
|
223
228
|
constructor(config, ctx) {
|
|
224
229
|
this.id = config.id;
|
|
225
230
|
this.name = config.name;
|
|
226
231
|
this.ctx = ctx;
|
|
227
|
-
const built = [];
|
|
228
232
|
for (const cam of config.cameras) {
|
|
229
|
-
|
|
230
|
-
id: `device:${this.id}/${cam.id}`,
|
|
231
|
-
logger: ctx.logger.child(cam.name),
|
|
232
|
-
eventBus: ctx.eventBus,
|
|
233
|
-
storage: ctx.storage,
|
|
234
|
-
config: new ElementConfigStore(`device:${this.id}/${cam.id}`, ctx.storage)
|
|
235
|
-
};
|
|
236
|
-
built.push(new RtspDevice(cam, this.id, deviceCtx));
|
|
233
|
+
this.devices.push(this.buildDevice(cam));
|
|
237
234
|
}
|
|
238
|
-
this.devices = built;
|
|
239
235
|
}
|
|
240
236
|
async start() {
|
|
237
|
+
const persisted = this.ctx.config.get("devices");
|
|
238
|
+
if (persisted && Array.isArray(persisted)) {
|
|
239
|
+
for (const cam of persisted) {
|
|
240
|
+
const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`);
|
|
241
|
+
if (!alreadyExists) {
|
|
242
|
+
this.devices.push(this.buildDevice(cam));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
241
246
|
this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`);
|
|
242
247
|
}
|
|
243
248
|
async stop() {
|
|
@@ -254,17 +259,80 @@ var RtspProvider = class {
|
|
|
254
259
|
}
|
|
255
260
|
getDeviceConfigSchema() {
|
|
256
261
|
return {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
sections: [
|
|
263
|
+
{
|
|
264
|
+
id: "device-info",
|
|
265
|
+
title: "Device Configuration",
|
|
266
|
+
description: "Configure the RTSP camera connection",
|
|
267
|
+
columns: 1,
|
|
268
|
+
fields: [
|
|
269
|
+
{
|
|
270
|
+
type: "text",
|
|
271
|
+
key: "name",
|
|
272
|
+
label: "Device Name",
|
|
273
|
+
placeholder: "e.g. Front Door Camera",
|
|
274
|
+
required: true
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
type: "text",
|
|
278
|
+
key: "snapshotUrl",
|
|
279
|
+
label: "Snapshot URL",
|
|
280
|
+
placeholder: "http://192.168.1.100/snapshot.jpg",
|
|
281
|
+
inputType: "url",
|
|
282
|
+
description: "HTTP URL for fetching JPEG snapshots (optional)"
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
async createDevice(config) {
|
|
290
|
+
const name = String(config.name ?? "");
|
|
291
|
+
if (!name.trim()) {
|
|
292
|
+
throw new Error("Device name is required");
|
|
293
|
+
}
|
|
294
|
+
const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : [];
|
|
295
|
+
const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : void 0;
|
|
296
|
+
const mainStreamUrl = streams[0];
|
|
297
|
+
if (!mainStreamUrl) {
|
|
298
|
+
throw new Error("At least one stream URL is required");
|
|
299
|
+
}
|
|
300
|
+
const deviceId = `rtsp-${Date.now()}`;
|
|
301
|
+
const camConfig = {
|
|
302
|
+
id: deviceId,
|
|
303
|
+
name,
|
|
304
|
+
url: mainStreamUrl,
|
|
305
|
+
subStreamUrl: streams[1],
|
|
306
|
+
snapshotUrl
|
|
262
307
|
};
|
|
308
|
+
const device = this.buildDevice(camConfig);
|
|
309
|
+
this.devices.push(device);
|
|
310
|
+
await this.persistDeviceConfigs();
|
|
311
|
+
this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`);
|
|
312
|
+
return device;
|
|
263
313
|
}
|
|
264
314
|
subscribeLiveEvents(_callback) {
|
|
265
315
|
return () => {
|
|
266
316
|
};
|
|
267
317
|
}
|
|
318
|
+
buildDevice(cam) {
|
|
319
|
+
const deviceCtx = {
|
|
320
|
+
id: `device:${this.id}/${cam.id}`,
|
|
321
|
+
logger: this.ctx.logger.child(cam.name),
|
|
322
|
+
eventBus: this.ctx.eventBus,
|
|
323
|
+
storage: this.ctx.storage,
|
|
324
|
+
config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage)
|
|
325
|
+
};
|
|
326
|
+
return new RtspDevice(cam, this.id, deviceCtx);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Persist all dynamically-created device configs so they survive restarts.
|
|
330
|
+
* Saves the full list of camera configs under the 'devices' key in the provider's config.
|
|
331
|
+
*/
|
|
332
|
+
async persistDeviceConfigs() {
|
|
333
|
+
const configs = this.devices.map((device) => device.getCameraConfig());
|
|
334
|
+
await this.ctx.config.set("devices", configs);
|
|
335
|
+
}
|
|
268
336
|
};
|
|
269
337
|
|
|
270
338
|
// src/addon.ts
|
|
@@ -273,19 +341,16 @@ var RtspProviderAddon = class {
|
|
|
273
341
|
id: "provider-rtsp",
|
|
274
342
|
name: "RTSP Camera Provider",
|
|
275
343
|
version: "0.1.0",
|
|
344
|
+
description: "Connessione diretta a camere via URL RTSP",
|
|
276
345
|
capabilities: ["device-provider"]
|
|
277
346
|
};
|
|
278
347
|
provider = null;
|
|
279
348
|
async initialize(context) {
|
|
280
349
|
const config = context.addonConfig;
|
|
281
|
-
if (!config.cameras || config.cameras.length === 0) {
|
|
282
|
-
context.logger.info("RTSP provider: no cameras configured");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
350
|
const providerConfig = {
|
|
286
351
|
id: config.id ?? "rtsp-default",
|
|
287
352
|
name: config.name ?? "RTSP Cameras",
|
|
288
|
-
cameras: config.cameras
|
|
353
|
+
cameras: config.cameras ?? []
|
|
289
354
|
};
|
|
290
355
|
this.provider = new RtspProvider(providerConfig, {
|
|
291
356
|
id: context.id,
|
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 type {\n IDevice,\n DeviceType,\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 = '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 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} from '@camstack/types'\nimport type { 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: readonly 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 config (immutable after construction)\n const built: RtspDevice[] = []\n for (const cam of config.cameras) {\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: ctx.logger.child(cam.name),\n eventBus: ctx.eventBus,\n storage: ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, ctx.storage),\n }\n built.push(new RtspDevice(cam, this.id, deviceCtx))\n }\n this.devices = built\n }\n\n async start(): Promise<void> {\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() {\n return {\n id: 'string',\n name: 'string',\n url: 'string (rtsp://...)',\n subStreamUrl: 'string (optional)',\n snapshotUrl: 'string (optional)',\n }\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","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 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 RtspProviderConfig\n\n if (!config.cameras || config.cameras.length === 0) {\n context.logger.info('RTSP provider: no cameras configured')\n return\n }\n\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 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":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/rtsp-device.ts
|
|
2
|
+
import { DeviceType } from "@camstack/types";
|
|
2
3
|
var RtspDevice = class {
|
|
3
4
|
constructor(config, providerId, ctx) {
|
|
4
5
|
this.config = config;
|
|
@@ -12,7 +13,7 @@ var RtspDevice = class {
|
|
|
12
13
|
id;
|
|
13
14
|
name;
|
|
14
15
|
providerId;
|
|
15
|
-
type =
|
|
16
|
+
type = DeviceType.Camera;
|
|
16
17
|
capabilities;
|
|
17
18
|
ctx;
|
|
18
19
|
capabilityMap = /* @__PURE__ */ new Map();
|
|
@@ -28,6 +29,10 @@ var RtspDevice = class {
|
|
|
28
29
|
getMetadata() {
|
|
29
30
|
return { manufacturer: "Generic RTSP" };
|
|
30
31
|
}
|
|
32
|
+
/** Return the camera config for persistence */
|
|
33
|
+
getCameraConfig() {
|
|
34
|
+
return { ...this.config };
|
|
35
|
+
}
|
|
31
36
|
createCamera() {
|
|
32
37
|
const cfg = this.config;
|
|
33
38
|
return {
|
|
@@ -191,25 +196,25 @@ var RtspProvider = class {
|
|
|
191
196
|
name;
|
|
192
197
|
discoveryMode = "manual";
|
|
193
198
|
ctx;
|
|
194
|
-
devices;
|
|
199
|
+
devices = [];
|
|
195
200
|
constructor(config, ctx) {
|
|
196
201
|
this.id = config.id;
|
|
197
202
|
this.name = config.name;
|
|
198
203
|
this.ctx = ctx;
|
|
199
|
-
const built = [];
|
|
200
204
|
for (const cam of config.cameras) {
|
|
201
|
-
|
|
202
|
-
id: `device:${this.id}/${cam.id}`,
|
|
203
|
-
logger: ctx.logger.child(cam.name),
|
|
204
|
-
eventBus: ctx.eventBus,
|
|
205
|
-
storage: ctx.storage,
|
|
206
|
-
config: new ElementConfigStore(`device:${this.id}/${cam.id}`, ctx.storage)
|
|
207
|
-
};
|
|
208
|
-
built.push(new RtspDevice(cam, this.id, deviceCtx));
|
|
205
|
+
this.devices.push(this.buildDevice(cam));
|
|
209
206
|
}
|
|
210
|
-
this.devices = built;
|
|
211
207
|
}
|
|
212
208
|
async start() {
|
|
209
|
+
const persisted = this.ctx.config.get("devices");
|
|
210
|
+
if (persisted && Array.isArray(persisted)) {
|
|
211
|
+
for (const cam of persisted) {
|
|
212
|
+
const alreadyExists = this.devices.some((d) => d.id === `${this.id}/${cam.id}`);
|
|
213
|
+
if (!alreadyExists) {
|
|
214
|
+
this.devices.push(this.buildDevice(cam));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
213
218
|
this.ctx.logger.info(`RTSP provider started with ${this.devices.length} cameras`);
|
|
214
219
|
}
|
|
215
220
|
async stop() {
|
|
@@ -226,17 +231,80 @@ var RtspProvider = class {
|
|
|
226
231
|
}
|
|
227
232
|
getDeviceConfigSchema() {
|
|
228
233
|
return {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
+
sections: [
|
|
235
|
+
{
|
|
236
|
+
id: "device-info",
|
|
237
|
+
title: "Device Configuration",
|
|
238
|
+
description: "Configure the RTSP camera connection",
|
|
239
|
+
columns: 1,
|
|
240
|
+
fields: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
key: "name",
|
|
244
|
+
label: "Device Name",
|
|
245
|
+
placeholder: "e.g. Front Door Camera",
|
|
246
|
+
required: true
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
type: "text",
|
|
250
|
+
key: "snapshotUrl",
|
|
251
|
+
label: "Snapshot URL",
|
|
252
|
+
placeholder: "http://192.168.1.100/snapshot.jpg",
|
|
253
|
+
inputType: "url",
|
|
254
|
+
description: "HTTP URL for fetching JPEG snapshots (optional)"
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async createDevice(config) {
|
|
262
|
+
const name = String(config.name ?? "");
|
|
263
|
+
if (!name.trim()) {
|
|
264
|
+
throw new Error("Device name is required");
|
|
265
|
+
}
|
|
266
|
+
const streams = Array.isArray(config.streams) ? config.streams.filter(Boolean).map(String) : [];
|
|
267
|
+
const snapshotUrl = config.snapshotUrl ? String(config.snapshotUrl) : void 0;
|
|
268
|
+
const mainStreamUrl = streams[0];
|
|
269
|
+
if (!mainStreamUrl) {
|
|
270
|
+
throw new Error("At least one stream URL is required");
|
|
271
|
+
}
|
|
272
|
+
const deviceId = `rtsp-${Date.now()}`;
|
|
273
|
+
const camConfig = {
|
|
274
|
+
id: deviceId,
|
|
275
|
+
name,
|
|
276
|
+
url: mainStreamUrl,
|
|
277
|
+
subStreamUrl: streams[1],
|
|
278
|
+
snapshotUrl
|
|
234
279
|
};
|
|
280
|
+
const device = this.buildDevice(camConfig);
|
|
281
|
+
this.devices.push(device);
|
|
282
|
+
await this.persistDeviceConfigs();
|
|
283
|
+
this.ctx.logger.info(`Created RTSP device: ${device.id} (${name})`);
|
|
284
|
+
return device;
|
|
235
285
|
}
|
|
236
286
|
subscribeLiveEvents(_callback) {
|
|
237
287
|
return () => {
|
|
238
288
|
};
|
|
239
289
|
}
|
|
290
|
+
buildDevice(cam) {
|
|
291
|
+
const deviceCtx = {
|
|
292
|
+
id: `device:${this.id}/${cam.id}`,
|
|
293
|
+
logger: this.ctx.logger.child(cam.name),
|
|
294
|
+
eventBus: this.ctx.eventBus,
|
|
295
|
+
storage: this.ctx.storage,
|
|
296
|
+
config: new ElementConfigStore(`device:${this.id}/${cam.id}`, this.ctx.storage)
|
|
297
|
+
};
|
|
298
|
+
return new RtspDevice(cam, this.id, deviceCtx);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Persist all dynamically-created device configs so they survive restarts.
|
|
302
|
+
* Saves the full list of camera configs under the 'devices' key in the provider's config.
|
|
303
|
+
*/
|
|
304
|
+
async persistDeviceConfigs() {
|
|
305
|
+
const configs = this.devices.map((device) => device.getCameraConfig());
|
|
306
|
+
await this.ctx.config.set("devices", configs);
|
|
307
|
+
}
|
|
240
308
|
};
|
|
241
309
|
|
|
242
310
|
// src/addon.ts
|
|
@@ -245,19 +313,16 @@ var RtspProviderAddon = class {
|
|
|
245
313
|
id: "provider-rtsp",
|
|
246
314
|
name: "RTSP Camera Provider",
|
|
247
315
|
version: "0.1.0",
|
|
316
|
+
description: "Connessione diretta a camere via URL RTSP",
|
|
248
317
|
capabilities: ["device-provider"]
|
|
249
318
|
};
|
|
250
319
|
provider = null;
|
|
251
320
|
async initialize(context) {
|
|
252
321
|
const config = context.addonConfig;
|
|
253
|
-
if (!config.cameras || config.cameras.length === 0) {
|
|
254
|
-
context.logger.info("RTSP provider: no cameras configured");
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
322
|
const providerConfig = {
|
|
258
323
|
id: config.id ?? "rtsp-default",
|
|
259
324
|
name: config.name ?? "RTSP Cameras",
|
|
260
|
-
cameras: config.cameras
|
|
325
|
+
cameras: config.cameras ?? []
|
|
261
326
|
};
|
|
262
327
|
this.provider = new RtspProvider(providerConfig, {
|
|
263
328
|
id: context.id,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/rtsp-device.ts","../src/element-config-store.ts","../src/rtsp-provider.ts","../src/addon.ts"],"sourcesContent":["import type {\n IDevice,\n DeviceType,\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 = '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 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} from '@camstack/types'\nimport type { 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: readonly 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 config (immutable after construction)\n const built: RtspDevice[] = []\n for (const cam of config.cameras) {\n const deviceCtx: CamstackContext = {\n id: `device:${this.id}/${cam.id}`,\n logger: ctx.logger.child(cam.name),\n eventBus: ctx.eventBus,\n storage: ctx.storage,\n config: new ElementConfigStore(`device:${this.id}/${cam.id}`, ctx.storage),\n }\n built.push(new RtspDevice(cam, this.id, deviceCtx))\n }\n this.devices = built\n }\n\n async start(): Promise<void> {\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() {\n return {\n id: 'string',\n name: 'string',\n url: 'string (rtsp://...)',\n subStreamUrl: 'string (optional)',\n snapshotUrl: 'string (optional)',\n }\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","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 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 RtspProviderConfig\n\n if (!config.cameras || config.cameras.length === 0) {\n context.logger.info('RTSP provider: no cameras configured')\n return\n }\n\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 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":[]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-rtsp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Generic RTSP camera device provider addon for CamStack",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"camstack",
|
|
7
|
+
"addon",
|
|
8
|
+
"camstack-addon",
|
|
9
|
+
"rtsp",
|
|
10
|
+
"provider",
|
|
11
|
+
"camera",
|
|
12
|
+
"streaming"
|
|
13
|
+
],
|
|
6
14
|
"license": "MIT",
|
|
7
15
|
"repository": {
|
|
8
16
|
"type": "git",
|
|
@@ -12,21 +20,36 @@
|
|
|
12
20
|
"module": "./dist/index.mjs",
|
|
13
21
|
"types": "./dist/index.d.ts",
|
|
14
22
|
"exports": {
|
|
15
|
-
".": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./package.json": "./package.json"
|
|
16
29
|
},
|
|
17
30
|
"camstack": {
|
|
31
|
+
"displayName": "RTSP Provider",
|
|
18
32
|
"addons": [
|
|
19
33
|
{
|
|
20
34
|
"id": "provider-rtsp",
|
|
21
35
|
"entry": "./dist/addon.js",
|
|
22
36
|
"slot": "provider",
|
|
37
|
+
"icon": "assets/icon.svg",
|
|
38
|
+
"color": "#78716c",
|
|
39
|
+
"instanceMode": "unique",
|
|
23
40
|
"capabilities": [
|
|
24
|
-
{
|
|
41
|
+
{
|
|
42
|
+
"name": "device-provider",
|
|
43
|
+
"mode": "collection"
|
|
44
|
+
}
|
|
25
45
|
]
|
|
26
46
|
}
|
|
27
47
|
]
|
|
28
48
|
},
|
|
29
|
-
"files": [
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"assets"
|
|
52
|
+
],
|
|
30
53
|
"scripts": {
|
|
31
54
|
"build": "tsup",
|
|
32
55
|
"dev": "tsup --watch",
|