@camera.ui/cli 0.0.48 → 0.0.50
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/LICENSE.md +1 -1
- package/README.md +10 -1
- package/dist/commands/artifacts.d.ts +9 -0
- package/dist/commands/artifacts.js +107 -0
- package/dist/commands/artifacts.js.map +1 -0
- package/dist/commands/bundle.d.ts +19 -1
- package/dist/commands/bundle.js +368 -56
- package/dist/commands/bundle.js.map +1 -1
- package/dist/commands/create.js +140 -93
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/publish.js +77 -36
- package/dist/commands/publish.js.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +21 -5
- package/dist/utils/banners.d.ts +3 -1
- package/dist/utils/banners.js +15 -11
- package/dist/utils/banners.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -24
- package/dist/utils/logger.js +57 -83
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/parser.d.ts +11 -4
- package/dist/utils/parser.js +27 -186
- package/dist/utils/parser.js.map +1 -1
- package/dist/utils/templates.d.ts +13 -1
- package/dist/utils/templates.js +148 -68
- package/dist/utils/templates.js.map +1 -1
- package/dist/utils/utils.js +13 -11
- package/dist/utils/utils.js.map +1 -1
- package/dist/utils/versions.js +1 -2
- package/dist/utils/versions.js.map +1 -1
- package/package.json +29 -43
- package/templates/base/README.md +2 -2
- package/templates/base/SECURITY.md +1 -1
- package/templates/base/package.json +2 -3
- package/templates/base/updates.config.js +1 -1
- package/templates/go/go.mod +5 -0
- package/templates/go/postinstall.js +35 -0
- package/templates/go/src/main.go +51 -0
- package/templates/python/requirements.txt +1 -1
- package/templates/python/ruff.toml +2 -2
- package/templates/python/src/main.py +299 -28
- package/templates/typescript/contract.ts +36 -0
- package/templates/typescript/eslint.config.js +13 -3
- package/templates/typescript/package.eslint.json +2 -2
- package/templates/typescript/package.json +4 -4
- package/templates/typescript/src/index.ts +96 -27
- package/templates/typescript/src/sensor.ts +207 -0
- package/CHANGELOG.md +0 -8
- package/CONTRIBUTING.md +0 -1
- package/scripts/chmod.js +0 -17
- package/templates/base/cameraui.config.js +0 -10
- package/templates/base/eslint.config.js +0 -64
- package/templates/base/src/index.js +0 -41
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { ClassifierDetectorSensor, LightControl, MotionSensor } from '@camera.ui/sdk';
|
|
2
|
+
|
|
3
|
+
import type { CameraDevice, ClassifierResult, Detection, JsonSchema, ModelSpec, VideoFrameData } from '@camera.ui/sdk';
|
|
4
|
+
|
|
5
|
+
// ============ MOTION SENSOR (External Events) ============
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Example Motion Sensor
|
|
9
|
+
*
|
|
10
|
+
* Use this for external motion sources:
|
|
11
|
+
* - ONVIF camera events
|
|
12
|
+
* - SMTP notifications
|
|
13
|
+
* - Webhook triggers
|
|
14
|
+
* - API polling
|
|
15
|
+
*
|
|
16
|
+
* For frame-based detection, extend MotionDetectorSensor instead.
|
|
17
|
+
*/
|
|
18
|
+
export class ExampleMotionSensor extends MotionSensor {
|
|
19
|
+
constructor(name: string) {
|
|
20
|
+
super(name);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Trigger motion from external event
|
|
25
|
+
*/
|
|
26
|
+
trigger(detections: Detection[] = []): void {
|
|
27
|
+
this.detected = true;
|
|
28
|
+
this.detections = detections;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Clear motion state
|
|
33
|
+
*/
|
|
34
|
+
reset(): void {
|
|
35
|
+
this.detected = false;
|
|
36
|
+
this.detections = [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============ LIGHT CONTROL ============
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Example Light Control
|
|
44
|
+
*
|
|
45
|
+
* Bidirectional control sensor - consumers can read and write state.
|
|
46
|
+
* Implements setOn/setBrightness to handle state changes.
|
|
47
|
+
*/
|
|
48
|
+
export class ExampleLightControl extends LightControl {
|
|
49
|
+
private cameraDevice: CameraDevice;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Storage schema for per-sensor configuration
|
|
53
|
+
* These settings are persisted and shown in the UI.
|
|
54
|
+
*/
|
|
55
|
+
schema: JsonSchema[] = [
|
|
56
|
+
{
|
|
57
|
+
type: 'number',
|
|
58
|
+
key: 'defaultBrightness',
|
|
59
|
+
title: 'Default Brightness',
|
|
60
|
+
description: 'Default brightness level (0-100)',
|
|
61
|
+
defaultValue: 100,
|
|
62
|
+
minimum: 0,
|
|
63
|
+
maximum: 100,
|
|
64
|
+
store: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
key: 'autoOff',
|
|
69
|
+
title: 'Auto-Off',
|
|
70
|
+
description: 'Automatically turn off after timeout',
|
|
71
|
+
defaultValue: false,
|
|
72
|
+
store: true,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
constructor(camera: CameraDevice, name = 'Light') {
|
|
77
|
+
super(name);
|
|
78
|
+
this.cameraDevice = camera;
|
|
79
|
+
|
|
80
|
+
// Initialize state
|
|
81
|
+
this.on = false;
|
|
82
|
+
this.brightness = 100;
|
|
83
|
+
|
|
84
|
+
// Log state changes
|
|
85
|
+
this.onPropertyChanged.subscribe(({ property, value }) => {
|
|
86
|
+
this.cameraDevice.logger.debug(`${this.name}: ${property} = ${value}`);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Called when consumer sets 'on' property
|
|
92
|
+
*/
|
|
93
|
+
async setOn(value: boolean): Promise<void> {
|
|
94
|
+
this.cameraDevice.logger.log(`Light turned ${value ? 'ON' : 'OFF'}`);
|
|
95
|
+
this.on = value;
|
|
96
|
+
|
|
97
|
+
// Apply default brightness when turning on
|
|
98
|
+
if (value && this.storage) {
|
|
99
|
+
const defaultBrightness = this.storage.values.defaultBrightness ?? 100;
|
|
100
|
+
this.brightness = defaultBrightness;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Called when consumer sets 'brightness' property
|
|
106
|
+
*/
|
|
107
|
+
async setBrightness(value: number): Promise<void> {
|
|
108
|
+
this.cameraDevice.logger.log(`Light brightness: ${value}%`);
|
|
109
|
+
this.brightness = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============ CLASSIFIER (Multi-Provider Example) ============
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Example Classifier Sensor
|
|
117
|
+
*
|
|
118
|
+
* Multi-provider sensor: Multiple classifiers can be registered per camera.
|
|
119
|
+
* Example use cases:
|
|
120
|
+
* - Bird species classifier (triggers on 'bird' from object detection)
|
|
121
|
+
* - Dog breed classifier (triggers on 'dog')
|
|
122
|
+
* - Plant species classifier
|
|
123
|
+
*
|
|
124
|
+
* The DetectionCoordinator calls detectClassifications() when triggerLabels are detected.
|
|
125
|
+
*/
|
|
126
|
+
export class ExampleClassifier extends ClassifierDetectorSensor {
|
|
127
|
+
private cameraDevice: CameraDevice;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Schema for classifier configuration
|
|
131
|
+
*/
|
|
132
|
+
schema: JsonSchema[] = [
|
|
133
|
+
{
|
|
134
|
+
type: 'number',
|
|
135
|
+
key: 'confidenceThreshold',
|
|
136
|
+
title: 'Confidence Threshold',
|
|
137
|
+
description: 'Minimum confidence for classifications (0-1)',
|
|
138
|
+
defaultValue: 0.5,
|
|
139
|
+
minimum: 0.1,
|
|
140
|
+
maximum: 1.0,
|
|
141
|
+
step: 0.05,
|
|
142
|
+
store: true,
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
constructor(camera: CameraDevice, name = 'Classifier') {
|
|
147
|
+
super(name);
|
|
148
|
+
this.cameraDevice = camera;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Model specification
|
|
153
|
+
*
|
|
154
|
+
* - input: Frame size and format expected by the model
|
|
155
|
+
* - outputLabels: Labels this classifier can output
|
|
156
|
+
* - triggerLabels: Object labels that trigger classification
|
|
157
|
+
*/
|
|
158
|
+
get modelSpec(): ModelSpec {
|
|
159
|
+
return {
|
|
160
|
+
input: {
|
|
161
|
+
width: 224,
|
|
162
|
+
height: 224,
|
|
163
|
+
format: 'rgb',
|
|
164
|
+
},
|
|
165
|
+
// Trigger when object detection finds these labels
|
|
166
|
+
triggerLabels: ['animal'],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Classify objects in a frame
|
|
172
|
+
*
|
|
173
|
+
* Called by DetectionCoordinator when triggerLabels are detected.
|
|
174
|
+
* The frame is pre-scaled to modelSpec.input dimensions.
|
|
175
|
+
*/
|
|
176
|
+
async detectClassifications(frame: VideoFrameData, _triggerRegions?: Detection[]): Promise<ClassifierResult> {
|
|
177
|
+
const threshold = this.storage?.values.confidenceThreshold ?? 0.5;
|
|
178
|
+
|
|
179
|
+
// TODO: Implement your classification model here
|
|
180
|
+
// Example: Load TensorFlow model and run inference
|
|
181
|
+
//
|
|
182
|
+
// const predictions = await this.model.classify(frame.data);
|
|
183
|
+
// return {
|
|
184
|
+
// detected: predictions.length > 0,
|
|
185
|
+
// detections: predictions.map(p => ({
|
|
186
|
+
// label: p.label,
|
|
187
|
+
// confidence: p.score,
|
|
188
|
+
// box: triggerRegions?.[0]?.box ?? { x: 0, y: 0, width: 1, height: 1 },
|
|
189
|
+
// })),
|
|
190
|
+
// };
|
|
191
|
+
|
|
192
|
+
this.cameraDevice.logger.debug(`Classifying frame ${frame.width}x${frame.height}, threshold: ${threshold}`);
|
|
193
|
+
|
|
194
|
+
// Return empty result (placeholder)
|
|
195
|
+
return {
|
|
196
|
+
detected: false,
|
|
197
|
+
detections: [],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Cleanup when sensor is destroyed
|
|
203
|
+
*/
|
|
204
|
+
async destroy(): Promise<void> {
|
|
205
|
+
// Release model resources if needed
|
|
206
|
+
}
|
|
207
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
All notable changes to this project will be documented in this file.
|
|
2
|
-
|
|
3
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
4
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
-
|
|
6
|
-
## [X.X.X] - ???
|
|
7
|
-
|
|
8
|
-
- Initial Release
|
package/CONTRIBUTING.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Contributing
|
package/scripts/chmod.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { platform } from 'node:os';
|
|
3
|
-
import { dirname, resolve } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
|
|
9
|
-
const isWindows = platform() === 'win32';
|
|
10
|
-
|
|
11
|
-
function chmod() {
|
|
12
|
-
const scriptPath = resolve(__dirname, '../dist/index.js');
|
|
13
|
-
const arg = isWindows ? 'echo "Skipping chmod command on Windows"' : `chmod +x ${scriptPath}`;
|
|
14
|
-
execSync(arg, { stdio: 'inherit' });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
chmod();
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import jsLint from '@eslint/js';
|
|
2
|
-
import stylistic from '@stylistic/eslint-plugin';
|
|
3
|
-
import globals from 'globals';
|
|
4
|
-
|
|
5
|
-
export default [
|
|
6
|
-
{
|
|
7
|
-
files: ['**/*.{js,mjs,cjs,ts,mts}'],
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
ignores: ['**/dist/**', '**/node_modules/**', '**/public/**', '**/build/**', '**/bundle/**', '**/test/**', '**/wasm/**', '**/example/**'],
|
|
11
|
-
},
|
|
12
|
-
jsLint.configs.recommended,
|
|
13
|
-
stylistic.configs['disable-legacy'],
|
|
14
|
-
stylistic.configs.customize({
|
|
15
|
-
indent: 2,
|
|
16
|
-
quotes: 'single',
|
|
17
|
-
semi: true,
|
|
18
|
-
commaDangle: 'always-multiline',
|
|
19
|
-
jsx: false,
|
|
20
|
-
arrowParens: true,
|
|
21
|
-
braceStyle: '1tbs',
|
|
22
|
-
blockSpacing: true,
|
|
23
|
-
quoteProps: 'as-needed',
|
|
24
|
-
}),
|
|
25
|
-
{
|
|
26
|
-
languageOptions: {
|
|
27
|
-
globals: { ...globals.node },
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
rules: {
|
|
31
|
-
'@stylistic/generator-star-spacing': ['error', { before: true, after: false }],
|
|
32
|
-
|
|
33
|
-
// Stylistic specific rules
|
|
34
|
-
'@stylistic/max-len': ['error', { code: 170, tabWidth: 2 }],
|
|
35
|
-
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
|
|
36
|
-
'@stylistic/operator-linebreak': 'off',
|
|
37
|
-
'@stylistic/comma-dangle': [
|
|
38
|
-
'error',
|
|
39
|
-
{
|
|
40
|
-
arrays: 'always-multiline',
|
|
41
|
-
objects: 'always-multiline',
|
|
42
|
-
imports: 'always-multiline',
|
|
43
|
-
exports: 'only-multiline',
|
|
44
|
-
functions: 'always-multiline',
|
|
45
|
-
enums: 'always-multiline',
|
|
46
|
-
generics: 'always-multiline',
|
|
47
|
-
tuples: 'always-multiline',
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
|
|
51
|
-
semi: [1, 'always'],
|
|
52
|
-
// quotes: ['error', 'single'],
|
|
53
|
-
'comma-dangle': ['error', 'only-multiline'],
|
|
54
|
-
'no-multiple-empty-lines': ['warn', { max: 1, maxEOF: 0 }],
|
|
55
|
-
'eol-last': ['error', 'always'],
|
|
56
|
-
'space-before-function-paren': ['error', { named: 'never' }],
|
|
57
|
-
|
|
58
|
-
'no-unused-vars': 'off',
|
|
59
|
-
'no-case-declarations': 'off',
|
|
60
|
-
'no-async-promise-executor': 'off',
|
|
61
|
-
'no-control-regex': 'off',
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
];
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export default class SamplePlugin {
|
|
2
|
-
constructor(logger, api) {
|
|
3
|
-
this.logger = logger;
|
|
4
|
-
this.api = api;
|
|
5
|
-
|
|
6
|
-
this.camera = new Map();
|
|
7
|
-
|
|
8
|
-
this.api.on('finishLaunching', this.start.bind(this));
|
|
9
|
-
this.api.on('shutdown', this.stop.bind(this));
|
|
10
|
-
|
|
11
|
-
this.api.deviceManager.on('cameraSelected', (cameraDevice) => {
|
|
12
|
-
this.logger.log('Camera selected:', cameraDevice.name);
|
|
13
|
-
|
|
14
|
-
if (!this.cameras.has(cameraDevice.id)) {
|
|
15
|
-
this.cameras.set(cameraDevice.id, cameraDevice);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
this.api.deviceManager.on('cameraDeselected', (cameraId) => {
|
|
20
|
-
const cameraDevice = this.cameras.get(cameraId);
|
|
21
|
-
if (cameraDevice) {
|
|
22
|
-
this.logger.log('Camera deselected:', cameraDevice.name);
|
|
23
|
-
this.cameras.delete(cameraId);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async configureCameras() {}
|
|
29
|
-
|
|
30
|
-
async getCapabilities() {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
start() {
|
|
35
|
-
this.logger.log('Finished launching plugin');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
stop() {
|
|
39
|
-
this.logger.log('Shutting down plugin');
|
|
40
|
-
}
|
|
41
|
-
}
|