import { API_EVENT, BasePlugin } from '@camera.ui/sdk'; import { ExampleLightControl, ExampleMotionSensor } from './sensor.js'; import type { CameraDevice, DeviceStorage, LightControl, LoggerService, MotionSensor, PluginAPI } from '@camera.ui/sdk'; /** * Sample Plugin * * Demonstrates the camera.ui plugin architecture with sensors: * - MotionSensor: External motion events (webhooks, ONVIF, etc.) * - LightControl: Controllable light with on/brightness * * Sensors are entities of their own. There are two ways to register them: * - camera.addSensor(sensor): the sensor belongs to this camera's hardware * (spotlight, siren, battery, ...). The assignment is locked, users cannot * re-assign it. * - api.sensorManager.addSensor(sensor): standalone device (smart plug, hub, * imported smart-home device). The user assigns it to cameras in the UI. * * The contract (provides/consumes) is defined in contract.ts. */ export default class SamplePlugin extends BasePlugin { // Maps to track cameras and sensors private cameras = new Map(); private motionSensors = new Map(); private lightControls = new Map(); constructor(logger: LoggerService, api: PluginAPI, storage: DeviceStorage) { super(logger, api, storage); // Register lifecycle event handlers this.api.on(API_EVENT.FINISH_LAUNCHING, this.onFinishLaunching.bind(this)); this.api.on(API_EVENT.SHUTDOWN, this.onShutdown.bind(this)); } /** * Configure cameras at startup. * Called for cameras already assigned to this plugin. */ public async configureCameras(cameras: CameraDevice[]): Promise { for (const camera of cameras) { await this.setupCamera(camera); } } /** * Called when a camera is selected for this plugin at runtime. */ public async onCameraAdded(camera: CameraDevice): Promise { this.logger.log('Camera selected:', camera.name); await this.setupCamera(camera); } /** * Called when a camera is deselected from this plugin. */ public async onCameraReleased(cameraId: string): Promise { const camera = this.cameras.get(cameraId); if (!camera) return; this.logger.log('Camera deselected:', camera.name); // Remove sensors const motion = this.motionSensors.get(cameraId); if (motion) { await camera.removeSensor(motion.id); this.motionSensors.delete(cameraId); } const light = this.lightControls.get(cameraId); if (light) { await camera.removeSensor(light.id); this.lightControls.delete(cameraId); } this.cameras.delete(cameraId); } /** * Set up sensors for a camera. */ private async setupCamera(camera: CameraDevice): Promise { if (this.cameras.has(camera.id)) return; this.cameras.set(camera.id, camera); // Create motion sensor const motion = new ExampleMotionSensor(`Motion - ${camera.name}`); this.motionSensors.set(camera.id, motion); await camera.addSensor(motion); // Create light control const light = new ExampleLightControl(camera, `Light - ${camera.name}`); this.lightControls.set(camera.id, light); await camera.addSensor(light); this.logger.log(`Sensors registered for ${camera.name}`); // Example: Trigger motion after 5 seconds (for testing) // setTimeout(() => motion.trigger(), 5000); } /** * Called when the plugin has finished launching. */ private onFinishLaunching(): void { this.logger.log('Plugin started'); // Standalone sensors (not part of a camera's hardware) go through the // sensor manager. Pass a nativeId so the host can reconcile the sensor // across restarts: // // const plug = new SwitchControl('Smart Plug', { nativeId: 'plug-1' }); // await this.api.sensorManager.addSensor(plug); } /** * Called when camera.ui is shutting down. */ private async onShutdown(): Promise { this.logger.log('Shutting down plugin'); // Cleanup all sensors this.motionSensors.clear(); this.lightControls.clear(); this.cameras.clear(); } }