scrypted-detection-trainer 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.
@@ -0,0 +1,2200 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ var __webpack_modules__ = ({
3
+
4
+ /***/ "./node_modules/@scrypted/sdk/dist/src/index.js"
5
+ /*!******************************************************!*\
6
+ !*** ./node_modules/@scrypted/sdk/dist/src/index.js ***!
7
+ \******************************************************/
8
+ (__unused_webpack_module, exports, __webpack_require__) {
9
+
10
+ "use strict";
11
+
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
25
+ };
26
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
27
+ exports.sdk = exports.MixinDeviceBase = exports.ScryptedDeviceBase = void 0;
28
+ __exportStar(__webpack_require__(/*! ../types/gen/index */ "./node_modules/@scrypted/sdk/dist/types/gen/index.js"), exports);
29
+ const index_1 = __webpack_require__(/*! ../types/gen/index */ "./node_modules/@scrypted/sdk/dist/types/gen/index.js");
30
+ const module_1 = __webpack_require__(/*! module */ "module");
31
+ /**
32
+ * @category Core Reference
33
+ */
34
+ class ScryptedDeviceBase extends index_1.DeviceBase {
35
+ constructor(nativeId) {
36
+ super();
37
+ this.nativeId = nativeId;
38
+ }
39
+ get storage() {
40
+ if (!this._storage) {
41
+ this._storage = exports.sdk.deviceManager.getDeviceStorage(this.nativeId);
42
+ }
43
+ return this._storage;
44
+ }
45
+ get log() {
46
+ if (!this._log) {
47
+ this._log = exports.sdk.deviceManager.getDeviceLogger(this.nativeId);
48
+ }
49
+ return this._log;
50
+ }
51
+ get console() {
52
+ if (!this._console) {
53
+ this._console = exports.sdk.deviceManager.getDeviceConsole(this.nativeId);
54
+ }
55
+ return this._console;
56
+ }
57
+ async createMediaObject(data, mimeType) {
58
+ return exports.sdk.mediaManager.createMediaObject(data, mimeType, {
59
+ sourceId: this.id,
60
+ });
61
+ }
62
+ getMediaObjectConsole(mediaObject) {
63
+ if (typeof mediaObject.sourceId !== 'string')
64
+ return this.console;
65
+ return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.nativeId);
66
+ }
67
+ _lazyLoadDeviceState() {
68
+ if (!this._deviceState) {
69
+ if (this.nativeId) {
70
+ this._deviceState = exports.sdk.deviceManager.getDeviceState(this.nativeId);
71
+ }
72
+ else {
73
+ this._deviceState = exports.sdk.deviceManager.getDeviceState();
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * Fire an event for this device.
79
+ */
80
+ onDeviceEvent(eventInterface, eventData) {
81
+ return exports.sdk.deviceManager.onDeviceEvent(this.nativeId, eventInterface, eventData);
82
+ }
83
+ }
84
+ exports.ScryptedDeviceBase = ScryptedDeviceBase;
85
+ /**
86
+ * @category Mixin Reference
87
+ */
88
+ class MixinDeviceBase extends index_1.DeviceBase {
89
+ constructor(options) {
90
+ super();
91
+ this._listeners = new Set();
92
+ this.mixinDevice = options.mixinDevice;
93
+ this.mixinDeviceInterfaces = options.mixinDeviceInterfaces;
94
+ this.mixinStorageSuffix = options.mixinStorageSuffix;
95
+ this._deviceState = options.mixinDeviceState;
96
+ this.nativeId = exports.sdk.systemManager.getDeviceById(this.id).nativeId;
97
+ this.mixinProviderNativeId = options.mixinProviderNativeId;
98
+ // RpcProxy will trap all properties, and the following check/hack will determine
99
+ // if the device state came from another node worker thread.
100
+ // This should ultimately be discouraged and warned at some point in the future.
101
+ if (this._deviceState.__rpcproxy_traps_all_properties && typeof this._deviceState.id === 'string') {
102
+ this._deviceState = exports.sdk.deviceManager.createDeviceState(this._deviceState.id, this._deviceState.setState);
103
+ }
104
+ }
105
+ get storage() {
106
+ if (!this._storage) {
107
+ const mixinStorageSuffix = this.mixinStorageSuffix;
108
+ const mixinStorageKey = this.id + (mixinStorageSuffix ? ':' + mixinStorageSuffix : '');
109
+ this._storage = exports.sdk.deviceManager.getMixinStorage(mixinStorageKey, this.mixinProviderNativeId);
110
+ }
111
+ return this._storage;
112
+ }
113
+ get console() {
114
+ if (!this._console) {
115
+ if (exports.sdk.deviceManager.getMixinConsole)
116
+ this._console = exports.sdk.deviceManager.getMixinConsole(this.id, this.mixinProviderNativeId);
117
+ else
118
+ this._console = exports.sdk.deviceManager.getDeviceConsole(this.mixinProviderNativeId);
119
+ }
120
+ return this._console;
121
+ }
122
+ async createMediaObject(data, mimeType) {
123
+ return exports.sdk.mediaManager.createMediaObject(data, mimeType, {
124
+ sourceId: this.id,
125
+ });
126
+ }
127
+ getMediaObjectConsole(mediaObject) {
128
+ if (typeof mediaObject.sourceId !== 'string')
129
+ return this.console;
130
+ return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.mixinProviderNativeId);
131
+ }
132
+ /**
133
+ * Fire an event for this device.
134
+ */
135
+ onDeviceEvent(eventInterface, eventData) {
136
+ return exports.sdk.deviceManager.onMixinEvent(this.id, this, eventInterface, eventData);
137
+ }
138
+ _lazyLoadDeviceState() {
139
+ }
140
+ manageListener(listener) {
141
+ this._listeners.add(listener);
142
+ }
143
+ release() {
144
+ for (const l of this._listeners) {
145
+ l.removeListener();
146
+ }
147
+ }
148
+ }
149
+ exports.MixinDeviceBase = MixinDeviceBase;
150
+ (function () {
151
+ function _createGetState(state) {
152
+ return function () {
153
+ this._lazyLoadDeviceState();
154
+ // @ts-ignore: accessing private property
155
+ return this._deviceState?.[state];
156
+ };
157
+ }
158
+ function _createSetState(state) {
159
+ return function (value) {
160
+ this._lazyLoadDeviceState();
161
+ // @ts-ignore: accessing private property
162
+ if (!this._deviceState) {
163
+ console.warn('device state is unavailable. the device must be discovered with deviceManager.onDeviceDiscovered or deviceManager.onDevicesChanged before the state can be set.');
164
+ }
165
+ else {
166
+ // @ts-ignore: accessing private property
167
+ this._deviceState[state] = value;
168
+ }
169
+ };
170
+ }
171
+ for (const field of Object.values(index_1.ScryptedInterfaceProperty)) {
172
+ if (field === index_1.ScryptedInterfaceProperty.nativeId)
173
+ continue;
174
+ Object.defineProperty(ScryptedDeviceBase.prototype, field, {
175
+ set: _createSetState(field),
176
+ get: _createGetState(field),
177
+ });
178
+ Object.defineProperty(MixinDeviceBase.prototype, field, {
179
+ set: _createSetState(field),
180
+ get: _createGetState(field),
181
+ });
182
+ }
183
+ })();
184
+ exports.sdk = {};
185
+ try {
186
+ let loaded = false;
187
+ try {
188
+ // todo: remove usage of process.env.SCRYPTED_SDK_MODULE, only existed in prerelease builds.
189
+ // import.meta is not a reliable way to detect es module support in webpack since webpack
190
+ // evaluates that to true at runtime.
191
+ const esModule = process.env.SCRYPTED_SDK_ES_MODULE || process.env.SCRYPTED_SDK_MODULE;
192
+ const cjsModule = process.env.SCRYPTED_SDK_CJS_MODULE || process.env.SCRYPTED_SDK_MODULE;
193
+ // @ts-expect-error
194
+ if (esModule && "undefined" !== 'undefined') // removed by dead control flow
195
+ {}
196
+ else if (cjsModule) {
197
+ // @ts-expect-error
198
+ if (typeof require !== 'undefined') {
199
+ // @ts-expect-error
200
+ const sdkModule = require(process.env.SCRYPTED_SDK_MODULE);
201
+ Object.assign(exports.sdk, sdkModule.getScryptedStatic());
202
+ loaded = true;
203
+ }
204
+ else {
205
+ const sdkModule = __webpack_require__("./node_modules/@scrypted/sdk/dist/src sync recursive")(cjsModule);
206
+ Object.assign(exports.sdk, sdkModule.getScryptedStatic());
207
+ loaded = true;
208
+ }
209
+ }
210
+ }
211
+ catch (e) {
212
+ console.warn("failed to load sdk module", e);
213
+ throw e;
214
+ }
215
+ if (!loaded) {
216
+ let runtimeAPI;
217
+ try {
218
+ runtimeAPI = pluginRuntimeAPI;
219
+ }
220
+ catch (e) {
221
+ }
222
+ Object.assign(exports.sdk, {
223
+ log: deviceManager.getDeviceLogger(undefined),
224
+ deviceManager,
225
+ endpointManager,
226
+ mediaManager,
227
+ systemManager,
228
+ pluginHostAPI,
229
+ ...runtimeAPI,
230
+ });
231
+ }
232
+ try {
233
+ exports.sdk.systemManager.setScryptedInterfaceDescriptors?.(index_1.TYPES_VERSION, index_1.ScryptedInterfaceDescriptors)?.catch(() => { });
234
+ }
235
+ catch (e) {
236
+ }
237
+ }
238
+ catch (e) {
239
+ console.error('sdk initialization error, import @scrypted/types or use @scrypted/client instead', e);
240
+ }
241
+ exports["default"] = exports.sdk;
242
+ //# sourceMappingURL=index.js.map
243
+
244
+ /***/ },
245
+
246
+ /***/ "./node_modules/@scrypted/sdk/dist/src sync recursive"
247
+ /*!***************************************************!*\
248
+ !*** ./node_modules/@scrypted/sdk/dist/src/ sync ***!
249
+ \***************************************************/
250
+ (module) {
251
+
252
+ function webpackEmptyContext(req) {
253
+ var e = new Error("Cannot find module '" + req + "'");
254
+ e.code = 'MODULE_NOT_FOUND';
255
+ throw e;
256
+ }
257
+ webpackEmptyContext.keys = () => ([]);
258
+ webpackEmptyContext.resolve = webpackEmptyContext;
259
+ webpackEmptyContext.id = "./node_modules/@scrypted/sdk/dist/src sync recursive";
260
+ module.exports = webpackEmptyContext;
261
+
262
+ /***/ },
263
+
264
+ /***/ "./node_modules/@scrypted/sdk/dist/types/gen/index.js"
265
+ /*!************************************************************!*\
266
+ !*** ./node_modules/@scrypted/sdk/dist/types/gen/index.js ***!
267
+ \************************************************************/
268
+ (__unused_webpack_module, exports) {
269
+
270
+ "use strict";
271
+
272
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
273
+ exports.ScryptedMimeTypes = exports.ScryptedInterface = exports.MediaPlayerState = exports.SecuritySystemObstruction = exports.SecuritySystemMode = exports.AirQuality = exports.AirPurifierMode = exports.AirPurifierStatus = exports.ChargeState = exports.LockState = exports.PanTiltZoomMovement = exports.ThermostatMode = exports.TemperatureUnit = exports.FanMode = exports.HumidityMode = exports.ScryptedDeviceType = exports.ScryptedInterfaceDescriptors = exports.ScryptedInterfaceMethod = exports.ScryptedInterfaceProperty = exports.DeviceBase = exports.TYPES_VERSION = void 0;
274
+ exports.TYPES_VERSION = "0.3.116";
275
+ class DeviceBase {
276
+ }
277
+ exports.DeviceBase = DeviceBase;
278
+ var ScryptedInterfaceProperty;
279
+ (function (ScryptedInterfaceProperty) {
280
+ ScryptedInterfaceProperty["id"] = "id";
281
+ ScryptedInterfaceProperty["info"] = "info";
282
+ ScryptedInterfaceProperty["interfaces"] = "interfaces";
283
+ ScryptedInterfaceProperty["mixins"] = "mixins";
284
+ ScryptedInterfaceProperty["name"] = "name";
285
+ ScryptedInterfaceProperty["nativeId"] = "nativeId";
286
+ ScryptedInterfaceProperty["pluginId"] = "pluginId";
287
+ ScryptedInterfaceProperty["providedInterfaces"] = "providedInterfaces";
288
+ ScryptedInterfaceProperty["providedName"] = "providedName";
289
+ ScryptedInterfaceProperty["providedRoom"] = "providedRoom";
290
+ ScryptedInterfaceProperty["providedType"] = "providedType";
291
+ ScryptedInterfaceProperty["providerId"] = "providerId";
292
+ ScryptedInterfaceProperty["room"] = "room";
293
+ ScryptedInterfaceProperty["type"] = "type";
294
+ ScryptedInterfaceProperty["scryptedRuntimeArguments"] = "scryptedRuntimeArguments";
295
+ ScryptedInterfaceProperty["on"] = "on";
296
+ ScryptedInterfaceProperty["brightness"] = "brightness";
297
+ ScryptedInterfaceProperty["colorTemperature"] = "colorTemperature";
298
+ ScryptedInterfaceProperty["rgb"] = "rgb";
299
+ ScryptedInterfaceProperty["hsv"] = "hsv";
300
+ ScryptedInterfaceProperty["buttons"] = "buttons";
301
+ ScryptedInterfaceProperty["sensors"] = "sensors";
302
+ ScryptedInterfaceProperty["running"] = "running";
303
+ ScryptedInterfaceProperty["paused"] = "paused";
304
+ ScryptedInterfaceProperty["docked"] = "docked";
305
+ ScryptedInterfaceProperty["temperatureSetting"] = "temperatureSetting";
306
+ ScryptedInterfaceProperty["temperature"] = "temperature";
307
+ ScryptedInterfaceProperty["temperatureUnit"] = "temperatureUnit";
308
+ ScryptedInterfaceProperty["humidity"] = "humidity";
309
+ ScryptedInterfaceProperty["audioVolumes"] = "audioVolumes";
310
+ ScryptedInterfaceProperty["recordingActive"] = "recordingActive";
311
+ ScryptedInterfaceProperty["ptzCapabilities"] = "ptzCapabilities";
312
+ ScryptedInterfaceProperty["lockState"] = "lockState";
313
+ ScryptedInterfaceProperty["entryOpen"] = "entryOpen";
314
+ ScryptedInterfaceProperty["batteryLevel"] = "batteryLevel";
315
+ ScryptedInterfaceProperty["chargeState"] = "chargeState";
316
+ ScryptedInterfaceProperty["online"] = "online";
317
+ ScryptedInterfaceProperty["fromMimeType"] = "fromMimeType";
318
+ ScryptedInterfaceProperty["toMimeType"] = "toMimeType";
319
+ ScryptedInterfaceProperty["converters"] = "converters";
320
+ ScryptedInterfaceProperty["binaryState"] = "binaryState";
321
+ ScryptedInterfaceProperty["tampered"] = "tampered";
322
+ ScryptedInterfaceProperty["sleeping"] = "sleeping";
323
+ ScryptedInterfaceProperty["powerDetected"] = "powerDetected";
324
+ ScryptedInterfaceProperty["audioDetected"] = "audioDetected";
325
+ ScryptedInterfaceProperty["motionDetected"] = "motionDetected";
326
+ ScryptedInterfaceProperty["ambientLight"] = "ambientLight";
327
+ ScryptedInterfaceProperty["occupied"] = "occupied";
328
+ ScryptedInterfaceProperty["flooded"] = "flooded";
329
+ ScryptedInterfaceProperty["ultraviolet"] = "ultraviolet";
330
+ ScryptedInterfaceProperty["luminance"] = "luminance";
331
+ ScryptedInterfaceProperty["position"] = "position";
332
+ ScryptedInterfaceProperty["securitySystemState"] = "securitySystemState";
333
+ ScryptedInterfaceProperty["pm10Density"] = "pm10Density";
334
+ ScryptedInterfaceProperty["pm25Density"] = "pm25Density";
335
+ ScryptedInterfaceProperty["vocDensity"] = "vocDensity";
336
+ ScryptedInterfaceProperty["noxDensity"] = "noxDensity";
337
+ ScryptedInterfaceProperty["co2ppm"] = "co2ppm";
338
+ ScryptedInterfaceProperty["airQuality"] = "airQuality";
339
+ ScryptedInterfaceProperty["airPurifierState"] = "airPurifierState";
340
+ ScryptedInterfaceProperty["filterChangeIndication"] = "filterChangeIndication";
341
+ ScryptedInterfaceProperty["filterLifeLevel"] = "filterLifeLevel";
342
+ ScryptedInterfaceProperty["humiditySetting"] = "humiditySetting";
343
+ ScryptedInterfaceProperty["fan"] = "fan";
344
+ ScryptedInterfaceProperty["applicationInfo"] = "applicationInfo";
345
+ ScryptedInterfaceProperty["systemDevice"] = "systemDevice";
346
+ })(ScryptedInterfaceProperty || (exports.ScryptedInterfaceProperty = ScryptedInterfaceProperty = {}));
347
+ var ScryptedInterfaceMethod;
348
+ (function (ScryptedInterfaceMethod) {
349
+ ScryptedInterfaceMethod["listen"] = "listen";
350
+ ScryptedInterfaceMethod["probe"] = "probe";
351
+ ScryptedInterfaceMethod["setMixins"] = "setMixins";
352
+ ScryptedInterfaceMethod["setName"] = "setName";
353
+ ScryptedInterfaceMethod["setRoom"] = "setRoom";
354
+ ScryptedInterfaceMethod["setType"] = "setType";
355
+ ScryptedInterfaceMethod["getPluginJson"] = "getPluginJson";
356
+ ScryptedInterfaceMethod["turnOff"] = "turnOff";
357
+ ScryptedInterfaceMethod["turnOn"] = "turnOn";
358
+ ScryptedInterfaceMethod["setBrightness"] = "setBrightness";
359
+ ScryptedInterfaceMethod["getTemperatureMaxK"] = "getTemperatureMaxK";
360
+ ScryptedInterfaceMethod["getTemperatureMinK"] = "getTemperatureMinK";
361
+ ScryptedInterfaceMethod["setColorTemperature"] = "setColorTemperature";
362
+ ScryptedInterfaceMethod["setRgb"] = "setRgb";
363
+ ScryptedInterfaceMethod["setHsv"] = "setHsv";
364
+ ScryptedInterfaceMethod["pressButton"] = "pressButton";
365
+ ScryptedInterfaceMethod["sendNotification"] = "sendNotification";
366
+ ScryptedInterfaceMethod["start"] = "start";
367
+ ScryptedInterfaceMethod["stop"] = "stop";
368
+ ScryptedInterfaceMethod["pause"] = "pause";
369
+ ScryptedInterfaceMethod["resume"] = "resume";
370
+ ScryptedInterfaceMethod["dock"] = "dock";
371
+ ScryptedInterfaceMethod["setTemperature"] = "setTemperature";
372
+ ScryptedInterfaceMethod["setTemperatureUnit"] = "setTemperatureUnit";
373
+ ScryptedInterfaceMethod["getPictureOptions"] = "getPictureOptions";
374
+ ScryptedInterfaceMethod["takePicture"] = "takePicture";
375
+ ScryptedInterfaceMethod["getAudioStream"] = "getAudioStream";
376
+ ScryptedInterfaceMethod["setAudioVolumes"] = "setAudioVolumes";
377
+ ScryptedInterfaceMethod["startDisplay"] = "startDisplay";
378
+ ScryptedInterfaceMethod["stopDisplay"] = "stopDisplay";
379
+ ScryptedInterfaceMethod["getVideoStream"] = "getVideoStream";
380
+ ScryptedInterfaceMethod["getVideoStreamOptions"] = "getVideoStreamOptions";
381
+ ScryptedInterfaceMethod["getPrivacyMasks"] = "getPrivacyMasks";
382
+ ScryptedInterfaceMethod["setPrivacyMasks"] = "setPrivacyMasks";
383
+ ScryptedInterfaceMethod["getVideoTextOverlays"] = "getVideoTextOverlays";
384
+ ScryptedInterfaceMethod["setVideoTextOverlay"] = "setVideoTextOverlay";
385
+ ScryptedInterfaceMethod["getRecordingStream"] = "getRecordingStream";
386
+ ScryptedInterfaceMethod["getRecordingStreamCurrentTime"] = "getRecordingStreamCurrentTime";
387
+ ScryptedInterfaceMethod["getRecordingStreamOptions"] = "getRecordingStreamOptions";
388
+ ScryptedInterfaceMethod["getRecordingStreamThumbnail"] = "getRecordingStreamThumbnail";
389
+ ScryptedInterfaceMethod["deleteRecordingStream"] = "deleteRecordingStream";
390
+ ScryptedInterfaceMethod["setRecordingActive"] = "setRecordingActive";
391
+ ScryptedInterfaceMethod["ptzCommand"] = "ptzCommand";
392
+ ScryptedInterfaceMethod["getRecordedEvents"] = "getRecordedEvents";
393
+ ScryptedInterfaceMethod["getVideoClip"] = "getVideoClip";
394
+ ScryptedInterfaceMethod["getVideoClips"] = "getVideoClips";
395
+ ScryptedInterfaceMethod["getVideoClipThumbnail"] = "getVideoClipThumbnail";
396
+ ScryptedInterfaceMethod["removeVideoClips"] = "removeVideoClips";
397
+ ScryptedInterfaceMethod["setVideoStreamOptions"] = "setVideoStreamOptions";
398
+ ScryptedInterfaceMethod["startIntercom"] = "startIntercom";
399
+ ScryptedInterfaceMethod["stopIntercom"] = "stopIntercom";
400
+ ScryptedInterfaceMethod["lock"] = "lock";
401
+ ScryptedInterfaceMethod["unlock"] = "unlock";
402
+ ScryptedInterfaceMethod["addPassword"] = "addPassword";
403
+ ScryptedInterfaceMethod["getPasswords"] = "getPasswords";
404
+ ScryptedInterfaceMethod["removePassword"] = "removePassword";
405
+ ScryptedInterfaceMethod["activate"] = "activate";
406
+ ScryptedInterfaceMethod["deactivate"] = "deactivate";
407
+ ScryptedInterfaceMethod["isReversible"] = "isReversible";
408
+ ScryptedInterfaceMethod["closeEntry"] = "closeEntry";
409
+ ScryptedInterfaceMethod["openEntry"] = "openEntry";
410
+ ScryptedInterfaceMethod["getDevice"] = "getDevice";
411
+ ScryptedInterfaceMethod["releaseDevice"] = "releaseDevice";
412
+ ScryptedInterfaceMethod["adoptDevice"] = "adoptDevice";
413
+ ScryptedInterfaceMethod["discoverDevices"] = "discoverDevices";
414
+ ScryptedInterfaceMethod["createDevice"] = "createDevice";
415
+ ScryptedInterfaceMethod["getCreateDeviceSettings"] = "getCreateDeviceSettings";
416
+ ScryptedInterfaceMethod["reboot"] = "reboot";
417
+ ScryptedInterfaceMethod["getRefreshFrequency"] = "getRefreshFrequency";
418
+ ScryptedInterfaceMethod["refresh"] = "refresh";
419
+ ScryptedInterfaceMethod["getMediaStatus"] = "getMediaStatus";
420
+ ScryptedInterfaceMethod["load"] = "load";
421
+ ScryptedInterfaceMethod["seek"] = "seek";
422
+ ScryptedInterfaceMethod["skipNext"] = "skipNext";
423
+ ScryptedInterfaceMethod["skipPrevious"] = "skipPrevious";
424
+ ScryptedInterfaceMethod["convert"] = "convert";
425
+ ScryptedInterfaceMethod["convertMedia"] = "convertMedia";
426
+ ScryptedInterfaceMethod["getSettings"] = "getSettings";
427
+ ScryptedInterfaceMethod["putSetting"] = "putSetting";
428
+ ScryptedInterfaceMethod["armSecuritySystem"] = "armSecuritySystem";
429
+ ScryptedInterfaceMethod["disarmSecuritySystem"] = "disarmSecuritySystem";
430
+ ScryptedInterfaceMethod["setAirPurifierState"] = "setAirPurifierState";
431
+ ScryptedInterfaceMethod["getReadmeMarkdown"] = "getReadmeMarkdown";
432
+ ScryptedInterfaceMethod["getOauthUrl"] = "getOauthUrl";
433
+ ScryptedInterfaceMethod["onOauthCallback"] = "onOauthCallback";
434
+ ScryptedInterfaceMethod["canMixin"] = "canMixin";
435
+ ScryptedInterfaceMethod["getMixin"] = "getMixin";
436
+ ScryptedInterfaceMethod["releaseMixin"] = "releaseMixin";
437
+ ScryptedInterfaceMethod["onRequest"] = "onRequest";
438
+ ScryptedInterfaceMethod["onConnection"] = "onConnection";
439
+ ScryptedInterfaceMethod["onPush"] = "onPush";
440
+ ScryptedInterfaceMethod["run"] = "run";
441
+ ScryptedInterfaceMethod["eval"] = "eval";
442
+ ScryptedInterfaceMethod["loadScripts"] = "loadScripts";
443
+ ScryptedInterfaceMethod["saveScript"] = "saveScript";
444
+ ScryptedInterfaceMethod["forkInterface"] = "forkInterface";
445
+ ScryptedInterfaceMethod["trackObjects"] = "trackObjects";
446
+ ScryptedInterfaceMethod["getDetectionInput"] = "getDetectionInput";
447
+ ScryptedInterfaceMethod["getObjectTypes"] = "getObjectTypes";
448
+ ScryptedInterfaceMethod["detectObjects"] = "detectObjects";
449
+ ScryptedInterfaceMethod["generateObjectDetections"] = "generateObjectDetections";
450
+ ScryptedInterfaceMethod["getDetectionModel"] = "getDetectionModel";
451
+ ScryptedInterfaceMethod["setHumidity"] = "setHumidity";
452
+ ScryptedInterfaceMethod["setFan"] = "setFan";
453
+ ScryptedInterfaceMethod["startRTCSignalingSession"] = "startRTCSignalingSession";
454
+ ScryptedInterfaceMethod["createRTCSignalingSession"] = "createRTCSignalingSession";
455
+ ScryptedInterfaceMethod["getScryptedUserAccessControl"] = "getScryptedUserAccessControl";
456
+ ScryptedInterfaceMethod["generateVideoFrames"] = "generateVideoFrames";
457
+ ScryptedInterfaceMethod["connectStream"] = "connectStream";
458
+ ScryptedInterfaceMethod["getTTYSettings"] = "getTTYSettings";
459
+ })(ScryptedInterfaceMethod || (exports.ScryptedInterfaceMethod = ScryptedInterfaceMethod = {}));
460
+ exports.ScryptedInterfaceDescriptors = {
461
+ "ScryptedDevice": {
462
+ "name": "ScryptedDevice",
463
+ "methods": [
464
+ "listen",
465
+ "probe",
466
+ "setMixins",
467
+ "setName",
468
+ "setRoom",
469
+ "setType"
470
+ ],
471
+ "properties": [
472
+ "id",
473
+ "info",
474
+ "interfaces",
475
+ "mixins",
476
+ "name",
477
+ "nativeId",
478
+ "pluginId",
479
+ "providedInterfaces",
480
+ "providedName",
481
+ "providedRoom",
482
+ "providedType",
483
+ "providerId",
484
+ "room",
485
+ "type"
486
+ ]
487
+ },
488
+ "ScryptedPlugin": {
489
+ "name": "ScryptedPlugin",
490
+ "methods": [
491
+ "getPluginJson"
492
+ ],
493
+ "properties": []
494
+ },
495
+ "ScryptedPluginRuntime": {
496
+ "name": "ScryptedPluginRuntime",
497
+ "methods": [],
498
+ "properties": [
499
+ "scryptedRuntimeArguments"
500
+ ]
501
+ },
502
+ "OnOff": {
503
+ "name": "OnOff",
504
+ "methods": [
505
+ "turnOff",
506
+ "turnOn"
507
+ ],
508
+ "properties": [
509
+ "on"
510
+ ]
511
+ },
512
+ "Brightness": {
513
+ "name": "Brightness",
514
+ "methods": [
515
+ "setBrightness"
516
+ ],
517
+ "properties": [
518
+ "brightness"
519
+ ]
520
+ },
521
+ "ColorSettingTemperature": {
522
+ "name": "ColorSettingTemperature",
523
+ "methods": [
524
+ "getTemperatureMaxK",
525
+ "getTemperatureMinK",
526
+ "setColorTemperature"
527
+ ],
528
+ "properties": [
529
+ "colorTemperature"
530
+ ]
531
+ },
532
+ "ColorSettingRgb": {
533
+ "name": "ColorSettingRgb",
534
+ "methods": [
535
+ "setRgb"
536
+ ],
537
+ "properties": [
538
+ "rgb"
539
+ ]
540
+ },
541
+ "ColorSettingHsv": {
542
+ "name": "ColorSettingHsv",
543
+ "methods": [
544
+ "setHsv"
545
+ ],
546
+ "properties": [
547
+ "hsv"
548
+ ]
549
+ },
550
+ "Buttons": {
551
+ "name": "Buttons",
552
+ "methods": [],
553
+ "properties": [
554
+ "buttons"
555
+ ]
556
+ },
557
+ "PressButtons": {
558
+ "name": "PressButtons",
559
+ "methods": [
560
+ "pressButton"
561
+ ],
562
+ "properties": []
563
+ },
564
+ "Sensors": {
565
+ "name": "Sensors",
566
+ "methods": [],
567
+ "properties": [
568
+ "sensors"
569
+ ]
570
+ },
571
+ "Notifier": {
572
+ "name": "Notifier",
573
+ "methods": [
574
+ "sendNotification"
575
+ ],
576
+ "properties": []
577
+ },
578
+ "StartStop": {
579
+ "name": "StartStop",
580
+ "methods": [
581
+ "start",
582
+ "stop"
583
+ ],
584
+ "properties": [
585
+ "running"
586
+ ]
587
+ },
588
+ "Pause": {
589
+ "name": "Pause",
590
+ "methods": [
591
+ "pause",
592
+ "resume"
593
+ ],
594
+ "properties": [
595
+ "paused"
596
+ ]
597
+ },
598
+ "Dock": {
599
+ "name": "Dock",
600
+ "methods": [
601
+ "dock"
602
+ ],
603
+ "properties": [
604
+ "docked"
605
+ ]
606
+ },
607
+ "TemperatureSetting": {
608
+ "name": "TemperatureSetting",
609
+ "methods": [
610
+ "setTemperature"
611
+ ],
612
+ "properties": [
613
+ "temperatureSetting"
614
+ ]
615
+ },
616
+ "Thermometer": {
617
+ "name": "Thermometer",
618
+ "methods": [
619
+ "setTemperatureUnit"
620
+ ],
621
+ "properties": [
622
+ "temperature",
623
+ "temperatureUnit"
624
+ ]
625
+ },
626
+ "HumiditySensor": {
627
+ "name": "HumiditySensor",
628
+ "methods": [],
629
+ "properties": [
630
+ "humidity"
631
+ ]
632
+ },
633
+ "Camera": {
634
+ "name": "Camera",
635
+ "methods": [
636
+ "getPictureOptions",
637
+ "takePicture"
638
+ ],
639
+ "properties": []
640
+ },
641
+ "Microphone": {
642
+ "name": "Microphone",
643
+ "methods": [
644
+ "getAudioStream"
645
+ ],
646
+ "properties": []
647
+ },
648
+ "AudioVolumeControl": {
649
+ "name": "AudioVolumeControl",
650
+ "methods": [
651
+ "setAudioVolumes"
652
+ ],
653
+ "properties": [
654
+ "audioVolumes"
655
+ ]
656
+ },
657
+ "Display": {
658
+ "name": "Display",
659
+ "methods": [
660
+ "startDisplay",
661
+ "stopDisplay"
662
+ ],
663
+ "properties": []
664
+ },
665
+ "VideoCamera": {
666
+ "name": "VideoCamera",
667
+ "methods": [
668
+ "getVideoStream",
669
+ "getVideoStreamOptions"
670
+ ],
671
+ "properties": []
672
+ },
673
+ "VideoCameraMask": {
674
+ "name": "VideoCameraMask",
675
+ "methods": [
676
+ "getPrivacyMasks",
677
+ "setPrivacyMasks"
678
+ ],
679
+ "properties": []
680
+ },
681
+ "VideoTextOverlays": {
682
+ "name": "VideoTextOverlays",
683
+ "methods": [
684
+ "getVideoTextOverlays",
685
+ "setVideoTextOverlay"
686
+ ],
687
+ "properties": []
688
+ },
689
+ "VideoRecorder": {
690
+ "name": "VideoRecorder",
691
+ "methods": [
692
+ "getRecordingStream",
693
+ "getRecordingStreamCurrentTime",
694
+ "getRecordingStreamOptions",
695
+ "getRecordingStreamThumbnail"
696
+ ],
697
+ "properties": [
698
+ "recordingActive"
699
+ ]
700
+ },
701
+ "VideoRecorderManagement": {
702
+ "name": "VideoRecorderManagement",
703
+ "methods": [
704
+ "deleteRecordingStream",
705
+ "setRecordingActive"
706
+ ],
707
+ "properties": []
708
+ },
709
+ "PanTiltZoom": {
710
+ "name": "PanTiltZoom",
711
+ "methods": [
712
+ "ptzCommand"
713
+ ],
714
+ "properties": [
715
+ "ptzCapabilities"
716
+ ]
717
+ },
718
+ "EventRecorder": {
719
+ "name": "EventRecorder",
720
+ "methods": [
721
+ "getRecordedEvents"
722
+ ],
723
+ "properties": []
724
+ },
725
+ "VideoClips": {
726
+ "name": "VideoClips",
727
+ "methods": [
728
+ "getVideoClip",
729
+ "getVideoClips",
730
+ "getVideoClipThumbnail",
731
+ "removeVideoClips"
732
+ ],
733
+ "properties": []
734
+ },
735
+ "VideoCameraConfiguration": {
736
+ "name": "VideoCameraConfiguration",
737
+ "methods": [
738
+ "setVideoStreamOptions"
739
+ ],
740
+ "properties": []
741
+ },
742
+ "Intercom": {
743
+ "name": "Intercom",
744
+ "methods": [
745
+ "startIntercom",
746
+ "stopIntercom"
747
+ ],
748
+ "properties": []
749
+ },
750
+ "Lock": {
751
+ "name": "Lock",
752
+ "methods": [
753
+ "lock",
754
+ "unlock"
755
+ ],
756
+ "properties": [
757
+ "lockState"
758
+ ]
759
+ },
760
+ "PasswordStore": {
761
+ "name": "PasswordStore",
762
+ "methods": [
763
+ "addPassword",
764
+ "getPasswords",
765
+ "removePassword"
766
+ ],
767
+ "properties": []
768
+ },
769
+ "Scene": {
770
+ "name": "Scene",
771
+ "methods": [
772
+ "activate",
773
+ "deactivate",
774
+ "isReversible"
775
+ ],
776
+ "properties": []
777
+ },
778
+ "Entry": {
779
+ "name": "Entry",
780
+ "methods": [
781
+ "closeEntry",
782
+ "openEntry"
783
+ ],
784
+ "properties": []
785
+ },
786
+ "EntrySensor": {
787
+ "name": "EntrySensor",
788
+ "methods": [],
789
+ "properties": [
790
+ "entryOpen"
791
+ ]
792
+ },
793
+ "DeviceProvider": {
794
+ "name": "DeviceProvider",
795
+ "methods": [
796
+ "getDevice",
797
+ "releaseDevice"
798
+ ],
799
+ "properties": []
800
+ },
801
+ "DeviceDiscovery": {
802
+ "name": "DeviceDiscovery",
803
+ "methods": [
804
+ "adoptDevice",
805
+ "discoverDevices"
806
+ ],
807
+ "properties": []
808
+ },
809
+ "DeviceCreator": {
810
+ "name": "DeviceCreator",
811
+ "methods": [
812
+ "createDevice",
813
+ "getCreateDeviceSettings"
814
+ ],
815
+ "properties": []
816
+ },
817
+ "Battery": {
818
+ "name": "Battery",
819
+ "methods": [],
820
+ "properties": [
821
+ "batteryLevel"
822
+ ]
823
+ },
824
+ "Charger": {
825
+ "name": "Charger",
826
+ "methods": [],
827
+ "properties": [
828
+ "chargeState"
829
+ ]
830
+ },
831
+ "Reboot": {
832
+ "name": "Reboot",
833
+ "methods": [
834
+ "reboot"
835
+ ],
836
+ "properties": []
837
+ },
838
+ "Refresh": {
839
+ "name": "Refresh",
840
+ "methods": [
841
+ "getRefreshFrequency",
842
+ "refresh"
843
+ ],
844
+ "properties": []
845
+ },
846
+ "MediaPlayer": {
847
+ "name": "MediaPlayer",
848
+ "methods": [
849
+ "getMediaStatus",
850
+ "load",
851
+ "seek",
852
+ "skipNext",
853
+ "skipPrevious"
854
+ ],
855
+ "properties": []
856
+ },
857
+ "Online": {
858
+ "name": "Online",
859
+ "methods": [],
860
+ "properties": [
861
+ "online"
862
+ ]
863
+ },
864
+ "BufferConverter": {
865
+ "name": "BufferConverter",
866
+ "methods": [
867
+ "convert"
868
+ ],
869
+ "properties": [
870
+ "fromMimeType",
871
+ "toMimeType"
872
+ ]
873
+ },
874
+ "MediaConverter": {
875
+ "name": "MediaConverter",
876
+ "methods": [
877
+ "convertMedia"
878
+ ],
879
+ "properties": [
880
+ "converters"
881
+ ]
882
+ },
883
+ "Settings": {
884
+ "name": "Settings",
885
+ "methods": [
886
+ "getSettings",
887
+ "putSetting"
888
+ ],
889
+ "properties": []
890
+ },
891
+ "BinarySensor": {
892
+ "name": "BinarySensor",
893
+ "methods": [],
894
+ "properties": [
895
+ "binaryState"
896
+ ]
897
+ },
898
+ "TamperSensor": {
899
+ "name": "TamperSensor",
900
+ "methods": [],
901
+ "properties": [
902
+ "tampered"
903
+ ]
904
+ },
905
+ "Sleep": {
906
+ "name": "Sleep",
907
+ "methods": [],
908
+ "properties": [
909
+ "sleeping"
910
+ ]
911
+ },
912
+ "PowerSensor": {
913
+ "name": "PowerSensor",
914
+ "methods": [],
915
+ "properties": [
916
+ "powerDetected"
917
+ ]
918
+ },
919
+ "AudioSensor": {
920
+ "name": "AudioSensor",
921
+ "methods": [],
922
+ "properties": [
923
+ "audioDetected"
924
+ ]
925
+ },
926
+ "MotionSensor": {
927
+ "name": "MotionSensor",
928
+ "methods": [],
929
+ "properties": [
930
+ "motionDetected"
931
+ ]
932
+ },
933
+ "AmbientLightSensor": {
934
+ "name": "AmbientLightSensor",
935
+ "methods": [],
936
+ "properties": [
937
+ "ambientLight"
938
+ ]
939
+ },
940
+ "OccupancySensor": {
941
+ "name": "OccupancySensor",
942
+ "methods": [],
943
+ "properties": [
944
+ "occupied"
945
+ ]
946
+ },
947
+ "FloodSensor": {
948
+ "name": "FloodSensor",
949
+ "methods": [],
950
+ "properties": [
951
+ "flooded"
952
+ ]
953
+ },
954
+ "UltravioletSensor": {
955
+ "name": "UltravioletSensor",
956
+ "methods": [],
957
+ "properties": [
958
+ "ultraviolet"
959
+ ]
960
+ },
961
+ "LuminanceSensor": {
962
+ "name": "LuminanceSensor",
963
+ "methods": [],
964
+ "properties": [
965
+ "luminance"
966
+ ]
967
+ },
968
+ "PositionSensor": {
969
+ "name": "PositionSensor",
970
+ "methods": [],
971
+ "properties": [
972
+ "position"
973
+ ]
974
+ },
975
+ "SecuritySystem": {
976
+ "name": "SecuritySystem",
977
+ "methods": [
978
+ "armSecuritySystem",
979
+ "disarmSecuritySystem"
980
+ ],
981
+ "properties": [
982
+ "securitySystemState"
983
+ ]
984
+ },
985
+ "PM10Sensor": {
986
+ "name": "PM10Sensor",
987
+ "methods": [],
988
+ "properties": [
989
+ "pm10Density"
990
+ ]
991
+ },
992
+ "PM25Sensor": {
993
+ "name": "PM25Sensor",
994
+ "methods": [],
995
+ "properties": [
996
+ "pm25Density"
997
+ ]
998
+ },
999
+ "VOCSensor": {
1000
+ "name": "VOCSensor",
1001
+ "methods": [],
1002
+ "properties": [
1003
+ "vocDensity"
1004
+ ]
1005
+ },
1006
+ "NOXSensor": {
1007
+ "name": "NOXSensor",
1008
+ "methods": [],
1009
+ "properties": [
1010
+ "noxDensity"
1011
+ ]
1012
+ },
1013
+ "CO2Sensor": {
1014
+ "name": "CO2Sensor",
1015
+ "methods": [],
1016
+ "properties": [
1017
+ "co2ppm"
1018
+ ]
1019
+ },
1020
+ "AirQualitySensor": {
1021
+ "name": "AirQualitySensor",
1022
+ "methods": [],
1023
+ "properties": [
1024
+ "airQuality"
1025
+ ]
1026
+ },
1027
+ "AirPurifier": {
1028
+ "name": "AirPurifier",
1029
+ "methods": [
1030
+ "setAirPurifierState"
1031
+ ],
1032
+ "properties": [
1033
+ "airPurifierState"
1034
+ ]
1035
+ },
1036
+ "FilterMaintenance": {
1037
+ "name": "FilterMaintenance",
1038
+ "methods": [],
1039
+ "properties": [
1040
+ "filterChangeIndication",
1041
+ "filterLifeLevel"
1042
+ ]
1043
+ },
1044
+ "Readme": {
1045
+ "name": "Readme",
1046
+ "methods": [
1047
+ "getReadmeMarkdown"
1048
+ ],
1049
+ "properties": []
1050
+ },
1051
+ "OauthClient": {
1052
+ "name": "OauthClient",
1053
+ "methods": [
1054
+ "getOauthUrl",
1055
+ "onOauthCallback"
1056
+ ],
1057
+ "properties": []
1058
+ },
1059
+ "MixinProvider": {
1060
+ "name": "MixinProvider",
1061
+ "methods": [
1062
+ "canMixin",
1063
+ "getMixin",
1064
+ "releaseMixin"
1065
+ ],
1066
+ "properties": []
1067
+ },
1068
+ "HttpRequestHandler": {
1069
+ "name": "HttpRequestHandler",
1070
+ "methods": [
1071
+ "onRequest"
1072
+ ],
1073
+ "properties": []
1074
+ },
1075
+ "EngineIOHandler": {
1076
+ "name": "EngineIOHandler",
1077
+ "methods": [
1078
+ "onConnection"
1079
+ ],
1080
+ "properties": []
1081
+ },
1082
+ "PushHandler": {
1083
+ "name": "PushHandler",
1084
+ "methods": [
1085
+ "onPush"
1086
+ ],
1087
+ "properties": []
1088
+ },
1089
+ "Program": {
1090
+ "name": "Program",
1091
+ "methods": [
1092
+ "run"
1093
+ ],
1094
+ "properties": []
1095
+ },
1096
+ "Scriptable": {
1097
+ "name": "Scriptable",
1098
+ "methods": [
1099
+ "eval",
1100
+ "loadScripts",
1101
+ "saveScript"
1102
+ ],
1103
+ "properties": []
1104
+ },
1105
+ "ClusterForkInterface": {
1106
+ "name": "ClusterForkInterface",
1107
+ "methods": [
1108
+ "forkInterface"
1109
+ ],
1110
+ "properties": []
1111
+ },
1112
+ "ObjectTracker": {
1113
+ "name": "ObjectTracker",
1114
+ "methods": [
1115
+ "trackObjects"
1116
+ ],
1117
+ "properties": []
1118
+ },
1119
+ "ObjectDetector": {
1120
+ "name": "ObjectDetector",
1121
+ "methods": [
1122
+ "getDetectionInput",
1123
+ "getObjectTypes"
1124
+ ],
1125
+ "properties": []
1126
+ },
1127
+ "ObjectDetection": {
1128
+ "name": "ObjectDetection",
1129
+ "methods": [
1130
+ "detectObjects",
1131
+ "generateObjectDetections",
1132
+ "getDetectionModel"
1133
+ ],
1134
+ "properties": []
1135
+ },
1136
+ "ObjectDetectionPreview": {
1137
+ "name": "ObjectDetectionPreview",
1138
+ "methods": [],
1139
+ "properties": []
1140
+ },
1141
+ "ObjectDetectionGenerator": {
1142
+ "name": "ObjectDetectionGenerator",
1143
+ "methods": [],
1144
+ "properties": []
1145
+ },
1146
+ "HumiditySetting": {
1147
+ "name": "HumiditySetting",
1148
+ "methods": [
1149
+ "setHumidity"
1150
+ ],
1151
+ "properties": [
1152
+ "humiditySetting"
1153
+ ]
1154
+ },
1155
+ "Fan": {
1156
+ "name": "Fan",
1157
+ "methods": [
1158
+ "setFan"
1159
+ ],
1160
+ "properties": [
1161
+ "fan"
1162
+ ]
1163
+ },
1164
+ "RTCSignalingChannel": {
1165
+ "name": "RTCSignalingChannel",
1166
+ "methods": [
1167
+ "startRTCSignalingSession"
1168
+ ],
1169
+ "properties": []
1170
+ },
1171
+ "RTCSignalingClient": {
1172
+ "name": "RTCSignalingClient",
1173
+ "methods": [
1174
+ "createRTCSignalingSession"
1175
+ ],
1176
+ "properties": []
1177
+ },
1178
+ "LauncherApplication": {
1179
+ "name": "LauncherApplication",
1180
+ "methods": [],
1181
+ "properties": [
1182
+ "applicationInfo"
1183
+ ]
1184
+ },
1185
+ "ScryptedUser": {
1186
+ "name": "ScryptedUser",
1187
+ "methods": [
1188
+ "getScryptedUserAccessControl"
1189
+ ],
1190
+ "properties": []
1191
+ },
1192
+ "VideoFrameGenerator": {
1193
+ "name": "VideoFrameGenerator",
1194
+ "methods": [
1195
+ "generateVideoFrames"
1196
+ ],
1197
+ "properties": []
1198
+ },
1199
+ "StreamService": {
1200
+ "name": "StreamService",
1201
+ "methods": [
1202
+ "connectStream"
1203
+ ],
1204
+ "properties": []
1205
+ },
1206
+ "TTY": {
1207
+ "name": "TTY",
1208
+ "methods": [],
1209
+ "properties": []
1210
+ },
1211
+ "TTYSettings": {
1212
+ "name": "TTYSettings",
1213
+ "methods": [
1214
+ "getTTYSettings"
1215
+ ],
1216
+ "properties": []
1217
+ },
1218
+ "ScryptedSystemDevice": {
1219
+ "name": "ScryptedSystemDevice",
1220
+ "methods": [],
1221
+ "properties": [
1222
+ "systemDevice"
1223
+ ]
1224
+ },
1225
+ "ScryptedDeviceCreator": {
1226
+ "name": "ScryptedDeviceCreator",
1227
+ "methods": [],
1228
+ "properties": []
1229
+ },
1230
+ "ScryptedSettings": {
1231
+ "name": "ScryptedSettings",
1232
+ "methods": [],
1233
+ "properties": []
1234
+ }
1235
+ };
1236
+ /**
1237
+ * @category Core Reference
1238
+ */
1239
+ var ScryptedDeviceType;
1240
+ (function (ScryptedDeviceType) {
1241
+ /**
1242
+ * @deprecated
1243
+ */
1244
+ ScryptedDeviceType["Builtin"] = "Builtin";
1245
+ /**
1246
+ * Internal devices will not show up in device lists unless explicitly searched.
1247
+ */
1248
+ ScryptedDeviceType["Internal"] = "Internal";
1249
+ ScryptedDeviceType["Camera"] = "Camera";
1250
+ ScryptedDeviceType["Fan"] = "Fan";
1251
+ ScryptedDeviceType["Light"] = "Light";
1252
+ ScryptedDeviceType["Switch"] = "Switch";
1253
+ ScryptedDeviceType["Outlet"] = "Outlet";
1254
+ ScryptedDeviceType["Sensor"] = "Sensor";
1255
+ ScryptedDeviceType["Scene"] = "Scene";
1256
+ ScryptedDeviceType["Program"] = "Program";
1257
+ ScryptedDeviceType["Automation"] = "Automation";
1258
+ ScryptedDeviceType["Vacuum"] = "Vacuum";
1259
+ ScryptedDeviceType["Notifier"] = "Notifier";
1260
+ ScryptedDeviceType["Thermostat"] = "Thermostat";
1261
+ ScryptedDeviceType["Lock"] = "Lock";
1262
+ ScryptedDeviceType["PasswordControl"] = "PasswordControl";
1263
+ /**
1264
+ * Displays have audio and video output.
1265
+ */
1266
+ ScryptedDeviceType["Display"] = "Display";
1267
+ /**
1268
+ * Smart Displays have two way audio and video.
1269
+ */
1270
+ ScryptedDeviceType["SmartDisplay"] = "SmartDisplay";
1271
+ ScryptedDeviceType["Speaker"] = "Speaker";
1272
+ /**
1273
+ * Smart Speakers have two way audio.
1274
+ */
1275
+ ScryptedDeviceType["SmartSpeaker"] = "SmartSpeaker";
1276
+ ScryptedDeviceType["Event"] = "Event";
1277
+ ScryptedDeviceType["Entry"] = "Entry";
1278
+ ScryptedDeviceType["Garage"] = "Garage";
1279
+ ScryptedDeviceType["DeviceProvider"] = "DeviceProvider";
1280
+ ScryptedDeviceType["DataSource"] = "DataSource";
1281
+ ScryptedDeviceType["API"] = "API";
1282
+ ScryptedDeviceType["Doorbell"] = "Doorbell";
1283
+ ScryptedDeviceType["Irrigation"] = "Irrigation";
1284
+ ScryptedDeviceType["Valve"] = "Valve";
1285
+ ScryptedDeviceType["Person"] = "Person";
1286
+ ScryptedDeviceType["SecuritySystem"] = "SecuritySystem";
1287
+ ScryptedDeviceType["WindowCovering"] = "WindowCovering";
1288
+ ScryptedDeviceType["Siren"] = "Siren";
1289
+ ScryptedDeviceType["AirPurifier"] = "AirPurifier";
1290
+ ScryptedDeviceType["Internet"] = "Internet";
1291
+ ScryptedDeviceType["Network"] = "Network";
1292
+ ScryptedDeviceType["Bridge"] = "Bridge";
1293
+ ScryptedDeviceType["Unknown"] = "Unknown";
1294
+ })(ScryptedDeviceType || (exports.ScryptedDeviceType = ScryptedDeviceType = {}));
1295
+ var HumidityMode;
1296
+ (function (HumidityMode) {
1297
+ HumidityMode["Humidify"] = "Humidify";
1298
+ HumidityMode["Dehumidify"] = "Dehumidify";
1299
+ HumidityMode["Auto"] = "Auto";
1300
+ HumidityMode["Off"] = "Off";
1301
+ })(HumidityMode || (exports.HumidityMode = HumidityMode = {}));
1302
+ var FanMode;
1303
+ (function (FanMode) {
1304
+ FanMode["Auto"] = "Auto";
1305
+ FanMode["Manual"] = "Manual";
1306
+ })(FanMode || (exports.FanMode = FanMode = {}));
1307
+ var TemperatureUnit;
1308
+ (function (TemperatureUnit) {
1309
+ TemperatureUnit["C"] = "C";
1310
+ TemperatureUnit["F"] = "F";
1311
+ })(TemperatureUnit || (exports.TemperatureUnit = TemperatureUnit = {}));
1312
+ var ThermostatMode;
1313
+ (function (ThermostatMode) {
1314
+ ThermostatMode["Off"] = "Off";
1315
+ ThermostatMode["Cool"] = "Cool";
1316
+ ThermostatMode["Heat"] = "Heat";
1317
+ ThermostatMode["HeatCool"] = "HeatCool";
1318
+ ThermostatMode["Auto"] = "Auto";
1319
+ ThermostatMode["FanOnly"] = "FanOnly";
1320
+ ThermostatMode["Purifier"] = "Purifier";
1321
+ ThermostatMode["Eco"] = "Eco";
1322
+ ThermostatMode["Dry"] = "Dry";
1323
+ ThermostatMode["On"] = "On";
1324
+ })(ThermostatMode || (exports.ThermostatMode = ThermostatMode = {}));
1325
+ var PanTiltZoomMovement;
1326
+ (function (PanTiltZoomMovement) {
1327
+ PanTiltZoomMovement["Absolute"] = "Absolute";
1328
+ PanTiltZoomMovement["Relative"] = "Relative";
1329
+ PanTiltZoomMovement["Continuous"] = "Continuous";
1330
+ PanTiltZoomMovement["Preset"] = "Preset";
1331
+ PanTiltZoomMovement["Home"] = "Home";
1332
+ })(PanTiltZoomMovement || (exports.PanTiltZoomMovement = PanTiltZoomMovement = {}));
1333
+ var LockState;
1334
+ (function (LockState) {
1335
+ LockState["Locked"] = "Locked";
1336
+ LockState["Unlocked"] = "Unlocked";
1337
+ LockState["Jammed"] = "Jammed";
1338
+ })(LockState || (exports.LockState = LockState = {}));
1339
+ var ChargeState;
1340
+ (function (ChargeState) {
1341
+ ChargeState["Trickle"] = "trickle";
1342
+ ChargeState["Charging"] = "charging";
1343
+ ChargeState["NotCharging"] = "not-charging";
1344
+ })(ChargeState || (exports.ChargeState = ChargeState = {}));
1345
+ var AirPurifierStatus;
1346
+ (function (AirPurifierStatus) {
1347
+ AirPurifierStatus["Inactive"] = "Inactive";
1348
+ AirPurifierStatus["Idle"] = "Idle";
1349
+ AirPurifierStatus["Active"] = "Active";
1350
+ AirPurifierStatus["ActiveNightMode"] = "ActiveNightMode";
1351
+ })(AirPurifierStatus || (exports.AirPurifierStatus = AirPurifierStatus = {}));
1352
+ var AirPurifierMode;
1353
+ (function (AirPurifierMode) {
1354
+ AirPurifierMode["Manual"] = "Manual";
1355
+ AirPurifierMode["Automatic"] = "Automatic";
1356
+ })(AirPurifierMode || (exports.AirPurifierMode = AirPurifierMode = {}));
1357
+ var AirQuality;
1358
+ (function (AirQuality) {
1359
+ AirQuality["Unknown"] = "Unknown";
1360
+ AirQuality["Excellent"] = "Excellent";
1361
+ AirQuality["Good"] = "Good";
1362
+ AirQuality["Fair"] = "Fair";
1363
+ AirQuality["Inferior"] = "Inferior";
1364
+ AirQuality["Poor"] = "Poor";
1365
+ })(AirQuality || (exports.AirQuality = AirQuality = {}));
1366
+ var SecuritySystemMode;
1367
+ (function (SecuritySystemMode) {
1368
+ SecuritySystemMode["Disarmed"] = "Disarmed";
1369
+ SecuritySystemMode["HomeArmed"] = "HomeArmed";
1370
+ SecuritySystemMode["AwayArmed"] = "AwayArmed";
1371
+ SecuritySystemMode["NightArmed"] = "NightArmed";
1372
+ })(SecuritySystemMode || (exports.SecuritySystemMode = SecuritySystemMode = {}));
1373
+ var SecuritySystemObstruction;
1374
+ (function (SecuritySystemObstruction) {
1375
+ SecuritySystemObstruction["Sensor"] = "Sensor";
1376
+ SecuritySystemObstruction["Occupied"] = "Occupied";
1377
+ SecuritySystemObstruction["Time"] = "Time";
1378
+ SecuritySystemObstruction["Error"] = "Error";
1379
+ })(SecuritySystemObstruction || (exports.SecuritySystemObstruction = SecuritySystemObstruction = {}));
1380
+ var MediaPlayerState;
1381
+ (function (MediaPlayerState) {
1382
+ MediaPlayerState["Idle"] = "Idle";
1383
+ MediaPlayerState["Playing"] = "Playing";
1384
+ MediaPlayerState["Paused"] = "Paused";
1385
+ MediaPlayerState["Buffering"] = "Buffering";
1386
+ })(MediaPlayerState || (exports.MediaPlayerState = MediaPlayerState = {}));
1387
+ var ScryptedInterface;
1388
+ (function (ScryptedInterface) {
1389
+ ScryptedInterface["ScryptedDevice"] = "ScryptedDevice";
1390
+ ScryptedInterface["ScryptedPlugin"] = "ScryptedPlugin";
1391
+ ScryptedInterface["ScryptedPluginRuntime"] = "ScryptedPluginRuntime";
1392
+ ScryptedInterface["OnOff"] = "OnOff";
1393
+ ScryptedInterface["Brightness"] = "Brightness";
1394
+ ScryptedInterface["ColorSettingTemperature"] = "ColorSettingTemperature";
1395
+ ScryptedInterface["ColorSettingRgb"] = "ColorSettingRgb";
1396
+ ScryptedInterface["ColorSettingHsv"] = "ColorSettingHsv";
1397
+ ScryptedInterface["Buttons"] = "Buttons";
1398
+ ScryptedInterface["PressButtons"] = "PressButtons";
1399
+ ScryptedInterface["Sensors"] = "Sensors";
1400
+ ScryptedInterface["Notifier"] = "Notifier";
1401
+ ScryptedInterface["StartStop"] = "StartStop";
1402
+ ScryptedInterface["Pause"] = "Pause";
1403
+ ScryptedInterface["Dock"] = "Dock";
1404
+ ScryptedInterface["TemperatureSetting"] = "TemperatureSetting";
1405
+ ScryptedInterface["Thermometer"] = "Thermometer";
1406
+ ScryptedInterface["HumiditySensor"] = "HumiditySensor";
1407
+ ScryptedInterface["Camera"] = "Camera";
1408
+ ScryptedInterface["Microphone"] = "Microphone";
1409
+ ScryptedInterface["AudioVolumeControl"] = "AudioVolumeControl";
1410
+ ScryptedInterface["Display"] = "Display";
1411
+ ScryptedInterface["VideoCamera"] = "VideoCamera";
1412
+ ScryptedInterface["VideoCameraMask"] = "VideoCameraMask";
1413
+ ScryptedInterface["VideoTextOverlays"] = "VideoTextOverlays";
1414
+ ScryptedInterface["VideoRecorder"] = "VideoRecorder";
1415
+ ScryptedInterface["VideoRecorderManagement"] = "VideoRecorderManagement";
1416
+ ScryptedInterface["PanTiltZoom"] = "PanTiltZoom";
1417
+ ScryptedInterface["EventRecorder"] = "EventRecorder";
1418
+ ScryptedInterface["VideoClips"] = "VideoClips";
1419
+ ScryptedInterface["VideoCameraConfiguration"] = "VideoCameraConfiguration";
1420
+ ScryptedInterface["Intercom"] = "Intercom";
1421
+ ScryptedInterface["Lock"] = "Lock";
1422
+ ScryptedInterface["PasswordStore"] = "PasswordStore";
1423
+ ScryptedInterface["Scene"] = "Scene";
1424
+ ScryptedInterface["Entry"] = "Entry";
1425
+ ScryptedInterface["EntrySensor"] = "EntrySensor";
1426
+ ScryptedInterface["DeviceProvider"] = "DeviceProvider";
1427
+ ScryptedInterface["DeviceDiscovery"] = "DeviceDiscovery";
1428
+ ScryptedInterface["DeviceCreator"] = "DeviceCreator";
1429
+ ScryptedInterface["Battery"] = "Battery";
1430
+ ScryptedInterface["Charger"] = "Charger";
1431
+ ScryptedInterface["Reboot"] = "Reboot";
1432
+ ScryptedInterface["Refresh"] = "Refresh";
1433
+ ScryptedInterface["MediaPlayer"] = "MediaPlayer";
1434
+ ScryptedInterface["Online"] = "Online";
1435
+ ScryptedInterface["BufferConverter"] = "BufferConverter";
1436
+ ScryptedInterface["MediaConverter"] = "MediaConverter";
1437
+ ScryptedInterface["Settings"] = "Settings";
1438
+ ScryptedInterface["BinarySensor"] = "BinarySensor";
1439
+ ScryptedInterface["TamperSensor"] = "TamperSensor";
1440
+ ScryptedInterface["Sleep"] = "Sleep";
1441
+ ScryptedInterface["PowerSensor"] = "PowerSensor";
1442
+ ScryptedInterface["AudioSensor"] = "AudioSensor";
1443
+ ScryptedInterface["MotionSensor"] = "MotionSensor";
1444
+ ScryptedInterface["AmbientLightSensor"] = "AmbientLightSensor";
1445
+ ScryptedInterface["OccupancySensor"] = "OccupancySensor";
1446
+ ScryptedInterface["FloodSensor"] = "FloodSensor";
1447
+ ScryptedInterface["UltravioletSensor"] = "UltravioletSensor";
1448
+ ScryptedInterface["LuminanceSensor"] = "LuminanceSensor";
1449
+ ScryptedInterface["PositionSensor"] = "PositionSensor";
1450
+ ScryptedInterface["SecuritySystem"] = "SecuritySystem";
1451
+ ScryptedInterface["PM10Sensor"] = "PM10Sensor";
1452
+ ScryptedInterface["PM25Sensor"] = "PM25Sensor";
1453
+ ScryptedInterface["VOCSensor"] = "VOCSensor";
1454
+ ScryptedInterface["NOXSensor"] = "NOXSensor";
1455
+ ScryptedInterface["CO2Sensor"] = "CO2Sensor";
1456
+ ScryptedInterface["AirQualitySensor"] = "AirQualitySensor";
1457
+ ScryptedInterface["AirPurifier"] = "AirPurifier";
1458
+ ScryptedInterface["FilterMaintenance"] = "FilterMaintenance";
1459
+ ScryptedInterface["Readme"] = "Readme";
1460
+ ScryptedInterface["OauthClient"] = "OauthClient";
1461
+ ScryptedInterface["MixinProvider"] = "MixinProvider";
1462
+ ScryptedInterface["HttpRequestHandler"] = "HttpRequestHandler";
1463
+ ScryptedInterface["EngineIOHandler"] = "EngineIOHandler";
1464
+ ScryptedInterface["PushHandler"] = "PushHandler";
1465
+ ScryptedInterface["Program"] = "Program";
1466
+ ScryptedInterface["Scriptable"] = "Scriptable";
1467
+ ScryptedInterface["ClusterForkInterface"] = "ClusterForkInterface";
1468
+ ScryptedInterface["ObjectTracker"] = "ObjectTracker";
1469
+ ScryptedInterface["ObjectDetector"] = "ObjectDetector";
1470
+ ScryptedInterface["ObjectDetection"] = "ObjectDetection";
1471
+ ScryptedInterface["ObjectDetectionPreview"] = "ObjectDetectionPreview";
1472
+ ScryptedInterface["ObjectDetectionGenerator"] = "ObjectDetectionGenerator";
1473
+ ScryptedInterface["HumiditySetting"] = "HumiditySetting";
1474
+ ScryptedInterface["Fan"] = "Fan";
1475
+ ScryptedInterface["RTCSignalingChannel"] = "RTCSignalingChannel";
1476
+ ScryptedInterface["RTCSignalingClient"] = "RTCSignalingClient";
1477
+ ScryptedInterface["LauncherApplication"] = "LauncherApplication";
1478
+ ScryptedInterface["ScryptedUser"] = "ScryptedUser";
1479
+ ScryptedInterface["VideoFrameGenerator"] = "VideoFrameGenerator";
1480
+ ScryptedInterface["StreamService"] = "StreamService";
1481
+ ScryptedInterface["TTY"] = "TTY";
1482
+ ScryptedInterface["TTYSettings"] = "TTYSettings";
1483
+ ScryptedInterface["ScryptedSystemDevice"] = "ScryptedSystemDevice";
1484
+ ScryptedInterface["ScryptedDeviceCreator"] = "ScryptedDeviceCreator";
1485
+ ScryptedInterface["ScryptedSettings"] = "ScryptedSettings";
1486
+ })(ScryptedInterface || (exports.ScryptedInterface = ScryptedInterface = {}));
1487
+ var ScryptedMimeTypes;
1488
+ (function (ScryptedMimeTypes) {
1489
+ ScryptedMimeTypes["Url"] = "text/x-uri";
1490
+ ScryptedMimeTypes["InsecureLocalUrl"] = "text/x-insecure-local-uri";
1491
+ ScryptedMimeTypes["LocalUrl"] = "text/x-local-uri";
1492
+ ScryptedMimeTypes["ServerId"] = "text/x-server-id";
1493
+ ScryptedMimeTypes["PushEndpoint"] = "text/x-push-endpoint";
1494
+ ScryptedMimeTypes["SchemePrefix"] = "x-scrypted/x-scrypted-scheme-";
1495
+ ScryptedMimeTypes["MediaStreamUrl"] = "text/x-media-url";
1496
+ ScryptedMimeTypes["MediaObject"] = "x-scrypted/x-scrypted-media-object";
1497
+ ScryptedMimeTypes["RequestMediaObject"] = "x-scrypted/x-scrypted-request-media-object";
1498
+ ScryptedMimeTypes["RequestMediaStream"] = "x-scrypted/x-scrypted-request-stream";
1499
+ ScryptedMimeTypes["MediaStreamFeedback"] = "x-scrypted/x-media-stream-feedback";
1500
+ ScryptedMimeTypes["FFmpegInput"] = "x-scrypted/x-ffmpeg-input";
1501
+ ScryptedMimeTypes["FFmpegTranscodeStream"] = "x-scrypted/x-ffmpeg-transcode-stream";
1502
+ ScryptedMimeTypes["RTCSignalingChannel"] = "x-scrypted/x-scrypted-rtc-signaling-channel";
1503
+ ScryptedMimeTypes["RTCSignalingSession"] = "x-scrypted/x-scrypted-rtc-signaling-session";
1504
+ ScryptedMimeTypes["RTCConnectionManagement"] = "x-scrypted/x-scrypted-rtc-connection-management";
1505
+ ScryptedMimeTypes["Image"] = "x-scrypted/x-scrypted-image";
1506
+ })(ScryptedMimeTypes || (exports.ScryptedMimeTypes = ScryptedMimeTypes = {}));
1507
+ //# sourceMappingURL=index.js.map
1508
+
1509
+ /***/ },
1510
+
1511
+ /***/ "./src/main.ts"
1512
+ /*!*********************!*\
1513
+ !*** ./src/main.ts ***!
1514
+ \*********************/
1515
+ (__unused_webpack_module, exports, __webpack_require__) {
1516
+
1517
+ "use strict";
1518
+
1519
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
1520
+ if (k2 === undefined) k2 = k;
1521
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1522
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1523
+ desc = { enumerable: true, get: function() { return m[k]; } };
1524
+ }
1525
+ Object.defineProperty(o, k2, desc);
1526
+ }) : (function(o, m, k, k2) {
1527
+ if (k2 === undefined) k2 = k;
1528
+ o[k2] = m[k];
1529
+ }));
1530
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
1531
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1532
+ }) : function(o, v) {
1533
+ o["default"] = v;
1534
+ });
1535
+ var __importStar = (this && this.__importStar) || (function () {
1536
+ var ownKeys = function(o) {
1537
+ ownKeys = Object.getOwnPropertyNames || function (o) {
1538
+ var ar = [];
1539
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
1540
+ return ar;
1541
+ };
1542
+ return ownKeys(o);
1543
+ };
1544
+ return function (mod) {
1545
+ if (mod && mod.__esModule) return mod;
1546
+ var result = {};
1547
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1548
+ __setModuleDefault(result, mod);
1549
+ return result;
1550
+ };
1551
+ })();
1552
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1553
+ const sdk_1 = __importStar(__webpack_require__(/*! @scrypted/sdk */ "./node_modules/@scrypted/sdk/dist/src/index.js"));
1554
+ const { systemManager, deviceManager, mediaManager } = sdk_1.default;
1555
+ // ─── Constants ────────────────────────────────────────────────────────────────
1556
+ const LABELS = ['person', 'animal', 'face', 'vehicle', 'plate', 'package', 'discard'];
1557
+ const CAPTURE_CLASSES = new Set(['person', 'cat', 'dog', 'animal', 'bird', 'face', 'vehicle', 'car', 'truck', 'bus', 'motorcycle', 'bicycle', 'plate', 'package']);
1558
+ const RATE_OPTIONS = ['disabled', '1 per minute', '1 per 10 seconds', 'every detection'];
1559
+ const RATE_MS = {
1560
+ 'disabled': Infinity,
1561
+ '1 per minute': 60_000,
1562
+ '1 per 10 seconds': 10_000,
1563
+ 'every detection': 0,
1564
+ };
1565
+ const MAX_CAPTURES = 2000;
1566
+ // ─── Plugin ───────────────────────────────────────────────────────────────────
1567
+ class DetectionTrainer extends sdk_1.ScryptedDeviceBase {
1568
+ constructor(nativeId) {
1569
+ super(nativeId);
1570
+ // Map<cameraId, lastCaptureTimestamp>
1571
+ this.lastCapture = new Map();
1572
+ // Map<captureId, CaptureRecord>
1573
+ this.captures = new Map();
1574
+ // Map<captureId, jpegBuffer>
1575
+ this.images = new Map();
1576
+ // Active event listeners
1577
+ this.listeners = [];
1578
+ this.loadState();
1579
+ this.registerListeners();
1580
+ }
1581
+ // ── Persistence ──────────────────────────────────────────────────────────
1582
+ loadState() {
1583
+ try {
1584
+ const raw = this.storage.getItem('captures');
1585
+ if (raw) {
1586
+ const arr = JSON.parse(raw);
1587
+ for (const r of arr)
1588
+ this.captures.set(r.id, r);
1589
+ this.console.log(`Loaded ${this.captures.size} captures from storage.`);
1590
+ }
1591
+ }
1592
+ catch (e) {
1593
+ this.console.warn('Could not load captures from storage:', e);
1594
+ }
1595
+ // images are stored as individual items
1596
+ for (const [id] of this.captures) {
1597
+ const raw = this.storage.getItem(`img:${id}`);
1598
+ if (raw)
1599
+ this.images.set(id, Buffer.from(raw, 'base64'));
1600
+ }
1601
+ }
1602
+ saveCaptures() {
1603
+ this.storage.setItem('captures', JSON.stringify([...this.captures.values()]));
1604
+ }
1605
+ saveImage(id, buf) {
1606
+ this.storage.setItem(`img:${id}`, buf.toString('base64'));
1607
+ }
1608
+ deleteCapture(id) {
1609
+ const old = this.storage.getItem(`img:${id}`);
1610
+ if (old)
1611
+ this.storage.removeItem(`img:${id}`);
1612
+ this.captures.delete(id);
1613
+ this.images.delete(id);
1614
+ this.saveCaptures();
1615
+ }
1616
+ // ── Settings ─────────────────────────────────────────────────────────────
1617
+ async getSettings() {
1618
+ const cameras = Object.keys(systemManager.getSystemState())
1619
+ .map(id => systemManager.getDeviceById(id))
1620
+ .filter(d => d &&
1621
+ (d.type === sdk_1.ScryptedDeviceType.Camera || d.type === sdk_1.ScryptedDeviceType.Doorbell) &&
1622
+ d.interfaces?.includes(sdk_1.ScryptedInterface.ObjectDetector));
1623
+ const settings = [
1624
+ {
1625
+ key: 'info',
1626
+ title: 'Detection Trainer',
1627
+ description: `${this.captures.size} captures stored (${[...this.captures.values()].filter(c => !c.reviewed).length} pending review, ${[...this.captures.values()].filter(c => c.reviewed && c.label !== 'discard').length} labeled). Open the web UI to review and export.`,
1628
+ readonly: true,
1629
+ value: '',
1630
+ },
1631
+ {
1632
+ key: 'open_ui',
1633
+ title: 'Open Review UI',
1634
+ description: 'Open the detection review and labeling interface.',
1635
+ type: 'button',
1636
+ },
1637
+ ];
1638
+ for (const cam of cameras) {
1639
+ const key = `rate:${cam.id}`;
1640
+ settings.push({
1641
+ key,
1642
+ title: cam.name,
1643
+ group: 'Capture Rate per Camera',
1644
+ description: 'How often to capture detections from this camera.',
1645
+ value: this.storage.getItem(key) || '1 per minute',
1646
+ choices: [...RATE_OPTIONS],
1647
+ });
1648
+ }
1649
+ return settings;
1650
+ }
1651
+ async putSetting(key, value) {
1652
+ if (key === 'open_ui')
1653
+ return;
1654
+ this.storage.setItem(key, value);
1655
+ if (key.startsWith('rate:')) {
1656
+ // Re-register listeners when rates change
1657
+ this.registerListeners();
1658
+ }
1659
+ }
1660
+ // ── Listeners ─────────────────────────────────────────────────────────────
1661
+ registerListeners() {
1662
+ // Remove old listeners
1663
+ for (const remove of this.listeners)
1664
+ remove();
1665
+ this.listeners = [];
1666
+ const cameras = Object.keys(systemManager.getSystemState())
1667
+ .map(id => systemManager.getDeviceById(id))
1668
+ .filter(d => d &&
1669
+ (d.type === sdk_1.ScryptedDeviceType.Camera || d.type === sdk_1.ScryptedDeviceType.Doorbell) &&
1670
+ d.interfaces?.includes(sdk_1.ScryptedInterface.ObjectDetector));
1671
+ for (const cam of cameras) {
1672
+ const rateKey = `rate:${cam.id}`;
1673
+ const rateLabel = (this.storage.getItem(rateKey) || '1 per minute');
1674
+ if (rateLabel === 'disabled')
1675
+ continue;
1676
+ const listener = cam.listen(sdk_1.ScryptedInterface.ObjectDetector, async (source, details, data) => {
1677
+ await this.onDetection(cam.id, cam.name, data, RATE_MS[rateLabel]);
1678
+ });
1679
+ this.listeners.push(() => listener.removeListener());
1680
+ }
1681
+ this.console.log(`Listening to ${this.listeners.length} camera(s).`);
1682
+ }
1683
+ // ── Detection Handler ─────────────────────────────────────────────────────
1684
+ async onDetection(cameraId, cameraName, data, rateLimitMs) {
1685
+ if (!data?.detections?.length || !data.inputDimensions)
1686
+ return;
1687
+ // Rate limit per camera
1688
+ const now = Date.now();
1689
+ const last = this.lastCapture.get(cameraId) || 0;
1690
+ if (now - last < rateLimitMs)
1691
+ return;
1692
+ // Filter to target classes
1693
+ const targets = data.detections.filter(d => d.className && CAPTURE_CLASSES.has(d.className.toLowerCase()) && d.boundingBox);
1694
+ if (!targets.length)
1695
+ return;
1696
+ // Pick the highest-confidence target detection
1697
+ const best = targets.sort((a, b) => (b.score || 0) - (a.score || 0))[0];
1698
+ // Enforce max storage
1699
+ if (this.captures.size >= MAX_CAPTURES) {
1700
+ // Evict oldest unreviewed capture
1701
+ const oldest = [...this.captures.values()]
1702
+ .filter(c => !c.reviewed)
1703
+ .sort((a, b) => a.timestamp - b.timestamp)[0];
1704
+ if (oldest)
1705
+ this.deleteCapture(oldest.id);
1706
+ else
1707
+ return; // All reviewed, don't evict labeled data
1708
+ }
1709
+ this.lastCapture.set(cameraId, now);
1710
+ // Try to get the detection image
1711
+ let jpeg;
1712
+ try {
1713
+ if (data.detectionId) {
1714
+ const cam = systemManager.getDeviceById(cameraId);
1715
+ const mo = await cam.getDetectionInput(data.detectionId);
1716
+ jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');
1717
+ }
1718
+ }
1719
+ catch (e) {
1720
+ this.console.warn(`Could not get detection image for ${cameraName}:`, e);
1721
+ }
1722
+ if (!jpeg)
1723
+ return; // Skip if no image
1724
+ const id = `${now}-${Math.random().toString(36).slice(2, 8)}`;
1725
+ const record = {
1726
+ id,
1727
+ cameraId,
1728
+ cameraName,
1729
+ timestamp: now,
1730
+ detectedClass: best.className,
1731
+ score: best.score || 0,
1732
+ boundingBox: best.boundingBox,
1733
+ inputDimensions: data.inputDimensions,
1734
+ detectionId: data.detectionId,
1735
+ reviewed: false,
1736
+ };
1737
+ this.captures.set(id, record);
1738
+ this.images.set(id, jpeg);
1739
+ this.saveImage(id, jpeg);
1740
+ this.saveCaptures();
1741
+ this.console.log(`Captured ${best.className} (${Math.round((best.score || 0) * 100)}%) from ${cameraName} [${this.captures.size} total]`);
1742
+ }
1743
+ // ── HTTP Handler ──────────────────────────────────────────────────────────
1744
+ async onRequest(request, response) {
1745
+ const url = new URL(request.url, 'http://localhost');
1746
+ const path = url.pathname.replace(request.rootPath, '');
1747
+ // Serve image
1748
+ if (path.startsWith('/img/')) {
1749
+ const id = path.slice(5);
1750
+ const img = this.images.get(id);
1751
+ if (!img)
1752
+ return response.send('Not found', { code: 404 });
1753
+ return response.send(img, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=3600' } });
1754
+ }
1755
+ // API: label a capture
1756
+ if (path === '/api/label' && request.body) {
1757
+ const rawBody = request.body;
1758
+ const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));
1759
+ const record = this.captures.get(body.id);
1760
+ if (!record)
1761
+ return response.send('Not found', { code: 404 });
1762
+ record.label = body.label;
1763
+ record.reviewed = true;
1764
+ if (body.label === 'discard') {
1765
+ this.deleteCapture(body.id);
1766
+ }
1767
+ else {
1768
+ this.captures.set(body.id, record);
1769
+ this.saveCaptures();
1770
+ }
1771
+ return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });
1772
+ }
1773
+ // API: get pending captures
1774
+ if (path === '/api/pending') {
1775
+ const pending = [...this.captures.values()]
1776
+ .filter(c => !c.reviewed)
1777
+ .sort((a, b) => b.timestamp - a.timestamp)
1778
+ .slice(0, 50);
1779
+ return response.send(JSON.stringify(pending), { headers: { 'Content-Type': 'application/json' } });
1780
+ }
1781
+ // API: stats
1782
+ if (path === '/api/stats') {
1783
+ const all = [...this.captures.values()];
1784
+ const stats = {
1785
+ total: all.length,
1786
+ pending: all.filter(c => !c.reviewed).length,
1787
+ labeled: all.filter(c => c.reviewed && c.label !== 'discard').length,
1788
+ byLabel: {},
1789
+ byCamera: {},
1790
+ byDetectedClass: {},
1791
+ };
1792
+ for (const r of all) {
1793
+ if (r.label)
1794
+ stats.byLabel[r.label] = (stats.byLabel[r.label] || 0) + 1;
1795
+ stats.byCamera[r.cameraName] = (stats.byCamera[r.cameraName] || 0) + 1;
1796
+ stats.byDetectedClass[r.detectedClass] = (stats.byDetectedClass[r.detectedClass] || 0) + 1;
1797
+ }
1798
+ return response.send(JSON.stringify(stats), { headers: { 'Content-Type': 'application/json' } });
1799
+ }
1800
+ // API: export YOLO dataset
1801
+ if (path === '/api/export') {
1802
+ const labeled = [...this.captures.values()].filter(c => c.reviewed && c.label && c.label !== 'discard');
1803
+ if (!labeled.length)
1804
+ return response.send(JSON.stringify({ error: 'No labeled data yet' }), { headers: { 'Content-Type': 'application/json' }, code: 400 });
1805
+ const classMap = {
1806
+ person: 0, animal: 1, face: 2, vehicle: 3, plate: 4, package: 5, discard: -1,
1807
+ };
1808
+ // Build a simple tarball-like structure as JSON for download
1809
+ const dataset = [];
1810
+ for (const record of labeled) {
1811
+ const img = this.images.get(record.id);
1812
+ if (!img)
1813
+ continue;
1814
+ const fname = `${record.id}`;
1815
+ dataset.push({ filename: `images/${fname}.jpg`, content: img.toString('base64'), encoding: 'base64' });
1816
+ const [x, y, w, h] = record.boundingBox;
1817
+ const [imgW, imgH] = record.inputDimensions;
1818
+ const cx = (x + w / 2) / imgW;
1819
+ const cy = (y + h / 2) / imgH;
1820
+ const nw = w / imgW;
1821
+ const nh = h / imgH;
1822
+ const classId = classMap[record.label];
1823
+ const labelLine = `${classId} ${cx.toFixed(6)} ${cy.toFixed(6)} ${nw.toFixed(6)} ${nh.toFixed(6)}\n`;
1824
+ dataset.push({ filename: `labels/${fname}.txt`, content: labelLine, encoding: 'utf8' });
1825
+ }
1826
+ const yaml = [
1827
+ 'path: dataset',
1828
+ 'train: images',
1829
+ 'val: images',
1830
+ '',
1831
+ 'nc: 6',
1832
+ "names: ['person', 'animal', 'face', 'vehicle', 'plate', 'package']",
1833
+ '',
1834
+ '# Generated by Scrypted Detection Trainer',
1835
+ `# ${labeled.length} labeled samples`,
1836
+ ].join('\n');
1837
+ dataset.push({ filename: 'data.yaml', content: yaml, encoding: 'utf8' });
1838
+ return response.send(JSON.stringify({ files: dataset, count: labeled.length }), {
1839
+ headers: { 'Content-Type': 'application/json' },
1840
+ });
1841
+ }
1842
+ // Serve Web UI
1843
+ if (path === '/' || path === '' || path === '/index.html') {
1844
+ return response.send(this.renderUI(), { headers: { 'Content-Type': 'text/html' } });
1845
+ }
1846
+ response.send('Not found', { code: 404 });
1847
+ }
1848
+ // ── Web UI ────────────────────────────────────────────────────────────────
1849
+ renderUI() {
1850
+ return `<!DOCTYPE html>
1851
+ <html lang="en">
1852
+ <head>
1853
+ <meta charset="UTF-8">
1854
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1855
+ <title>Detection Trainer</title>
1856
+ <style>
1857
+ * { box-sizing: border-box; margin: 0; padding: 0; }
1858
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f0f0f; color: #e8e8e8; min-height: 100vh; }
1859
+ header { background: #1a1a1a; border-bottom: 1px solid #333; padding: 16px 24px; display: flex; align-items: center; justify-content: space-between; }
1860
+ header h1 { font-size: 18px; font-weight: 600; color: #fff; }
1861
+ .stats { display: flex; gap: 20px; font-size: 13px; color: #aaa; }
1862
+ .stat span { color: #fff; font-weight: 600; }
1863
+ .container { max-width: 1000px; margin: 0 auto; padding: 24px; }
1864
+ .card { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 12px; overflow: hidden; margin-bottom: 24px; }
1865
+ .card-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; display: flex; align-items: center; justify-content: space-between; }
1866
+ .card-header h2 { font-size: 15px; font-weight: 600; }
1867
+ .badge { background: #333; color: #aaa; font-size: 12px; padding: 2px 8px; border-radius: 20px; }
1868
+ .badge.orange { background: #3d2a00; color: #f90; }
1869
+ .badge.green { background: #0d2d0d; color: #4c4; }
1870
+
1871
+ /* Detection card */
1872
+ .detection { display: grid; grid-template-columns: 200px 1fr; gap: 0; border-bottom: 1px solid #222; }
1873
+ .detection:last-child { border-bottom: none; }
1874
+ .detection-img { position: relative; background: #111; display: flex; align-items: center; justify-content: center; min-height: 150px; }
1875
+ .detection-img img { width: 100%; height: 150px; object-fit: cover; display: block; }
1876
+ .detection-class { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,0.7); color: #fff; font-size: 11px; padding: 2px 6px; border-radius: 4px; }
1877
+ .detection-info { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
1878
+ .detection-meta { font-size: 12px; color: #888; display: flex; flex-wrap: wrap; gap: 10px; }
1879
+ .detection-meta strong { color: #ccc; }
1880
+ .label-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
1881
+ .label-btn { padding: 7px 14px; border-radius: 8px; border: 1px solid #444; background: #222; color: #ccc; cursor: pointer; font-size: 13px; transition: all .15s; }
1882
+ .label-btn:hover { border-color: #666; background: #2a2a2a; color: #fff; }
1883
+ .label-btn.person { border-color: #2a6; color: #4d9; }
1884
+ .label-btn.person:hover { background: #0d2a1a; }
1885
+ .label-btn.animal { border-color: #a63; color: #d85; }
1886
+ .label-btn.animal:hover { background: #2a1a0d; }
1887
+ .label-btn.face { border-color: #49c; color: #6be; }
1888
+ .label-btn.face:hover { background: #0d1a2a; }
1889
+ .label-btn.vehicle { border-color: #76b; color: #99d; }
1890
+ .label-btn.vehicle:hover { background: #1a1a2a; }
1891
+ .label-btn.discard { border-color: #622; color: #a44; }
1892
+ .label-btn.discard:hover { background: #2a0d0d; }
1893
+ .detection.labeled { opacity: 0.4; pointer-events: none; }
1894
+ .labeled-tag { font-size: 11px; color: #4d9; background: #0d2a1a; border: 1px solid #2a6; padding: 2px 8px; border-radius: 4px; }
1895
+
1896
+ /* Empty state */
1897
+ .empty { padding: 48px; text-align: center; color: #555; }
1898
+ .empty .icon { font-size: 48px; margin-bottom: 12px; }
1899
+
1900
+ /* Export section */
1901
+ .export-btn { padding: 10px 20px; background: #1a4d8a; border: none; border-radius: 8px; color: #fff; cursor: pointer; font-size: 14px; font-weight: 500; }
1902
+ .export-btn:hover { background: #1e5ca0; }
1903
+ .export-btn:disabled { background: #333; color: #666; cursor: not-allowed; }
1904
+ .export-info { font-size: 13px; color: #888; padding: 12px 20px; }
1905
+
1906
+ /* Progress bar */
1907
+ .progress { height: 4px; background: #222; border-radius: 2px; overflow: hidden; margin-top: 8px; }
1908
+ .progress-bar { height: 100%; background: #1a6; border-radius: 2px; transition: width .3s; }
1909
+
1910
+ .toast { position: fixed; bottom: 24px; right: 24px; background: #1a3; color: #fff; padding: 10px 18px; border-radius: 8px; font-size: 13px; opacity: 0; transition: opacity .3s; pointer-events: none; }
1911
+ .toast.show { opacity: 1; }
1912
+
1913
+ .tab-bar { display: flex; gap: 2px; padding: 12px 20px 0; border-bottom: 1px solid #2a2a2a; }
1914
+ .tab { padding: 8px 14px; font-size: 13px; color: #888; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
1915
+ .tab.active { color: #fff; border-bottom-color: #4a9; }
1916
+ .tab-content { padding: 20px; }
1917
+ .tab-panel { display: none; }
1918
+ .tab-panel.active { display: block; }
1919
+
1920
+ .breakdown-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
1921
+ .breakdown-item { background: #222; border-radius: 8px; padding: 12px; }
1922
+ .breakdown-item .label { font-size: 12px; color: #888; margin-bottom: 4px; }
1923
+ .breakdown-item .value { font-size: 20px; font-weight: 600; color: #fff; }
1924
+ </style>
1925
+ </head>
1926
+ <body>
1927
+ <header>
1928
+ <h1>🎯 Detection Trainer</h1>
1929
+ <div class="stats">
1930
+ <div>Pending <span id="stat-pending">—</span></div>
1931
+ <div>Labeled <span id="stat-labeled">—</span></div>
1932
+ <div>Total <span id="stat-total">—</span></div>
1933
+ </div>
1934
+ </header>
1935
+ <div class="container">
1936
+
1937
+ <div class="card">
1938
+ <div class="tab-bar">
1939
+ <div class="tab active" onclick="showTab('review')">Review</div>
1940
+ <div class="tab" onclick="showTab('stats')">Stats</div>
1941
+ <div class="tab" onclick="showTab('export')">Export Dataset</div>
1942
+ </div>
1943
+
1944
+ <!-- Review tab -->
1945
+ <div class="tab-panel active" id="tab-review">
1946
+ <div id="detections-list"></div>
1947
+ </div>
1948
+
1949
+ <!-- Stats tab -->
1950
+ <div class="tab-panel" id="tab-stats">
1951
+ <div class="tab-content">
1952
+ <p style="font-size:13px;color:#888;margin-bottom:16px;">Breakdown of captured and labeled detections.</p>
1953
+ <h3 style="font-size:13px;color:#aaa;margin-bottom:10px;">By Detected Class (what the model said)</h3>
1954
+ <div class="breakdown-grid" id="stats-detected"></div>
1955
+ <h3 style="font-size:13px;color:#aaa;margin:20px 0 10px;">By Corrected Label (what you said)</h3>
1956
+ <div class="breakdown-grid" id="stats-label"></div>
1957
+ <h3 style="font-size:13px;color:#aaa;margin:20px 0 10px;">By Camera</h3>
1958
+ <div class="breakdown-grid" id="stats-camera"></div>
1959
+ </div>
1960
+ </div>
1961
+
1962
+ <!-- Export tab -->
1963
+ <div class="tab-panel" id="tab-export">
1964
+ <div class="tab-content">
1965
+ <p style="font-size:13px;color:#888;margin-bottom:16px;">
1966
+ Exports a YOLO-format dataset (images + labels + data.yaml) as a downloadable bundle.
1967
+ Only labeled detections are included. Review more detections first to build a larger dataset.
1968
+ </p>
1969
+ <div id="export-stats" class="export-info">Loading…</div>
1970
+ <div style="display:flex;gap:12px;align-items:center;margin-top:12px;">
1971
+ <button class="export-btn" id="export-btn" onclick="exportDataset()">Download Dataset</button>
1972
+ <span id="export-status" style="font-size:13px;color:#888;"></span>
1973
+ </div>
1974
+ </div>
1975
+ </div>
1976
+ </div>
1977
+ </div>
1978
+
1979
+ <div class="toast" id="toast"></div>
1980
+
1981
+ <script>
1982
+ const BASE = location.pathname.replace(/\\/$/, '');
1983
+ let pending = [];
1984
+ let labeledCount = 0;
1985
+
1986
+ function showTab(name) {
1987
+ document.querySelectorAll('.tab').forEach((t, i) => {
1988
+ const names = ['review', 'stats', 'export'];
1989
+ t.classList.toggle('active', names[i] === name);
1990
+ });
1991
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
1992
+ document.getElementById('tab-' + name).classList.add('active');
1993
+ if (name === 'stats') loadStats();
1994
+ if (name === 'export') loadExportInfo();
1995
+ }
1996
+
1997
+ function toast(msg, color='#1a3') {
1998
+ const el = document.getElementById('toast');
1999
+ el.textContent = msg;
2000
+ el.style.background = color;
2001
+ el.classList.add('show');
2002
+ setTimeout(() => el.classList.remove('show'), 2500);
2003
+ }
2004
+
2005
+ async function loadPending() {
2006
+ const res = await fetch(BASE + '/api/pending');
2007
+ pending = await res.json();
2008
+
2009
+ const statsRes = await fetch(BASE + '/api/stats');
2010
+ const stats = await statsRes.json();
2011
+ document.getElementById('stat-pending').textContent = stats.pending;
2012
+ document.getElementById('stat-labeled').textContent = stats.labeled;
2013
+ document.getElementById('stat-total').textContent = stats.total;
2014
+
2015
+ const list = document.getElementById('detections-list');
2016
+ if (!pending.length) {
2017
+ list.innerHTML = '<div class="empty"><div class="icon">✅</div><div>No pending detections to review.<br><span style="font-size:12px;color:#444">Captures will appear here as cameras detect objects.</span></div></div>';
2018
+ return;
2019
+ }
2020
+
2021
+ list.innerHTML = pending.map(r => {
2022
+ const date = new Date(r.timestamp).toLocaleString();
2023
+ const score = Math.round(r.score * 100);
2024
+ return \`
2025
+ <div class="detection" id="det-\${r.id}">
2026
+ <div class="detection-img">
2027
+ <img src="\${BASE}/img/\${r.id}" alt="\${r.detectedClass}" loading="lazy" onerror="this.parentElement.innerHTML='<div style=\\"padding:20px;color:#555;font-size:12px;text-align:center\\">Image unavailable</div>'">
2028
+ <div class="detection-class">\${r.detectedClass} \${score}%</div>
2029
+ </div>
2030
+ <div class="detection-info">
2031
+ <div class="detection-meta">
2032
+ <div><strong>\${r.cameraName}</strong></div>
2033
+ <div>\${date}</div>
2034
+ <div>Box: \${r.boundingBox.map(v => Math.round(v)).join(', ')}</div>
2035
+ </div>
2036
+ <div style="font-size:12px;color:#888;">What is this actually?</div>
2037
+ <div class="label-buttons">
2038
+ <button class="label-btn person" onclick="label('\${r.id}', 'person')">👤 Person</button>
2039
+ <button class="label-btn animal" onclick="label('\${r.id}', 'animal')">🐾 Animal</button>
2040
+ <button class="label-btn face" onclick="label('\${r.id}', 'face')">😀 Face</button>
2041
+ <button class="label-btn vehicle" onclick="label('\${r.id}', 'vehicle')">🚗 Vehicle</button>
2042
+ <button class="label-btn" onclick="label('\${r.id}', 'plate')">🔢 Plate</button>
2043
+ <button class="label-btn" onclick="label('\${r.id}', 'package')">📦 Package</button>
2044
+ <button class="label-btn discard" onclick="label('\${r.id}', 'discard')">🗑 Discard</button>
2045
+ </div>
2046
+ </div>
2047
+ </div>\`;
2048
+ }).join('');
2049
+ }
2050
+
2051
+ async function label(id, labelVal) {
2052
+ const el = document.getElementById('det-' + id);
2053
+ if (el) {
2054
+ el.classList.add('labeled');
2055
+ const btns = el.querySelectorAll('.label-btn');
2056
+ btns.forEach(b => b.disabled = true);
2057
+ }
2058
+ await fetch(BASE + '/api/label', {
2059
+ method: 'POST',
2060
+ headers: { 'Content-Type': 'application/json' },
2061
+ body: JSON.stringify({ id, label: labelVal }),
2062
+ });
2063
+ toast(labelVal === 'discard' ? 'Discarded' : 'Labeled: ' + labelVal, labelVal === 'discard' ? '#633' : '#1a6');
2064
+ setTimeout(() => {
2065
+ if (el) el.remove();
2066
+ loadPending();
2067
+ }, 600);
2068
+ }
2069
+
2070
+ async function loadStats() {
2071
+ const res = await fetch(BASE + '/api/stats');
2072
+ const stats = await res.json();
2073
+
2074
+ const renderBreakdown = (obj, container) => {
2075
+ const el = document.getElementById(container);
2076
+ const entries = Object.entries(obj).sort((a, b) => b[1] - a[1]);
2077
+ el.innerHTML = entries.length
2078
+ ? entries.map(([k, v]) => \`<div class="breakdown-item"><div class="label">\${k}</div><div class="value">\${v}</div></div>\`).join('')
2079
+ : '<div style="color:#555;font-size:13px;">None yet</div>';
2080
+ };
2081
+
2082
+ renderBreakdown(stats.byDetectedClass, 'stats-detected');
2083
+ renderBreakdown(stats.byLabel, 'stats-label');
2084
+ renderBreakdown(stats.byCamera, 'stats-camera');
2085
+ }
2086
+
2087
+ async function loadExportInfo() {
2088
+ const res = await fetch(BASE + '/api/stats');
2089
+ const stats = await res.json();
2090
+ document.getElementById('export-stats').textContent =
2091
+ \`\${stats.labeled} labeled samples ready for export across \${Object.keys(stats.byCamera).length} camera(s).\`;
2092
+ }
2093
+
2094
+ async function exportDataset() {
2095
+ const btn = document.getElementById('export-btn');
2096
+ const status = document.getElementById('export-status');
2097
+ btn.disabled = true;
2098
+ status.textContent = 'Preparing…';
2099
+
2100
+ try {
2101
+ const res = await fetch(BASE + '/api/export');
2102
+ if (!res.ok) { status.textContent = 'Nothing to export yet.'; btn.disabled = false; return; }
2103
+ const data = await res.json();
2104
+ if (data.error) { status.textContent = data.error; btn.disabled = false; return; }
2105
+
2106
+ // Build a zip-like structure using a self-extracting HTML page
2107
+ // Actually just download as a JSON bundle that train.py can consume
2108
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
2109
+ const url = URL.createObjectURL(blob);
2110
+ const a = document.createElement('a');
2111
+ a.href = url;
2112
+ a.download = 'scrypted_dataset_' + new Date().toISOString().slice(0,10) + '.json';
2113
+ a.click();
2114
+ URL.revokeObjectURL(url);
2115
+ status.textContent = \`Downloaded \${data.count} samples.\`;
2116
+ toast('Dataset downloaded!');
2117
+ } catch (e) {
2118
+ status.textContent = 'Export failed: ' + e.message;
2119
+ }
2120
+ btn.disabled = false;
2121
+ }
2122
+
2123
+ // Initial load
2124
+ loadPending();
2125
+ // Auto-refresh pending every 30s
2126
+ setInterval(loadPending, 30_000);
2127
+ </script>
2128
+ </body>
2129
+ </html>`;
2130
+ }
2131
+ }
2132
+ exports["default"] = DetectionTrainer;
2133
+
2134
+
2135
+ /***/ },
2136
+
2137
+ /***/ "module"
2138
+ /*!*************************!*\
2139
+ !*** external "module" ***!
2140
+ \*************************/
2141
+ (module) {
2142
+
2143
+ "use strict";
2144
+ module.exports = require("module");
2145
+
2146
+ /***/ }
2147
+
2148
+ /******/ });
2149
+ /************************************************************************/
2150
+ /******/ // The module cache
2151
+ /******/ var __webpack_module_cache__ = {};
2152
+ /******/
2153
+ /******/ // The require function
2154
+ /******/ function __webpack_require__(moduleId) {
2155
+ /******/ // Check if module is in cache
2156
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
2157
+ /******/ if (cachedModule !== undefined) {
2158
+ /******/ return cachedModule.exports;
2159
+ /******/ }
2160
+ /******/ // Create a new module (and put it into the cache)
2161
+ /******/ var module = __webpack_module_cache__[moduleId] = {
2162
+ /******/ // no module.id needed
2163
+ /******/ // no module.loaded needed
2164
+ /******/ exports: {}
2165
+ /******/ };
2166
+ /******/
2167
+ /******/ // Execute the module function
2168
+ /******/ if (!(moduleId in __webpack_modules__)) {
2169
+ /******/ delete __webpack_module_cache__[moduleId];
2170
+ /******/ var e = new Error("Cannot find module '" + moduleId + "'");
2171
+ /******/ e.code = 'MODULE_NOT_FOUND';
2172
+ /******/ throw e;
2173
+ /******/ }
2174
+ /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
2175
+ /******/
2176
+ /******/ // Return the exports of the module
2177
+ /******/ return module.exports;
2178
+ /******/ }
2179
+ /******/
2180
+ /************************************************************************/
2181
+ /******/ /* webpack/runtime/hasOwnProperty shorthand */
2182
+ /******/ (() => {
2183
+ /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
2184
+ /******/ })();
2185
+ /******/
2186
+ /************************************************************************/
2187
+ /******/
2188
+ /******/ // startup
2189
+ /******/ // Load entry module and return exports
2190
+ /******/ // This entry module is referenced by other modules so it can't be inlined
2191
+ /******/ var __webpack_exports__ = __webpack_require__("./src/main.ts");
2192
+ /******/ var __webpack_export_target__ = (exports = typeof exports === "undefined" ? {} : exports);
2193
+ /******/ for(var __webpack_i__ in __webpack_exports__) __webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
2194
+ /******/ if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
2195
+ /******/
2196
+ /******/ })()
2197
+ ;
2198
+
2199
+ //# sourceURL=/plugin/main.nodejs.js
2200
+ //# sourceMappingURL=main.nodejs.js.map