@camera.ui/sdk 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +2 -2
  2. package/dist/camera/events.js +2 -0
  3. package/dist/camera/index.js +3 -1
  4. package/dist/external.js +7 -0
  5. package/dist/index.d.ts +7132 -4248
  6. package/dist/index.js +3 -11
  7. package/dist/internal/contract-validators.js +21 -0
  8. package/dist/internal/index.d.ts +915 -0
  9. package/dist/internal/index.js +9 -0
  10. package/dist/internal/sensor-triggers.js +2 -0
  11. package/dist/internal/shared-utils.js +86 -0
  12. package/dist/internal/streaming-internal.js +1 -0
  13. package/dist/manager/index.js +1 -1
  14. package/dist/observable/index.js +419 -0
  15. package/dist/plugin/api.js +21 -0
  16. package/dist/plugin/contract.js +101 -114
  17. package/dist/plugin/helper.js +277 -0
  18. package/dist/plugin/index.js +4 -1
  19. package/dist/plugin/interfaces.js +51 -1
  20. package/dist/plugin/notifier.js +23 -0
  21. package/dist/plugin/oauth.js +1 -0
  22. package/dist/sensor/audio.js +103 -81
  23. package/dist/sensor/base.js +350 -318
  24. package/dist/sensor/battery.js +73 -59
  25. package/dist/sensor/classifier.js +117 -0
  26. package/dist/sensor/clip.js +30 -0
  27. package/dist/sensor/contact.js +37 -18
  28. package/dist/sensor/detection.js +4 -0
  29. package/dist/sensor/doorbell.js +52 -38
  30. package/dist/sensor/face.js +71 -86
  31. package/dist/sensor/garage.js +121 -0
  32. package/dist/sensor/humidity.js +52 -0
  33. package/dist/sensor/index.js +17 -11
  34. package/dist/sensor/leak.js +52 -0
  35. package/dist/sensor/licensePlate.js +70 -79
  36. package/dist/sensor/light.js +82 -38
  37. package/dist/sensor/lock.js +99 -0
  38. package/dist/sensor/motion.js +85 -70
  39. package/dist/sensor/object.js +73 -94
  40. package/dist/sensor/occupancy.js +52 -0
  41. package/dist/sensor/ptz.js +114 -100
  42. package/dist/sensor/securitySystem.js +98 -0
  43. package/dist/sensor/siren.js +75 -43
  44. package/dist/sensor/smoke.js +52 -0
  45. package/dist/sensor/spec.js +1 -0
  46. package/dist/sensor/switch.js +72 -0
  47. package/dist/sensor/temperature.js +52 -0
  48. package/dist/storage/index.js +1 -2
  49. package/dist/types.js +1 -0
  50. package/package.json +36 -23
  51. package/CHANGELOG.md +0 -8
  52. package/CONTRIBUTING.md +0 -1
  53. package/LICENSE.md +0 -22
  54. package/dist/sensor/guards.js +0 -133
  55. package/dist/sensor/types.js +0 -46
  56. package/dist/service/base.js +0 -96
  57. package/dist/service/index.js +0 -3
  58. package/tsconfig.node.json +0 -30
  59. /package/dist/camera/{types.js → enums.js} +0 -0
  60. /package/dist/{manager/types.js → camera/frames.js} +0 -0
  61. /package/dist/{plugin/types.js → internal/camera-config-internal.js} +0 -0
  62. /package/dist/{service/services.js → internal/camera-enums.js} +0 -0
  63. /package/dist/{service/types.js → internal/camera-wire.js} +0 -0
  64. /package/dist/{storage/schema.js → internal/manager-rpc.js} +0 -0
  65. /package/dist/{storage/storages.js → internal/sensor-rpc.js} +0 -0
@@ -1,66 +1,110 @@
1
- import { Sensor } from './base.js';
2
- import { SensorCategory, SensorType } from './types.js';
3
- /**
4
- * Light Capability enum
5
- * Describes what Light features this sensor supports
6
- * Note: "on" property is always available (standard)
7
- */
1
+ import { Sensor, SensorType, SensorCategory } from './base.js';
2
+ /** Optional capabilities for light controls */
8
3
  export var LightCapability;
9
4
  (function (LightCapability) {
10
- /** Supports brightness control */
5
+ /** Light supports brightness adjustment (0–100) */
11
6
  LightCapability["Brightness"] = "brightness";
12
7
  })(LightCapability || (LightCapability = {}));
13
8
  /**
14
- * Light control property keys
9
+ * Properties for light controls
10
+ *
11
+ * @internal
15
12
  */
16
13
  export var LightProperty;
17
14
  (function (LightProperty) {
15
+ /** Whether the light is on */
18
16
  LightProperty["On"] = "on";
17
+ /** Brightness level (0–100) */
19
18
  LightProperty["Brightness"] = "brightness";
20
19
  })(LightProperty || (LightProperty = {}));
21
20
  /**
22
- * Light Control
21
+ * Light control sensor. Override `setOn()`/`setOff()` to drive your hardware,
22
+ * then `await super.setOn()` / `await super.setOff()` to sync the SDK state.
23
23
  *
24
- * Bidirectional control for camera spotlight/floodlight.
25
- * Properties can be set directly: `light.on = true`
24
+ * Plugins that have no hardware-action use case can leave the methods unoverridden —
25
+ * the base implementation just updates the state.
26
+ *
27
+ * For hardware-pushed updates (someone manually flipped the switch), call
28
+ * `super.setOn()` / `super.setOff()` from your event handler — that bypasses
29
+ * any plugin override and only syncs state.
26
30
  */
27
31
  export class LightControl extends Sensor {
28
32
  type = SensorType.Light;
29
33
  category = SensorCategory.Control;
30
- name;
31
34
  constructor(name = 'Light') {
32
- super();
33
- this.name = name;
34
- // Initialize defaults
35
- this.props.on = false;
36
- this.props.brightness = 100;
35
+ super(name);
36
+ this._writeState({
37
+ [LightProperty.On]: false,
38
+ [LightProperty.Brightness]: 100,
39
+ });
37
40
  }
38
- /** Whether light is on */
39
41
  get on() {
40
- return this.rawProps.on;
41
- }
42
- /** Turn light on/off */
43
- set on(value) {
44
- this.props.on = value;
42
+ return this.props.on;
45
43
  }
46
- /** Brightness level (0-100) */
47
44
  get brightness() {
48
- return this.rawProps.brightness;
45
+ return this.props.brightness;
49
46
  }
50
- /** Set brightness level (0-100) */
51
- set brightness(value) {
52
- this.props.brightness = Math.max(0, Math.min(100, value));
47
+ /**
48
+ * Turn the light on. Override to drive hardware and call `await super.setOn()`
49
+ * after the hardware call succeeds to sync the SDK state.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * await light.setOn();
54
+ * ```
55
+ */
56
+ async setOn() {
57
+ this._writeState({ [LightProperty.On]: true });
53
58
  }
54
- /** Turn light on */
55
- turnOn() {
56
- this.props.on = true;
59
+ /**
60
+ * Turn the light off. Override to drive hardware and call `await super.setOff()`
61
+ * after the hardware call succeeds to sync the SDK state.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * await light.setOff();
66
+ * ```
67
+ */
68
+ async setOff() {
69
+ this._writeState({ [LightProperty.On]: false });
57
70
  }
58
- /** Turn light off */
59
- turnOff() {
60
- this.props.on = false;
71
+ /**
72
+ * Set brightness. Override to drive hardware and call `await super.setBrightness(value)`
73
+ * after the hardware call succeeds. The default implementation clamps the value to [0, 100].
74
+ *
75
+ * @param value - Brightness level in the range 0–100.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * await light.setBrightness(75);
80
+ * ```
81
+ */
82
+ async setBrightness(value) {
83
+ this._writeState({ [LightProperty.Brightness]: Math.max(0, Math.min(100, value)) });
61
84
  }
62
- /** Toggle light state */
63
- toggle() {
64
- this.props.on = !this.on;
85
+ /**
86
+ * Cross-process consumer entry point. Dispatches writable properties
87
+ * to semantic methods so plugin overrides (hardware actions) are honored.
88
+ * Unknown properties are ignored — only `On` and `Brightness` are externally writable.
89
+ *
90
+ * @param property - Property name to write.
91
+ *
92
+ * @param value - New value for the property.
93
+ *
94
+ * @internal
95
+ */
96
+ async updateValue(property, value) {
97
+ switch (property) {
98
+ case LightProperty.On:
99
+ if (value)
100
+ await this.setOn();
101
+ else
102
+ await this.setOff();
103
+ return;
104
+ case LightProperty.Brightness:
105
+ await this.setBrightness(value);
106
+ return;
107
+ }
108
+ // Unknown / non-writable property — ignored.
65
109
  }
66
110
  }
@@ -0,0 +1,99 @@
1
+ import { Sensor, SensorType, SensorCategory } from './base.js';
2
+ /** Lock states (HomeKit-compatible values) */
3
+ export var LockState;
4
+ (function (LockState) {
5
+ LockState[LockState["Secured"] = 0] = "Secured";
6
+ LockState[LockState["Unsecured"] = 1] = "Unsecured";
7
+ LockState[LockState["Unknown"] = 2] = "Unknown";
8
+ })(LockState || (LockState = {}));
9
+ /**
10
+ * Properties for lock controls
11
+ *
12
+ * @internal
13
+ */
14
+ export var LockProperty;
15
+ (function (LockProperty) {
16
+ /** The actual current state of the lock */
17
+ LockProperty["CurrentState"] = "currentState";
18
+ /** The desired target state (set by user, transitions to currentState) */
19
+ LockProperty["TargetState"] = "targetState";
20
+ })(LockProperty || (LockProperty = {}));
21
+ /**
22
+ * Lock control. Override `setTargetState()` to drive hardware and call
23
+ * `await super.setTargetState(value)` once the hardware confirms — the base
24
+ * implementation updates both `targetState` and `currentState` to the new value.
25
+ *
26
+ * For asymmetric flows (long-running unlock with intermediate state) override
27
+ * `setTargetState` and write `currentState` separately when transitions complete.
28
+ */
29
+ export class LockControl extends Sensor {
30
+ type = SensorType.Lock;
31
+ category = SensorCategory.Control;
32
+ constructor(name = 'Lock') {
33
+ super(name);
34
+ this._writeState({
35
+ [LockProperty.CurrentState]: LockState.Secured,
36
+ [LockProperty.TargetState]: LockState.Secured,
37
+ });
38
+ }
39
+ get currentState() {
40
+ return this.props.currentState;
41
+ }
42
+ get targetState() {
43
+ return this.props.targetState;
44
+ }
45
+ /**
46
+ * Set the target state. Override to drive hardware and call
47
+ * `await super.setTargetState(value)` after success — the base implementation
48
+ * syncs both `targetState` and `currentState` to the new value.
49
+ *
50
+ * @param value - Desired lock state from the {@link LockState} enum.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import { LockState } from '@camera.ui/sdk';
55
+ * await lock.setTargetState(LockState.Secured);
56
+ * ```
57
+ */
58
+ async setTargetState(value) {
59
+ this._writeState({
60
+ [LockProperty.TargetState]: value,
61
+ [LockProperty.CurrentState]: value,
62
+ });
63
+ }
64
+ /**
65
+ * Publish the actual lock state. Use this to drive transitions where the
66
+ * physical state diverges from the user-requested target — e.g. motorized
67
+ * smart locks that take time to rotate (publish `Unknown` while moving),
68
+ * or hardware reporting an out-of-band state change. Read-only from
69
+ * cross-process consumers (`updateValue` ignores it).
70
+ *
71
+ * @param value - Current physical lock state from the {@link LockState} enum.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * import { LockState } from '@camera.ui/sdk';
76
+ * lock.setCurrentState(LockState.Unknown);
77
+ * ```
78
+ */
79
+ setCurrentState(value) {
80
+ this._writeState({ [LockProperty.CurrentState]: value });
81
+ }
82
+ /**
83
+ * Cross-process consumer entry point. Dispatches writable properties
84
+ * to semantic methods so plugin overrides (hardware actions) are honored.
85
+ * `currentState` is observed-only and not externally writable; only `targetState` may be set.
86
+ *
87
+ * @param property - Property name to write.
88
+ *
89
+ * @param value - New value for the property.
90
+ *
91
+ * @internal
92
+ */
93
+ async updateValue(property, value) {
94
+ if (property === LockProperty.TargetState) {
95
+ await this.setTargetState(value);
96
+ }
97
+ // Unknown / non-writable property (incl. currentState) — ignored.
98
+ }
99
+ }
@@ -1,103 +1,118 @@
1
- import { Sensor } from './base.js';
2
- import { SensorCategory, SensorType } from './types.js';
1
+ import { Sensor, SensorType, SensorCategory } from './base.js';
3
2
  /**
4
- * Motion sensor property keys
3
+ * Property names of a motion sensor.
4
+ *
5
+ * @internal
5
6
  */
6
7
  export var MotionProperty;
7
8
  (function (MotionProperty) {
9
+ /** Whether motion is currently detected. */
8
10
  MotionProperty["Detected"] = "detected";
11
+ /** List of detection results with bounding boxes. */
9
12
  MotionProperty["Detections"] = "detections";
13
+ /** When true, detection updates are suppressed (set by the backend dwell logic). */
14
+ MotionProperty["Blocked"] = "blocked";
15
+ /** Timestamp in milliseconds of the last detection trigger, set by the backend. */
16
+ MotionProperty["LastTriggered"] = "lastTriggered";
10
17
  })(MotionProperty || (MotionProperty = {}));
11
18
  /**
12
- * Motion Sensor
13
- *
14
- * Base class for external motion detection (Ring, ONVIF events, SMTP, etc.)
15
- * Properties can be set directly: `sensor.detected = true`
19
+ * Motion sensor that reports motion state and detection results.
16
20
  *
17
- * For frame-based motion detection, use `MotionDetectorSensor` instead.
21
+ * Plugin authors call `reportDetections(list)` to push detection results.
22
+ * `detected` is auto-derived from the detection list. `blocked` is read-only
23
+ * and is set by the backend (dwell logic) — `reportDetections()` becomes a
24
+ * no-op while the sensor is blocked.
18
25
  */
19
26
  export class MotionSensor extends Sensor {
20
27
  type = SensorType.Motion;
21
28
  category = SensorCategory.Sensor;
22
- name;
23
- /**
24
- * External motion sensors don't require video frames.
25
- * They receive motion events from external sources (SMTP, ONVIF, Ring API, etc.)
26
- */
27
29
  _requiresFrames = false;
28
30
  constructor(name = 'Motion Sensor') {
29
- super();
30
- this.name = name;
31
- // Initialize defaults
32
- this.props.detected = false;
33
- this.props.detections = [];
31
+ super(name);
32
+ this._writeState({
33
+ [MotionProperty.Detected]: false,
34
+ [MotionProperty.Detections]: [],
35
+ [MotionProperty.Blocked]: false,
36
+ });
34
37
  }
35
- /** Whether motion is currently detected */
38
+ /** Whether motion is currently detected. */
36
39
  get detected() {
37
- return this.rawProps.detected;
38
- }
39
- /** Set motion detected state */
40
- set detected(value) {
41
- this.props.detected = value;
40
+ return this.props.detected;
42
41
  }
43
- /** Current motion detections */
42
+ /** Current detection list. */
44
43
  get detections() {
45
- return this.rawProps.detections;
44
+ return this.props.detections;
46
45
  }
47
- /** Set motion detections */
48
- set detections(value) {
49
- this.props.detections = value;
46
+ /** Whether the sensor is currently blocked. Read-only — set by the backend dwell logic, not by plugin code. */
47
+ get blocked() {
48
+ return this.props.blocked;
50
49
  }
51
50
  /**
52
- * Update both detected state and detections at once
51
+ * Report a motion detection result.
52
+ *
53
+ * - `reportDetections(true)` — motion detected without bbox (e.g. Ring camera).
54
+ * The SDK synthesizes a single full-frame `'motion'` detection.
55
+ * - `reportDetections(true, [...])` — motion detected with explicit detections.
56
+ * - `reportDetections(false)` — no motion (clears detections).
53
57
  *
54
- * @param detected - Whether motion is detected
58
+ * No-op while the sensor is blocked by backend dwell logic.
59
+ *
60
+ * @param detected - Whether motion is currently detected.
61
+ *
62
+ * @param detections - Optional explicit detections produced for this frame.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import type { Detection } from '@camera.ui/sdk';
67
+ * sensor.reportDetections(true, [
68
+ * { label: 'motion', confidence: 0.85, box: { x: 0.1, y: 0.2, width: 0.3, height: 0.4 } } satisfies Detection,
69
+ * ]);
70
+ * sensor.reportDetections(false);
71
+ * ```
72
+ */
73
+ reportDetections(detected, detections) {
74
+ if (this.blocked)
75
+ return;
76
+ const list = this._normalizeReportedDetections(detected, detections, 'motion');
77
+ this._writeState({
78
+ [MotionProperty.Detected]: detected,
79
+ [MotionProperty.Detections]: list,
80
+ });
81
+ }
82
+ /**
83
+ * Explicitly clear motion state (detected = false, detections = []).
55
84
  *
56
- * @param detections - Array of motion detections
85
+ * @example
86
+ * ```ts
87
+ * sensor.clearDetections();
88
+ * ```
57
89
  */
58
- setMotion(detected, detections = []) {
59
- this.props.detected = detected;
60
- this.props.detections = detections;
90
+ clearDetections() {
91
+ this.reportDetections(false);
61
92
  }
62
- /** Clear motion state */
63
- clearMotion() {
64
- this.props.detected = false;
65
- this.props.detections = [];
93
+ /**
94
+ * Read-only sensor: external writes are ignored. State is reported via `reportDetections`.
95
+ *
96
+ * Called by the cross-process plugin host when a generic property write is received.
97
+ * Motion sensors have no externally writable properties, so the parameters are
98
+ * unused (underscore-prefixed) and the call is a no-op.
99
+ *
100
+ * @param _property - Unused — motion sensors expose no writable properties.
101
+ *
102
+ * @param _value - Unused — motion sensors expose no writable properties.
103
+ *
104
+ * @internal
105
+ */
106
+ updateValue(_property, _value) {
107
+ // No-op — motion state is reported by the plugin, not set externally.
66
108
  }
67
109
  }
68
110
  /**
69
- * Motion Detector Sensor (Active Detection)
70
- *
71
- * Use this class for frame-based motion detection (rust-motion, OpenCV, etc.)
72
- * The `inputProperties` getter specifies the required input format.
73
- *
74
- * For external motion events (Ring, ONVIF, SMTP), use the base `MotionSensor` class instead.
75
- *
76
- * @example
77
- * ```typescript
78
- * class RustMotionDetector extends MotionDetectorSensor {
79
- * get inputProperties(): VideoInputProperties {
80
- * return { width: 320, height: 240, format: 'gray' };
81
- * }
82
- *
83
- * async detectMotion(frame: VideoFrameData): Promise<MotionResult> {
84
- * const result = await this.rustDetector.process(frame.data);
85
- * return {
86
- * detected: result.motionDetected,
87
- * detections: result.regions.map(r => ({
88
- * label: 'motion',
89
- * confidence: r.intensity,
90
- * box: r.boundingBox,
91
- * })),
92
- * };
93
- * }
94
- * }
95
- * ```
111
+ * Motion detector that receives video frames from the backend pipeline.
112
+ * Extend this class and implement {@link detectMotion} to analyze frames
113
+ * for motion. The backend calls `detectMotion()` at the configured frame
114
+ * interval and applies the returned result to the sensor.
96
115
  */
97
116
  export class MotionDetectorSensor extends MotionSensor {
98
- /**
99
- * Indicates this sensor requires video frames for detection.
100
- * Used by the backend to determine streaming requirements.
101
- */
102
117
  _requiresFrames = true;
103
118
  }
@@ -1,137 +1,116 @@
1
- import { Sensor } from './base.js';
2
- import { SensorCategory, SensorType } from './types.js';
1
+ import { Sensor, SensorType, SensorCategory } from './base.js';
3
2
  /**
4
- * Object sensor property keys
3
+ * Property names of an object detection sensor.
4
+ *
5
+ * @internal
5
6
  */
6
7
  export var ObjectProperty;
7
8
  (function (ObjectProperty) {
9
+ /** Whether any object is currently detected. */
8
10
  ObjectProperty["Detected"] = "detected";
11
+ /** List of detected objects with labels and bounding boxes. */
9
12
  ObjectProperty["Detections"] = "detections";
13
+ /** Unique labels of the current detections (auto-derived when reporting detections). */
10
14
  ObjectProperty["Labels"] = "labels";
11
15
  })(ObjectProperty || (ObjectProperty = {}));
12
16
  /**
13
- * Object Sensor
14
- *
15
- * Base class for external object detection (Ring, ONVIF events, Eufy, etc.)
16
- * Properties can be set directly: `sensor.detected = true`
17
+ * Object detection sensor that reports detected objects (person, vehicle, animal, etc.).
17
18
  *
18
- * For frame-based object detection, use `ObjectDetectorSensor` instead.
19
+ * Plugin authors call `reportDetections(list)` to push detection results.
20
+ * `detected` and `labels` are auto-derived from the detection list.
19
21
  */
20
22
  export class ObjectSensor extends Sensor {
21
23
  type = SensorType.Object;
22
24
  category = SensorCategory.Sensor;
23
- name;
24
- /**
25
- * External object sensors don't require video frames.
26
- * They receive detection events from external sources (APIs, ONVIF, etc.)
27
- */
28
25
  _requiresFrames = false;
29
26
  constructor(name = 'Object Sensor') {
30
- super();
31
- this.name = name;
32
- // Initialize defaults
33
- this.props.detected = false;
34
- this.props.detections = [];
35
- this.props.labels = [];
27
+ super(name);
28
+ this._writeState({
29
+ [ObjectProperty.Detected]: false,
30
+ [ObjectProperty.Detections]: [],
31
+ [ObjectProperty.Labels]: [],
32
+ });
36
33
  }
37
- /** Whether objects are currently detected */
34
+ /** Whether any object is currently detected. */
38
35
  get detected() {
39
- return this.rawProps.detected;
40
- }
41
- /** Set object detected state */
42
- set detected(value) {
43
- this.props.detected = value;
36
+ return this.props.detected;
44
37
  }
45
- /** Current object detections */
38
+ /** Current detection list. */
46
39
  get detections() {
47
- return this.rawProps.detections;
40
+ return this.props.detections;
48
41
  }
49
- /** Set object detections */
50
- set detections(value) {
51
- this.props.detections = value;
52
- }
53
- /** Labels currently being detected */
42
+ /** Unique labels of the current detections. */
54
43
  get labels() {
55
- return this.rawProps.labels;
56
- }
57
- /** Set detected labels */
58
- set labels(value) {
59
- this.props.labels = value;
44
+ return this.props.labels;
60
45
  }
61
46
  /**
62
- * Update object detection state
47
+ * Report detected objects. Auto-derives `detected` and `labels` from the list.
48
+ *
49
+ * - `reportDetections(true)` — something detected without specific data. The SDK
50
+ * synthesizes a single full-frame `'motion'` detection as a generic fallback.
51
+ * - `reportDetections(true, [...])` — explicit detections (typical case).
52
+ * - `reportDetections(false)` — clear.
63
53
  *
64
- * @param detected - Whether objects are detected
54
+ * @param detected - Whether any object is currently detected.
65
55
  *
66
- * @param detections - Array of object detections
56
+ * @param detections - Optional explicit object detections (with optional tracking metadata).
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import type { TrackedDetection } from '@camera.ui/sdk';
61
+ * sensor.reportDetections(true, [
62
+ * {
63
+ * label: 'person',
64
+ * confidence: 0.92,
65
+ * box: { x: 0.1, y: 0.2, width: 0.3, height: 0.4 },
66
+ * } satisfies TrackedDetection,
67
+ * ]);
68
+ * sensor.reportDetections(false);
69
+ * ```
67
70
  */
68
- setObjects(detected, detections = []) {
69
- this.props.detected = detected;
70
- this.props.detections = detections;
71
- // Extract unique labels from detections
72
- const labelSet = new Set(detections.map((d) => d.label));
73
- this.props.labels = Array.from(labelSet);
74
- }
75
- /** Clear object detection state */
76
- clearObjects() {
77
- this.props.detected = false;
78
- this.props.detections = [];
79
- this.props.labels = [];
71
+ reportDetections(detected, detections) {
72
+ const list = this._normalizeReportedDetections(detected, detections, 'motion');
73
+ const labels = Array.from(new Set(list.map((d) => d.label)));
74
+ this._writeState({
75
+ [ObjectProperty.Detected]: detected,
76
+ [ObjectProperty.Detections]: list,
77
+ [ObjectProperty.Labels]: labels,
78
+ });
80
79
  }
81
80
  /**
82
- * Get detections filtered by label
81
+ * Explicitly clear detection state (detected = false, detections = [], labels = []).
83
82
  *
84
- * @param label - The label to filter detections by
85
- *
86
- * @returns Array of detections matching the label
83
+ * @example
84
+ * ```ts
85
+ * sensor.clearDetections();
86
+ * ```
87
87
  */
88
- getDetectionsByLabel(label) {
89
- return this.detections.filter((d) => d.label === label);
88
+ clearDetections() {
89
+ this.reportDetections(false);
90
90
  }
91
91
  /**
92
- * Check if specific label is detected
92
+ * Read-only sensor: external writes are ignored. State is reported via `reportDetections`.
93
+ *
94
+ * Called by the cross-process plugin host when a generic property write is received.
95
+ * Object detection sensors have no externally writable properties, so the parameters are
96
+ * unused (underscore-prefixed) and the call is a no-op.
97
+ *
98
+ * @param _property - Unused — object detection sensors expose no writable properties.
93
99
  *
94
- * @param label - Label to check
100
+ * @param _value - Unused object detection sensors expose no writable properties.
95
101
  *
96
- * @returns True if label is detected, false otherwise
102
+ * @internal
97
103
  */
98
- hasLabel(label) {
99
- return this.labels.includes(label);
104
+ updateValue(_property, _value) {
105
+ // No-op — object detection state is reported by the plugin, not set externally.
100
106
  }
101
107
  }
102
108
  /**
103
- * Object Detector Sensor (Active Detection)
104
- *
105
- * Use this class for frame-based object detection (TensorFlow, YOLO, etc.)
106
- * The `inputProperties` getter specifies the required input format.
107
- *
108
- * For external object events, use the base `ObjectSensor` class instead.
109
- *
110
- * @example
111
- * ```typescript
112
- * class TensorFlowObjectDetector extends ObjectDetectorSensor {
113
- * get inputProperties(): VideoInputProperties {
114
- * return { width: 640, height: 640, format: 'rgb' };
115
- * }
116
- *
117
- * async detectObjects(frame: VideoFrameData): Promise<ObjectResult> {
118
- * const predictions = await this.model.detect(frame.data);
119
- * return {
120
- * detected: predictions.length > 0,
121
- * detections: predictions.map(p => ({
122
- * label: p.class,
123
- * confidence: p.score,
124
- * box: p.bbox,
125
- * })),
126
- * };
127
- * }
128
- * }
129
- * ```
109
+ * Object detector that receives video frames from the backend pipeline.
110
+ * Extend this class and implement {@link detectObjects} and {@link modelSpec}.
111
+ * The backend scales frames to match `modelSpec.input` dimensions before
112
+ * each call.
130
113
  */
131
114
  export class ObjectDetectorSensor extends ObjectSensor {
132
- /**
133
- * Indicates this sensor requires video frames for detection.
134
- * Used by the backend to determine streaming requirements.
135
- */
136
115
  _requiresFrames = true;
137
116
  }