scrypted-detection-trainer 0.1.13 → 0.1.14
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/dist/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.map +1 -1
- package/dist/plugin.zip +0 -0
- package/out/main.nodejs.js +20 -8
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/main.ts +20 -8
package/dist/main.nodejs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.nodejs.js","mappings":"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","sources":["../node_modules/@scrypted/sdk/dist/src/index.js","../node_modules/@scrypted/sdk/dist/src|sync","../node_modules/@scrypted/sdk/dist/types/gen/index.js","../src/main.ts","../external node-commonjs \"fs\"","../external node-commonjs \"module\"","../external node-commonjs \"path\"","../webpack/bootstrap","../webpack/runtime/hasOwnProperty shorthand","../webpack/startup"],"sourcesContent":["\"use strict\";\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n}));\nvar __exportStar = (this && this.__exportStar) || function(m, exports) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.sdk = exports.MixinDeviceBase = exports.ScryptedDeviceBase = void 0;\n__exportStar(require(\"../types/gen/index\"), exports);\nconst index_1 = require(\"../types/gen/index\");\nconst module_1 = require(\"module\");\n/**\n * @category Core Reference\n */\nclass ScryptedDeviceBase extends index_1.DeviceBase {\n constructor(nativeId) {\n super();\n this.nativeId = nativeId;\n }\n get storage() {\n if (!this._storage) {\n this._storage = exports.sdk.deviceManager.getDeviceStorage(this.nativeId);\n }\n return this._storage;\n }\n get log() {\n if (!this._log) {\n this._log = exports.sdk.deviceManager.getDeviceLogger(this.nativeId);\n }\n return this._log;\n }\n get console() {\n if (!this._console) {\n this._console = exports.sdk.deviceManager.getDeviceConsole(this.nativeId);\n }\n return this._console;\n }\n async createMediaObject(data, mimeType) {\n return exports.sdk.mediaManager.createMediaObject(data, mimeType, {\n sourceId: this.id,\n });\n }\n getMediaObjectConsole(mediaObject) {\n if (typeof mediaObject.sourceId !== 'string')\n return this.console;\n return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.nativeId);\n }\n _lazyLoadDeviceState() {\n if (!this._deviceState) {\n if (this.nativeId) {\n this._deviceState = exports.sdk.deviceManager.getDeviceState(this.nativeId);\n }\n else {\n this._deviceState = exports.sdk.deviceManager.getDeviceState();\n }\n }\n }\n /**\n * Fire an event for this device.\n */\n onDeviceEvent(eventInterface, eventData) {\n return exports.sdk.deviceManager.onDeviceEvent(this.nativeId, eventInterface, eventData);\n }\n}\nexports.ScryptedDeviceBase = ScryptedDeviceBase;\n/**\n * @category Mixin Reference\n */\nclass MixinDeviceBase extends index_1.DeviceBase {\n constructor(options) {\n super();\n this._listeners = new Set();\n this.mixinDevice = options.mixinDevice;\n this.mixinDeviceInterfaces = options.mixinDeviceInterfaces;\n this.mixinStorageSuffix = options.mixinStorageSuffix;\n this._deviceState = options.mixinDeviceState;\n this.nativeId = exports.sdk.systemManager.getDeviceById(this.id).nativeId;\n this.mixinProviderNativeId = options.mixinProviderNativeId;\n // RpcProxy will trap all properties, and the following check/hack will determine\n // if the device state came from another node worker thread.\n // This should ultimately be discouraged and warned at some point in the future.\n if (this._deviceState.__rpcproxy_traps_all_properties && typeof this._deviceState.id === 'string') {\n this._deviceState = exports.sdk.deviceManager.createDeviceState(this._deviceState.id, this._deviceState.setState);\n }\n }\n get storage() {\n if (!this._storage) {\n const mixinStorageSuffix = this.mixinStorageSuffix;\n const mixinStorageKey = this.id + (mixinStorageSuffix ? ':' + mixinStorageSuffix : '');\n this._storage = exports.sdk.deviceManager.getMixinStorage(mixinStorageKey, this.mixinProviderNativeId);\n }\n return this._storage;\n }\n get console() {\n if (!this._console) {\n if (exports.sdk.deviceManager.getMixinConsole)\n this._console = exports.sdk.deviceManager.getMixinConsole(this.id, this.mixinProviderNativeId);\n else\n this._console = exports.sdk.deviceManager.getDeviceConsole(this.mixinProviderNativeId);\n }\n return this._console;\n }\n async createMediaObject(data, mimeType) {\n return exports.sdk.mediaManager.createMediaObject(data, mimeType, {\n sourceId: this.id,\n });\n }\n getMediaObjectConsole(mediaObject) {\n if (typeof mediaObject.sourceId !== 'string')\n return this.console;\n return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.mixinProviderNativeId);\n }\n /**\n * Fire an event for this device.\n */\n onDeviceEvent(eventInterface, eventData) {\n return exports.sdk.deviceManager.onMixinEvent(this.id, this, eventInterface, eventData);\n }\n _lazyLoadDeviceState() {\n }\n manageListener(listener) {\n this._listeners.add(listener);\n }\n release() {\n for (const l of this._listeners) {\n l.removeListener();\n }\n }\n}\nexports.MixinDeviceBase = MixinDeviceBase;\n(function () {\n function _createGetState(state) {\n return function () {\n this._lazyLoadDeviceState();\n // @ts-ignore: accessing private property\n return this._deviceState?.[state];\n };\n }\n function _createSetState(state) {\n return function (value) {\n this._lazyLoadDeviceState();\n // @ts-ignore: accessing private property\n if (!this._deviceState) {\n console.warn('device state is unavailable. the device must be discovered with deviceManager.onDeviceDiscovered or deviceManager.onDevicesChanged before the state can be set.');\n }\n else {\n // @ts-ignore: accessing private property\n this._deviceState[state] = value;\n }\n };\n }\n for (const field of Object.values(index_1.ScryptedInterfaceProperty)) {\n if (field === index_1.ScryptedInterfaceProperty.nativeId)\n continue;\n Object.defineProperty(ScryptedDeviceBase.prototype, field, {\n set: _createSetState(field),\n get: _createGetState(field),\n });\n Object.defineProperty(MixinDeviceBase.prototype, field, {\n set: _createSetState(field),\n get: _createGetState(field),\n });\n }\n})();\nexports.sdk = {};\ntry {\n let loaded = false;\n try {\n // todo: remove usage of process.env.SCRYPTED_SDK_MODULE, only existed in prerelease builds.\n // import.meta is not a reliable way to detect es module support in webpack since webpack\n // evaluates that to true at runtime.\n const esModule = process.env.SCRYPTED_SDK_ES_MODULE || process.env.SCRYPTED_SDK_MODULE;\n const cjsModule = process.env.SCRYPTED_SDK_CJS_MODULE || process.env.SCRYPTED_SDK_MODULE;\n // @ts-expect-error\n if (esModule && typeof import.meta !== 'undefined') {\n // @ts-expect-error\n const require = (0, module_1.createRequire)(import.meta.url);\n const sdkModule = require(esModule);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n else if (cjsModule) {\n // @ts-expect-error\n if (typeof __non_webpack_require__ !== 'undefined') {\n // @ts-expect-error\n const sdkModule = __non_webpack_require__(process.env.SCRYPTED_SDK_MODULE);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n else {\n const sdkModule = require(cjsModule);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n }\n }\n catch (e) {\n console.warn(\"failed to load sdk module\", e);\n throw e;\n }\n if (!loaded) {\n let runtimeAPI;\n try {\n runtimeAPI = pluginRuntimeAPI;\n }\n catch (e) {\n }\n Object.assign(exports.sdk, {\n log: deviceManager.getDeviceLogger(undefined),\n deviceManager,\n endpointManager,\n mediaManager,\n systemManager,\n pluginHostAPI,\n ...runtimeAPI,\n });\n }\n try {\n exports.sdk.systemManager.setScryptedInterfaceDescriptors?.(index_1.TYPES_VERSION, index_1.ScryptedInterfaceDescriptors)?.catch(() => { });\n }\n catch (e) {\n }\n}\ncatch (e) {\n console.error('sdk initialization error, import @scrypted/types or use @scrypted/client instead', e);\n}\nexports.default = exports.sdk;\n//# sourceMappingURL=index.js.map","function webpackEmptyContext(req) {\n\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\te.code = 'MODULE_NOT_FOUND';\n\tthrow e;\n}\nwebpackEmptyContext.keys = () => ([]);\nwebpackEmptyContext.resolve = webpackEmptyContext;\nwebpackEmptyContext.id = 891;\nmodule.exports = webpackEmptyContext;","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.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;\nexports.TYPES_VERSION = \"0.3.116\";\nclass DeviceBase {\n}\nexports.DeviceBase = DeviceBase;\nvar ScryptedInterfaceProperty;\n(function (ScryptedInterfaceProperty) {\n ScryptedInterfaceProperty[\"id\"] = \"id\";\n ScryptedInterfaceProperty[\"info\"] = \"info\";\n ScryptedInterfaceProperty[\"interfaces\"] = \"interfaces\";\n ScryptedInterfaceProperty[\"mixins\"] = \"mixins\";\n ScryptedInterfaceProperty[\"name\"] = \"name\";\n ScryptedInterfaceProperty[\"nativeId\"] = \"nativeId\";\n ScryptedInterfaceProperty[\"pluginId\"] = \"pluginId\";\n ScryptedInterfaceProperty[\"providedInterfaces\"] = \"providedInterfaces\";\n ScryptedInterfaceProperty[\"providedName\"] = \"providedName\";\n ScryptedInterfaceProperty[\"providedRoom\"] = \"providedRoom\";\n ScryptedInterfaceProperty[\"providedType\"] = \"providedType\";\n ScryptedInterfaceProperty[\"providerId\"] = \"providerId\";\n ScryptedInterfaceProperty[\"room\"] = \"room\";\n ScryptedInterfaceProperty[\"type\"] = \"type\";\n ScryptedInterfaceProperty[\"scryptedRuntimeArguments\"] = \"scryptedRuntimeArguments\";\n ScryptedInterfaceProperty[\"on\"] = \"on\";\n ScryptedInterfaceProperty[\"brightness\"] = \"brightness\";\n ScryptedInterfaceProperty[\"colorTemperature\"] = \"colorTemperature\";\n ScryptedInterfaceProperty[\"rgb\"] = \"rgb\";\n ScryptedInterfaceProperty[\"hsv\"] = \"hsv\";\n ScryptedInterfaceProperty[\"buttons\"] = \"buttons\";\n ScryptedInterfaceProperty[\"sensors\"] = \"sensors\";\n ScryptedInterfaceProperty[\"running\"] = \"running\";\n ScryptedInterfaceProperty[\"paused\"] = \"paused\";\n ScryptedInterfaceProperty[\"docked\"] = \"docked\";\n ScryptedInterfaceProperty[\"temperatureSetting\"] = \"temperatureSetting\";\n ScryptedInterfaceProperty[\"temperature\"] = \"temperature\";\n ScryptedInterfaceProperty[\"temperatureUnit\"] = \"temperatureUnit\";\n ScryptedInterfaceProperty[\"humidity\"] = \"humidity\";\n ScryptedInterfaceProperty[\"audioVolumes\"] = \"audioVolumes\";\n ScryptedInterfaceProperty[\"recordingActive\"] = \"recordingActive\";\n ScryptedInterfaceProperty[\"ptzCapabilities\"] = \"ptzCapabilities\";\n ScryptedInterfaceProperty[\"lockState\"] = \"lockState\";\n ScryptedInterfaceProperty[\"entryOpen\"] = \"entryOpen\";\n ScryptedInterfaceProperty[\"batteryLevel\"] = \"batteryLevel\";\n ScryptedInterfaceProperty[\"chargeState\"] = \"chargeState\";\n ScryptedInterfaceProperty[\"online\"] = \"online\";\n ScryptedInterfaceProperty[\"fromMimeType\"] = \"fromMimeType\";\n ScryptedInterfaceProperty[\"toMimeType\"] = \"toMimeType\";\n ScryptedInterfaceProperty[\"converters\"] = \"converters\";\n ScryptedInterfaceProperty[\"binaryState\"] = \"binaryState\";\n ScryptedInterfaceProperty[\"tampered\"] = \"tampered\";\n ScryptedInterfaceProperty[\"sleeping\"] = \"sleeping\";\n ScryptedInterfaceProperty[\"powerDetected\"] = \"powerDetected\";\n ScryptedInterfaceProperty[\"audioDetected\"] = \"audioDetected\";\n ScryptedInterfaceProperty[\"motionDetected\"] = \"motionDetected\";\n ScryptedInterfaceProperty[\"ambientLight\"] = \"ambientLight\";\n ScryptedInterfaceProperty[\"occupied\"] = \"occupied\";\n ScryptedInterfaceProperty[\"flooded\"] = \"flooded\";\n ScryptedInterfaceProperty[\"ultraviolet\"] = \"ultraviolet\";\n ScryptedInterfaceProperty[\"luminance\"] = \"luminance\";\n ScryptedInterfaceProperty[\"position\"] = \"position\";\n ScryptedInterfaceProperty[\"securitySystemState\"] = \"securitySystemState\";\n ScryptedInterfaceProperty[\"pm10Density\"] = \"pm10Density\";\n ScryptedInterfaceProperty[\"pm25Density\"] = \"pm25Density\";\n ScryptedInterfaceProperty[\"vocDensity\"] = \"vocDensity\";\n ScryptedInterfaceProperty[\"noxDensity\"] = \"noxDensity\";\n ScryptedInterfaceProperty[\"co2ppm\"] = \"co2ppm\";\n ScryptedInterfaceProperty[\"airQuality\"] = \"airQuality\";\n ScryptedInterfaceProperty[\"airPurifierState\"] = \"airPurifierState\";\n ScryptedInterfaceProperty[\"filterChangeIndication\"] = \"filterChangeIndication\";\n ScryptedInterfaceProperty[\"filterLifeLevel\"] = \"filterLifeLevel\";\n ScryptedInterfaceProperty[\"humiditySetting\"] = \"humiditySetting\";\n ScryptedInterfaceProperty[\"fan\"] = \"fan\";\n ScryptedInterfaceProperty[\"applicationInfo\"] = \"applicationInfo\";\n ScryptedInterfaceProperty[\"systemDevice\"] = \"systemDevice\";\n})(ScryptedInterfaceProperty || (exports.ScryptedInterfaceProperty = ScryptedInterfaceProperty = {}));\nvar ScryptedInterfaceMethod;\n(function (ScryptedInterfaceMethod) {\n ScryptedInterfaceMethod[\"listen\"] = \"listen\";\n ScryptedInterfaceMethod[\"probe\"] = \"probe\";\n ScryptedInterfaceMethod[\"setMixins\"] = \"setMixins\";\n ScryptedInterfaceMethod[\"setName\"] = \"setName\";\n ScryptedInterfaceMethod[\"setRoom\"] = \"setRoom\";\n ScryptedInterfaceMethod[\"setType\"] = \"setType\";\n ScryptedInterfaceMethod[\"getPluginJson\"] = \"getPluginJson\";\n ScryptedInterfaceMethod[\"turnOff\"] = \"turnOff\";\n ScryptedInterfaceMethod[\"turnOn\"] = \"turnOn\";\n ScryptedInterfaceMethod[\"setBrightness\"] = \"setBrightness\";\n ScryptedInterfaceMethod[\"getTemperatureMaxK\"] = \"getTemperatureMaxK\";\n ScryptedInterfaceMethod[\"getTemperatureMinK\"] = \"getTemperatureMinK\";\n ScryptedInterfaceMethod[\"setColorTemperature\"] = \"setColorTemperature\";\n ScryptedInterfaceMethod[\"setRgb\"] = \"setRgb\";\n ScryptedInterfaceMethod[\"setHsv\"] = \"setHsv\";\n ScryptedInterfaceMethod[\"pressButton\"] = \"pressButton\";\n ScryptedInterfaceMethod[\"sendNotification\"] = \"sendNotification\";\n ScryptedInterfaceMethod[\"start\"] = \"start\";\n ScryptedInterfaceMethod[\"stop\"] = \"stop\";\n ScryptedInterfaceMethod[\"pause\"] = \"pause\";\n ScryptedInterfaceMethod[\"resume\"] = \"resume\";\n ScryptedInterfaceMethod[\"dock\"] = \"dock\";\n ScryptedInterfaceMethod[\"setTemperature\"] = \"setTemperature\";\n ScryptedInterfaceMethod[\"setTemperatureUnit\"] = \"setTemperatureUnit\";\n ScryptedInterfaceMethod[\"getPictureOptions\"] = \"getPictureOptions\";\n ScryptedInterfaceMethod[\"takePicture\"] = \"takePicture\";\n ScryptedInterfaceMethod[\"getAudioStream\"] = \"getAudioStream\";\n ScryptedInterfaceMethod[\"setAudioVolumes\"] = \"setAudioVolumes\";\n ScryptedInterfaceMethod[\"startDisplay\"] = \"startDisplay\";\n ScryptedInterfaceMethod[\"stopDisplay\"] = \"stopDisplay\";\n ScryptedInterfaceMethod[\"getVideoStream\"] = \"getVideoStream\";\n ScryptedInterfaceMethod[\"getVideoStreamOptions\"] = \"getVideoStreamOptions\";\n ScryptedInterfaceMethod[\"getPrivacyMasks\"] = \"getPrivacyMasks\";\n ScryptedInterfaceMethod[\"setPrivacyMasks\"] = \"setPrivacyMasks\";\n ScryptedInterfaceMethod[\"getVideoTextOverlays\"] = \"getVideoTextOverlays\";\n ScryptedInterfaceMethod[\"setVideoTextOverlay\"] = \"setVideoTextOverlay\";\n ScryptedInterfaceMethod[\"getRecordingStream\"] = \"getRecordingStream\";\n ScryptedInterfaceMethod[\"getRecordingStreamCurrentTime\"] = \"getRecordingStreamCurrentTime\";\n ScryptedInterfaceMethod[\"getRecordingStreamOptions\"] = \"getRecordingStreamOptions\";\n ScryptedInterfaceMethod[\"getRecordingStreamThumbnail\"] = \"getRecordingStreamThumbnail\";\n ScryptedInterfaceMethod[\"deleteRecordingStream\"] = \"deleteRecordingStream\";\n ScryptedInterfaceMethod[\"setRecordingActive\"] = \"setRecordingActive\";\n ScryptedInterfaceMethod[\"ptzCommand\"] = \"ptzCommand\";\n ScryptedInterfaceMethod[\"getRecordedEvents\"] = \"getRecordedEvents\";\n ScryptedInterfaceMethod[\"getVideoClip\"] = \"getVideoClip\";\n ScryptedInterfaceMethod[\"getVideoClips\"] = \"getVideoClips\";\n ScryptedInterfaceMethod[\"getVideoClipThumbnail\"] = \"getVideoClipThumbnail\";\n ScryptedInterfaceMethod[\"removeVideoClips\"] = \"removeVideoClips\";\n ScryptedInterfaceMethod[\"setVideoStreamOptions\"] = \"setVideoStreamOptions\";\n ScryptedInterfaceMethod[\"startIntercom\"] = \"startIntercom\";\n ScryptedInterfaceMethod[\"stopIntercom\"] = \"stopIntercom\";\n ScryptedInterfaceMethod[\"lock\"] = \"lock\";\n ScryptedInterfaceMethod[\"unlock\"] = \"unlock\";\n ScryptedInterfaceMethod[\"addPassword\"] = \"addPassword\";\n ScryptedInterfaceMethod[\"getPasswords\"] = \"getPasswords\";\n ScryptedInterfaceMethod[\"removePassword\"] = \"removePassword\";\n ScryptedInterfaceMethod[\"activate\"] = \"activate\";\n ScryptedInterfaceMethod[\"deactivate\"] = \"deactivate\";\n ScryptedInterfaceMethod[\"isReversible\"] = \"isReversible\";\n ScryptedInterfaceMethod[\"closeEntry\"] = \"closeEntry\";\n ScryptedInterfaceMethod[\"openEntry\"] = \"openEntry\";\n ScryptedInterfaceMethod[\"getDevice\"] = \"getDevice\";\n ScryptedInterfaceMethod[\"releaseDevice\"] = \"releaseDevice\";\n ScryptedInterfaceMethod[\"adoptDevice\"] = \"adoptDevice\";\n ScryptedInterfaceMethod[\"discoverDevices\"] = \"discoverDevices\";\n ScryptedInterfaceMethod[\"createDevice\"] = \"createDevice\";\n ScryptedInterfaceMethod[\"getCreateDeviceSettings\"] = \"getCreateDeviceSettings\";\n ScryptedInterfaceMethod[\"reboot\"] = \"reboot\";\n ScryptedInterfaceMethod[\"getRefreshFrequency\"] = \"getRefreshFrequency\";\n ScryptedInterfaceMethod[\"refresh\"] = \"refresh\";\n ScryptedInterfaceMethod[\"getMediaStatus\"] = \"getMediaStatus\";\n ScryptedInterfaceMethod[\"load\"] = \"load\";\n ScryptedInterfaceMethod[\"seek\"] = \"seek\";\n ScryptedInterfaceMethod[\"skipNext\"] = \"skipNext\";\n ScryptedInterfaceMethod[\"skipPrevious\"] = \"skipPrevious\";\n ScryptedInterfaceMethod[\"convert\"] = \"convert\";\n ScryptedInterfaceMethod[\"convertMedia\"] = \"convertMedia\";\n ScryptedInterfaceMethod[\"getSettings\"] = \"getSettings\";\n ScryptedInterfaceMethod[\"putSetting\"] = \"putSetting\";\n ScryptedInterfaceMethod[\"armSecuritySystem\"] = \"armSecuritySystem\";\n ScryptedInterfaceMethod[\"disarmSecuritySystem\"] = \"disarmSecuritySystem\";\n ScryptedInterfaceMethod[\"setAirPurifierState\"] = \"setAirPurifierState\";\n ScryptedInterfaceMethod[\"getReadmeMarkdown\"] = \"getReadmeMarkdown\";\n ScryptedInterfaceMethod[\"getOauthUrl\"] = \"getOauthUrl\";\n ScryptedInterfaceMethod[\"onOauthCallback\"] = \"onOauthCallback\";\n ScryptedInterfaceMethod[\"canMixin\"] = \"canMixin\";\n ScryptedInterfaceMethod[\"getMixin\"] = \"getMixin\";\n ScryptedInterfaceMethod[\"releaseMixin\"] = \"releaseMixin\";\n ScryptedInterfaceMethod[\"onRequest\"] = \"onRequest\";\n ScryptedInterfaceMethod[\"onConnection\"] = \"onConnection\";\n ScryptedInterfaceMethod[\"onPush\"] = \"onPush\";\n ScryptedInterfaceMethod[\"run\"] = \"run\";\n ScryptedInterfaceMethod[\"eval\"] = \"eval\";\n ScryptedInterfaceMethod[\"loadScripts\"] = \"loadScripts\";\n ScryptedInterfaceMethod[\"saveScript\"] = \"saveScript\";\n ScryptedInterfaceMethod[\"forkInterface\"] = \"forkInterface\";\n ScryptedInterfaceMethod[\"trackObjects\"] = \"trackObjects\";\n ScryptedInterfaceMethod[\"getDetectionInput\"] = \"getDetectionInput\";\n ScryptedInterfaceMethod[\"getObjectTypes\"] = \"getObjectTypes\";\n ScryptedInterfaceMethod[\"detectObjects\"] = \"detectObjects\";\n ScryptedInterfaceMethod[\"generateObjectDetections\"] = \"generateObjectDetections\";\n ScryptedInterfaceMethod[\"getDetectionModel\"] = \"getDetectionModel\";\n ScryptedInterfaceMethod[\"setHumidity\"] = \"setHumidity\";\n ScryptedInterfaceMethod[\"setFan\"] = \"setFan\";\n ScryptedInterfaceMethod[\"startRTCSignalingSession\"] = \"startRTCSignalingSession\";\n ScryptedInterfaceMethod[\"createRTCSignalingSession\"] = \"createRTCSignalingSession\";\n ScryptedInterfaceMethod[\"getScryptedUserAccessControl\"] = \"getScryptedUserAccessControl\";\n ScryptedInterfaceMethod[\"generateVideoFrames\"] = \"generateVideoFrames\";\n ScryptedInterfaceMethod[\"connectStream\"] = \"connectStream\";\n ScryptedInterfaceMethod[\"getTTYSettings\"] = \"getTTYSettings\";\n})(ScryptedInterfaceMethod || (exports.ScryptedInterfaceMethod = ScryptedInterfaceMethod = {}));\nexports.ScryptedInterfaceDescriptors = {\n \"ScryptedDevice\": {\n \"name\": \"ScryptedDevice\",\n \"methods\": [\n \"listen\",\n \"probe\",\n \"setMixins\",\n \"setName\",\n \"setRoom\",\n \"setType\"\n ],\n \"properties\": [\n \"id\",\n \"info\",\n \"interfaces\",\n \"mixins\",\n \"name\",\n \"nativeId\",\n \"pluginId\",\n \"providedInterfaces\",\n \"providedName\",\n \"providedRoom\",\n \"providedType\",\n \"providerId\",\n \"room\",\n \"type\"\n ]\n },\n \"ScryptedPlugin\": {\n \"name\": \"ScryptedPlugin\",\n \"methods\": [\n \"getPluginJson\"\n ],\n \"properties\": []\n },\n \"ScryptedPluginRuntime\": {\n \"name\": \"ScryptedPluginRuntime\",\n \"methods\": [],\n \"properties\": [\n \"scryptedRuntimeArguments\"\n ]\n },\n \"OnOff\": {\n \"name\": \"OnOff\",\n \"methods\": [\n \"turnOff\",\n \"turnOn\"\n ],\n \"properties\": [\n \"on\"\n ]\n },\n \"Brightness\": {\n \"name\": \"Brightness\",\n \"methods\": [\n \"setBrightness\"\n ],\n \"properties\": [\n \"brightness\"\n ]\n },\n \"ColorSettingTemperature\": {\n \"name\": \"ColorSettingTemperature\",\n \"methods\": [\n \"getTemperatureMaxK\",\n \"getTemperatureMinK\",\n \"setColorTemperature\"\n ],\n \"properties\": [\n \"colorTemperature\"\n ]\n },\n \"ColorSettingRgb\": {\n \"name\": \"ColorSettingRgb\",\n \"methods\": [\n \"setRgb\"\n ],\n \"properties\": [\n \"rgb\"\n ]\n },\n \"ColorSettingHsv\": {\n \"name\": \"ColorSettingHsv\",\n \"methods\": [\n \"setHsv\"\n ],\n \"properties\": [\n \"hsv\"\n ]\n },\n \"Buttons\": {\n \"name\": \"Buttons\",\n \"methods\": [],\n \"properties\": [\n \"buttons\"\n ]\n },\n \"PressButtons\": {\n \"name\": \"PressButtons\",\n \"methods\": [\n \"pressButton\"\n ],\n \"properties\": []\n },\n \"Sensors\": {\n \"name\": \"Sensors\",\n \"methods\": [],\n \"properties\": [\n \"sensors\"\n ]\n },\n \"Notifier\": {\n \"name\": \"Notifier\",\n \"methods\": [\n \"sendNotification\"\n ],\n \"properties\": []\n },\n \"StartStop\": {\n \"name\": \"StartStop\",\n \"methods\": [\n \"start\",\n \"stop\"\n ],\n \"properties\": [\n \"running\"\n ]\n },\n \"Pause\": {\n \"name\": \"Pause\",\n \"methods\": [\n \"pause\",\n \"resume\"\n ],\n \"properties\": [\n \"paused\"\n ]\n },\n \"Dock\": {\n \"name\": \"Dock\",\n \"methods\": [\n \"dock\"\n ],\n \"properties\": [\n \"docked\"\n ]\n },\n \"TemperatureSetting\": {\n \"name\": \"TemperatureSetting\",\n \"methods\": [\n \"setTemperature\"\n ],\n \"properties\": [\n \"temperatureSetting\"\n ]\n },\n \"Thermometer\": {\n \"name\": \"Thermometer\",\n \"methods\": [\n \"setTemperatureUnit\"\n ],\n \"properties\": [\n \"temperature\",\n \"temperatureUnit\"\n ]\n },\n \"HumiditySensor\": {\n \"name\": \"HumiditySensor\",\n \"methods\": [],\n \"properties\": [\n \"humidity\"\n ]\n },\n \"Camera\": {\n \"name\": \"Camera\",\n \"methods\": [\n \"getPictureOptions\",\n \"takePicture\"\n ],\n \"properties\": []\n },\n \"Microphone\": {\n \"name\": \"Microphone\",\n \"methods\": [\n \"getAudioStream\"\n ],\n \"properties\": []\n },\n \"AudioVolumeControl\": {\n \"name\": \"AudioVolumeControl\",\n \"methods\": [\n \"setAudioVolumes\"\n ],\n \"properties\": [\n \"audioVolumes\"\n ]\n },\n \"Display\": {\n \"name\": \"Display\",\n \"methods\": [\n \"startDisplay\",\n \"stopDisplay\"\n ],\n \"properties\": []\n },\n \"VideoCamera\": {\n \"name\": \"VideoCamera\",\n \"methods\": [\n \"getVideoStream\",\n \"getVideoStreamOptions\"\n ],\n \"properties\": []\n },\n \"VideoCameraMask\": {\n \"name\": \"VideoCameraMask\",\n \"methods\": [\n \"getPrivacyMasks\",\n \"setPrivacyMasks\"\n ],\n \"properties\": []\n },\n \"VideoTextOverlays\": {\n \"name\": \"VideoTextOverlays\",\n \"methods\": [\n \"getVideoTextOverlays\",\n \"setVideoTextOverlay\"\n ],\n \"properties\": []\n },\n \"VideoRecorder\": {\n \"name\": \"VideoRecorder\",\n \"methods\": [\n \"getRecordingStream\",\n \"getRecordingStreamCurrentTime\",\n \"getRecordingStreamOptions\",\n \"getRecordingStreamThumbnail\"\n ],\n \"properties\": [\n \"recordingActive\"\n ]\n },\n \"VideoRecorderManagement\": {\n \"name\": \"VideoRecorderManagement\",\n \"methods\": [\n \"deleteRecordingStream\",\n \"setRecordingActive\"\n ],\n \"properties\": []\n },\n \"PanTiltZoom\": {\n \"name\": \"PanTiltZoom\",\n \"methods\": [\n \"ptzCommand\"\n ],\n \"properties\": [\n \"ptzCapabilities\"\n ]\n },\n \"EventRecorder\": {\n \"name\": \"EventRecorder\",\n \"methods\": [\n \"getRecordedEvents\"\n ],\n \"properties\": []\n },\n \"VideoClips\": {\n \"name\": \"VideoClips\",\n \"methods\": [\n \"getVideoClip\",\n \"getVideoClips\",\n \"getVideoClipThumbnail\",\n \"removeVideoClips\"\n ],\n \"properties\": []\n },\n \"VideoCameraConfiguration\": {\n \"name\": \"VideoCameraConfiguration\",\n \"methods\": [\n \"setVideoStreamOptions\"\n ],\n \"properties\": []\n },\n \"Intercom\": {\n \"name\": \"Intercom\",\n \"methods\": [\n \"startIntercom\",\n \"stopIntercom\"\n ],\n \"properties\": []\n },\n \"Lock\": {\n \"name\": \"Lock\",\n \"methods\": [\n \"lock\",\n \"unlock\"\n ],\n \"properties\": [\n \"lockState\"\n ]\n },\n \"PasswordStore\": {\n \"name\": \"PasswordStore\",\n \"methods\": [\n \"addPassword\",\n \"getPasswords\",\n \"removePassword\"\n ],\n \"properties\": []\n },\n \"Scene\": {\n \"name\": \"Scene\",\n \"methods\": [\n \"activate\",\n \"deactivate\",\n \"isReversible\"\n ],\n \"properties\": []\n },\n \"Entry\": {\n \"name\": \"Entry\",\n \"methods\": [\n \"closeEntry\",\n \"openEntry\"\n ],\n \"properties\": []\n },\n \"EntrySensor\": {\n \"name\": \"EntrySensor\",\n \"methods\": [],\n \"properties\": [\n \"entryOpen\"\n ]\n },\n \"DeviceProvider\": {\n \"name\": \"DeviceProvider\",\n \"methods\": [\n \"getDevice\",\n \"releaseDevice\"\n ],\n \"properties\": []\n },\n \"DeviceDiscovery\": {\n \"name\": \"DeviceDiscovery\",\n \"methods\": [\n \"adoptDevice\",\n \"discoverDevices\"\n ],\n \"properties\": []\n },\n \"DeviceCreator\": {\n \"name\": \"DeviceCreator\",\n \"methods\": [\n \"createDevice\",\n \"getCreateDeviceSettings\"\n ],\n \"properties\": []\n },\n \"Battery\": {\n \"name\": \"Battery\",\n \"methods\": [],\n \"properties\": [\n \"batteryLevel\"\n ]\n },\n \"Charger\": {\n \"name\": \"Charger\",\n \"methods\": [],\n \"properties\": [\n \"chargeState\"\n ]\n },\n \"Reboot\": {\n \"name\": \"Reboot\",\n \"methods\": [\n \"reboot\"\n ],\n \"properties\": []\n },\n \"Refresh\": {\n \"name\": \"Refresh\",\n \"methods\": [\n \"getRefreshFrequency\",\n \"refresh\"\n ],\n \"properties\": []\n },\n \"MediaPlayer\": {\n \"name\": \"MediaPlayer\",\n \"methods\": [\n \"getMediaStatus\",\n \"load\",\n \"seek\",\n \"skipNext\",\n \"skipPrevious\"\n ],\n \"properties\": []\n },\n \"Online\": {\n \"name\": \"Online\",\n \"methods\": [],\n \"properties\": [\n \"online\"\n ]\n },\n \"BufferConverter\": {\n \"name\": \"BufferConverter\",\n \"methods\": [\n \"convert\"\n ],\n \"properties\": [\n \"fromMimeType\",\n \"toMimeType\"\n ]\n },\n \"MediaConverter\": {\n \"name\": \"MediaConverter\",\n \"methods\": [\n \"convertMedia\"\n ],\n \"properties\": [\n \"converters\"\n ]\n },\n \"Settings\": {\n \"name\": \"Settings\",\n \"methods\": [\n \"getSettings\",\n \"putSetting\"\n ],\n \"properties\": []\n },\n \"BinarySensor\": {\n \"name\": \"BinarySensor\",\n \"methods\": [],\n \"properties\": [\n \"binaryState\"\n ]\n },\n \"TamperSensor\": {\n \"name\": \"TamperSensor\",\n \"methods\": [],\n \"properties\": [\n \"tampered\"\n ]\n },\n \"Sleep\": {\n \"name\": \"Sleep\",\n \"methods\": [],\n \"properties\": [\n \"sleeping\"\n ]\n },\n \"PowerSensor\": {\n \"name\": \"PowerSensor\",\n \"methods\": [],\n \"properties\": [\n \"powerDetected\"\n ]\n },\n \"AudioSensor\": {\n \"name\": \"AudioSensor\",\n \"methods\": [],\n \"properties\": [\n \"audioDetected\"\n ]\n },\n \"MotionSensor\": {\n \"name\": \"MotionSensor\",\n \"methods\": [],\n \"properties\": [\n \"motionDetected\"\n ]\n },\n \"AmbientLightSensor\": {\n \"name\": \"AmbientLightSensor\",\n \"methods\": [],\n \"properties\": [\n \"ambientLight\"\n ]\n },\n \"OccupancySensor\": {\n \"name\": \"OccupancySensor\",\n \"methods\": [],\n \"properties\": [\n \"occupied\"\n ]\n },\n \"FloodSensor\": {\n \"name\": \"FloodSensor\",\n \"methods\": [],\n \"properties\": [\n \"flooded\"\n ]\n },\n \"UltravioletSensor\": {\n \"name\": \"UltravioletSensor\",\n \"methods\": [],\n \"properties\": [\n \"ultraviolet\"\n ]\n },\n \"LuminanceSensor\": {\n \"name\": \"LuminanceSensor\",\n \"methods\": [],\n \"properties\": [\n \"luminance\"\n ]\n },\n \"PositionSensor\": {\n \"name\": \"PositionSensor\",\n \"methods\": [],\n \"properties\": [\n \"position\"\n ]\n },\n \"SecuritySystem\": {\n \"name\": \"SecuritySystem\",\n \"methods\": [\n \"armSecuritySystem\",\n \"disarmSecuritySystem\"\n ],\n \"properties\": [\n \"securitySystemState\"\n ]\n },\n \"PM10Sensor\": {\n \"name\": \"PM10Sensor\",\n \"methods\": [],\n \"properties\": [\n \"pm10Density\"\n ]\n },\n \"PM25Sensor\": {\n \"name\": \"PM25Sensor\",\n \"methods\": [],\n \"properties\": [\n \"pm25Density\"\n ]\n },\n \"VOCSensor\": {\n \"name\": \"VOCSensor\",\n \"methods\": [],\n \"properties\": [\n \"vocDensity\"\n ]\n },\n \"NOXSensor\": {\n \"name\": \"NOXSensor\",\n \"methods\": [],\n \"properties\": [\n \"noxDensity\"\n ]\n },\n \"CO2Sensor\": {\n \"name\": \"CO2Sensor\",\n \"methods\": [],\n \"properties\": [\n \"co2ppm\"\n ]\n },\n \"AirQualitySensor\": {\n \"name\": \"AirQualitySensor\",\n \"methods\": [],\n \"properties\": [\n \"airQuality\"\n ]\n },\n \"AirPurifier\": {\n \"name\": \"AirPurifier\",\n \"methods\": [\n \"setAirPurifierState\"\n ],\n \"properties\": [\n \"airPurifierState\"\n ]\n },\n \"FilterMaintenance\": {\n \"name\": \"FilterMaintenance\",\n \"methods\": [],\n \"properties\": [\n \"filterChangeIndication\",\n \"filterLifeLevel\"\n ]\n },\n \"Readme\": {\n \"name\": \"Readme\",\n \"methods\": [\n \"getReadmeMarkdown\"\n ],\n \"properties\": []\n },\n \"OauthClient\": {\n \"name\": \"OauthClient\",\n \"methods\": [\n \"getOauthUrl\",\n \"onOauthCallback\"\n ],\n \"properties\": []\n },\n \"MixinProvider\": {\n \"name\": \"MixinProvider\",\n \"methods\": [\n \"canMixin\",\n \"getMixin\",\n \"releaseMixin\"\n ],\n \"properties\": []\n },\n \"HttpRequestHandler\": {\n \"name\": \"HttpRequestHandler\",\n \"methods\": [\n \"onRequest\"\n ],\n \"properties\": []\n },\n \"EngineIOHandler\": {\n \"name\": \"EngineIOHandler\",\n \"methods\": [\n \"onConnection\"\n ],\n \"properties\": []\n },\n \"PushHandler\": {\n \"name\": \"PushHandler\",\n \"methods\": [\n \"onPush\"\n ],\n \"properties\": []\n },\n \"Program\": {\n \"name\": \"Program\",\n \"methods\": [\n \"run\"\n ],\n \"properties\": []\n },\n \"Scriptable\": {\n \"name\": \"Scriptable\",\n \"methods\": [\n \"eval\",\n \"loadScripts\",\n \"saveScript\"\n ],\n \"properties\": []\n },\n \"ClusterForkInterface\": {\n \"name\": \"ClusterForkInterface\",\n \"methods\": [\n \"forkInterface\"\n ],\n \"properties\": []\n },\n \"ObjectTracker\": {\n \"name\": \"ObjectTracker\",\n \"methods\": [\n \"trackObjects\"\n ],\n \"properties\": []\n },\n \"ObjectDetector\": {\n \"name\": \"ObjectDetector\",\n \"methods\": [\n \"getDetectionInput\",\n \"getObjectTypes\"\n ],\n \"properties\": []\n },\n \"ObjectDetection\": {\n \"name\": \"ObjectDetection\",\n \"methods\": [\n \"detectObjects\",\n \"generateObjectDetections\",\n \"getDetectionModel\"\n ],\n \"properties\": []\n },\n \"ObjectDetectionPreview\": {\n \"name\": \"ObjectDetectionPreview\",\n \"methods\": [],\n \"properties\": []\n },\n \"ObjectDetectionGenerator\": {\n \"name\": \"ObjectDetectionGenerator\",\n \"methods\": [],\n \"properties\": []\n },\n \"HumiditySetting\": {\n \"name\": \"HumiditySetting\",\n \"methods\": [\n \"setHumidity\"\n ],\n \"properties\": [\n \"humiditySetting\"\n ]\n },\n \"Fan\": {\n \"name\": \"Fan\",\n \"methods\": [\n \"setFan\"\n ],\n \"properties\": [\n \"fan\"\n ]\n },\n \"RTCSignalingChannel\": {\n \"name\": \"RTCSignalingChannel\",\n \"methods\": [\n \"startRTCSignalingSession\"\n ],\n \"properties\": []\n },\n \"RTCSignalingClient\": {\n \"name\": \"RTCSignalingClient\",\n \"methods\": [\n \"createRTCSignalingSession\"\n ],\n \"properties\": []\n },\n \"LauncherApplication\": {\n \"name\": \"LauncherApplication\",\n \"methods\": [],\n \"properties\": [\n \"applicationInfo\"\n ]\n },\n \"ScryptedUser\": {\n \"name\": \"ScryptedUser\",\n \"methods\": [\n \"getScryptedUserAccessControl\"\n ],\n \"properties\": []\n },\n \"VideoFrameGenerator\": {\n \"name\": \"VideoFrameGenerator\",\n \"methods\": [\n \"generateVideoFrames\"\n ],\n \"properties\": []\n },\n \"StreamService\": {\n \"name\": \"StreamService\",\n \"methods\": [\n \"connectStream\"\n ],\n \"properties\": []\n },\n \"TTY\": {\n \"name\": \"TTY\",\n \"methods\": [],\n \"properties\": []\n },\n \"TTYSettings\": {\n \"name\": \"TTYSettings\",\n \"methods\": [\n \"getTTYSettings\"\n ],\n \"properties\": []\n },\n \"ScryptedSystemDevice\": {\n \"name\": \"ScryptedSystemDevice\",\n \"methods\": [],\n \"properties\": [\n \"systemDevice\"\n ]\n },\n \"ScryptedDeviceCreator\": {\n \"name\": \"ScryptedDeviceCreator\",\n \"methods\": [],\n \"properties\": []\n },\n \"ScryptedSettings\": {\n \"name\": \"ScryptedSettings\",\n \"methods\": [],\n \"properties\": []\n }\n};\n/**\n * @category Core Reference\n */\nvar ScryptedDeviceType;\n(function (ScryptedDeviceType) {\n /**\n * @deprecated\n */\n ScryptedDeviceType[\"Builtin\"] = \"Builtin\";\n /**\n * Internal devices will not show up in device lists unless explicitly searched.\n */\n ScryptedDeviceType[\"Internal\"] = \"Internal\";\n ScryptedDeviceType[\"Camera\"] = \"Camera\";\n ScryptedDeviceType[\"Fan\"] = \"Fan\";\n ScryptedDeviceType[\"Light\"] = \"Light\";\n ScryptedDeviceType[\"Switch\"] = \"Switch\";\n ScryptedDeviceType[\"Outlet\"] = \"Outlet\";\n ScryptedDeviceType[\"Sensor\"] = \"Sensor\";\n ScryptedDeviceType[\"Scene\"] = \"Scene\";\n ScryptedDeviceType[\"Program\"] = \"Program\";\n ScryptedDeviceType[\"Automation\"] = \"Automation\";\n ScryptedDeviceType[\"Vacuum\"] = \"Vacuum\";\n ScryptedDeviceType[\"Notifier\"] = \"Notifier\";\n ScryptedDeviceType[\"Thermostat\"] = \"Thermostat\";\n ScryptedDeviceType[\"Lock\"] = \"Lock\";\n ScryptedDeviceType[\"PasswordControl\"] = \"PasswordControl\";\n /**\n * Displays have audio and video output.\n */\n ScryptedDeviceType[\"Display\"] = \"Display\";\n /**\n * Smart Displays have two way audio and video.\n */\n ScryptedDeviceType[\"SmartDisplay\"] = \"SmartDisplay\";\n ScryptedDeviceType[\"Speaker\"] = \"Speaker\";\n /**\n * Smart Speakers have two way audio.\n */\n ScryptedDeviceType[\"SmartSpeaker\"] = \"SmartSpeaker\";\n ScryptedDeviceType[\"Event\"] = \"Event\";\n ScryptedDeviceType[\"Entry\"] = \"Entry\";\n ScryptedDeviceType[\"Garage\"] = \"Garage\";\n ScryptedDeviceType[\"DeviceProvider\"] = \"DeviceProvider\";\n ScryptedDeviceType[\"DataSource\"] = \"DataSource\";\n ScryptedDeviceType[\"API\"] = \"API\";\n ScryptedDeviceType[\"Doorbell\"] = \"Doorbell\";\n ScryptedDeviceType[\"Irrigation\"] = \"Irrigation\";\n ScryptedDeviceType[\"Valve\"] = \"Valve\";\n ScryptedDeviceType[\"Person\"] = \"Person\";\n ScryptedDeviceType[\"SecuritySystem\"] = \"SecuritySystem\";\n ScryptedDeviceType[\"WindowCovering\"] = \"WindowCovering\";\n ScryptedDeviceType[\"Siren\"] = \"Siren\";\n ScryptedDeviceType[\"AirPurifier\"] = \"AirPurifier\";\n ScryptedDeviceType[\"Internet\"] = \"Internet\";\n ScryptedDeviceType[\"Network\"] = \"Network\";\n ScryptedDeviceType[\"Bridge\"] = \"Bridge\";\n ScryptedDeviceType[\"Unknown\"] = \"Unknown\";\n})(ScryptedDeviceType || (exports.ScryptedDeviceType = ScryptedDeviceType = {}));\nvar HumidityMode;\n(function (HumidityMode) {\n HumidityMode[\"Humidify\"] = \"Humidify\";\n HumidityMode[\"Dehumidify\"] = \"Dehumidify\";\n HumidityMode[\"Auto\"] = \"Auto\";\n HumidityMode[\"Off\"] = \"Off\";\n})(HumidityMode || (exports.HumidityMode = HumidityMode = {}));\nvar FanMode;\n(function (FanMode) {\n FanMode[\"Auto\"] = \"Auto\";\n FanMode[\"Manual\"] = \"Manual\";\n})(FanMode || (exports.FanMode = FanMode = {}));\nvar TemperatureUnit;\n(function (TemperatureUnit) {\n TemperatureUnit[\"C\"] = \"C\";\n TemperatureUnit[\"F\"] = \"F\";\n})(TemperatureUnit || (exports.TemperatureUnit = TemperatureUnit = {}));\nvar ThermostatMode;\n(function (ThermostatMode) {\n ThermostatMode[\"Off\"] = \"Off\";\n ThermostatMode[\"Cool\"] = \"Cool\";\n ThermostatMode[\"Heat\"] = \"Heat\";\n ThermostatMode[\"HeatCool\"] = \"HeatCool\";\n ThermostatMode[\"Auto\"] = \"Auto\";\n ThermostatMode[\"FanOnly\"] = \"FanOnly\";\n ThermostatMode[\"Purifier\"] = \"Purifier\";\n ThermostatMode[\"Eco\"] = \"Eco\";\n ThermostatMode[\"Dry\"] = \"Dry\";\n ThermostatMode[\"On\"] = \"On\";\n})(ThermostatMode || (exports.ThermostatMode = ThermostatMode = {}));\nvar PanTiltZoomMovement;\n(function (PanTiltZoomMovement) {\n PanTiltZoomMovement[\"Absolute\"] = \"Absolute\";\n PanTiltZoomMovement[\"Relative\"] = \"Relative\";\n PanTiltZoomMovement[\"Continuous\"] = \"Continuous\";\n PanTiltZoomMovement[\"Preset\"] = \"Preset\";\n PanTiltZoomMovement[\"Home\"] = \"Home\";\n})(PanTiltZoomMovement || (exports.PanTiltZoomMovement = PanTiltZoomMovement = {}));\nvar LockState;\n(function (LockState) {\n LockState[\"Locked\"] = \"Locked\";\n LockState[\"Unlocked\"] = \"Unlocked\";\n LockState[\"Jammed\"] = \"Jammed\";\n})(LockState || (exports.LockState = LockState = {}));\nvar ChargeState;\n(function (ChargeState) {\n ChargeState[\"Trickle\"] = \"trickle\";\n ChargeState[\"Charging\"] = \"charging\";\n ChargeState[\"NotCharging\"] = \"not-charging\";\n})(ChargeState || (exports.ChargeState = ChargeState = {}));\nvar AirPurifierStatus;\n(function (AirPurifierStatus) {\n AirPurifierStatus[\"Inactive\"] = \"Inactive\";\n AirPurifierStatus[\"Idle\"] = \"Idle\";\n AirPurifierStatus[\"Active\"] = \"Active\";\n AirPurifierStatus[\"ActiveNightMode\"] = \"ActiveNightMode\";\n})(AirPurifierStatus || (exports.AirPurifierStatus = AirPurifierStatus = {}));\nvar AirPurifierMode;\n(function (AirPurifierMode) {\n AirPurifierMode[\"Manual\"] = \"Manual\";\n AirPurifierMode[\"Automatic\"] = \"Automatic\";\n})(AirPurifierMode || (exports.AirPurifierMode = AirPurifierMode = {}));\nvar AirQuality;\n(function (AirQuality) {\n AirQuality[\"Unknown\"] = \"Unknown\";\n AirQuality[\"Excellent\"] = \"Excellent\";\n AirQuality[\"Good\"] = \"Good\";\n AirQuality[\"Fair\"] = \"Fair\";\n AirQuality[\"Inferior\"] = \"Inferior\";\n AirQuality[\"Poor\"] = \"Poor\";\n})(AirQuality || (exports.AirQuality = AirQuality = {}));\nvar SecuritySystemMode;\n(function (SecuritySystemMode) {\n SecuritySystemMode[\"Disarmed\"] = \"Disarmed\";\n SecuritySystemMode[\"HomeArmed\"] = \"HomeArmed\";\n SecuritySystemMode[\"AwayArmed\"] = \"AwayArmed\";\n SecuritySystemMode[\"NightArmed\"] = \"NightArmed\";\n})(SecuritySystemMode || (exports.SecuritySystemMode = SecuritySystemMode = {}));\nvar SecuritySystemObstruction;\n(function (SecuritySystemObstruction) {\n SecuritySystemObstruction[\"Sensor\"] = \"Sensor\";\n SecuritySystemObstruction[\"Occupied\"] = \"Occupied\";\n SecuritySystemObstruction[\"Time\"] = \"Time\";\n SecuritySystemObstruction[\"Error\"] = \"Error\";\n})(SecuritySystemObstruction || (exports.SecuritySystemObstruction = SecuritySystemObstruction = {}));\nvar MediaPlayerState;\n(function (MediaPlayerState) {\n MediaPlayerState[\"Idle\"] = \"Idle\";\n MediaPlayerState[\"Playing\"] = \"Playing\";\n MediaPlayerState[\"Paused\"] = \"Paused\";\n MediaPlayerState[\"Buffering\"] = \"Buffering\";\n})(MediaPlayerState || (exports.MediaPlayerState = MediaPlayerState = {}));\nvar ScryptedInterface;\n(function (ScryptedInterface) {\n ScryptedInterface[\"ScryptedDevice\"] = \"ScryptedDevice\";\n ScryptedInterface[\"ScryptedPlugin\"] = \"ScryptedPlugin\";\n ScryptedInterface[\"ScryptedPluginRuntime\"] = \"ScryptedPluginRuntime\";\n ScryptedInterface[\"OnOff\"] = \"OnOff\";\n ScryptedInterface[\"Brightness\"] = \"Brightness\";\n ScryptedInterface[\"ColorSettingTemperature\"] = \"ColorSettingTemperature\";\n ScryptedInterface[\"ColorSettingRgb\"] = \"ColorSettingRgb\";\n ScryptedInterface[\"ColorSettingHsv\"] = \"ColorSettingHsv\";\n ScryptedInterface[\"Buttons\"] = \"Buttons\";\n ScryptedInterface[\"PressButtons\"] = \"PressButtons\";\n ScryptedInterface[\"Sensors\"] = \"Sensors\";\n ScryptedInterface[\"Notifier\"] = \"Notifier\";\n ScryptedInterface[\"StartStop\"] = \"StartStop\";\n ScryptedInterface[\"Pause\"] = \"Pause\";\n ScryptedInterface[\"Dock\"] = \"Dock\";\n ScryptedInterface[\"TemperatureSetting\"] = \"TemperatureSetting\";\n ScryptedInterface[\"Thermometer\"] = \"Thermometer\";\n ScryptedInterface[\"HumiditySensor\"] = \"HumiditySensor\";\n ScryptedInterface[\"Camera\"] = \"Camera\";\n ScryptedInterface[\"Microphone\"] = \"Microphone\";\n ScryptedInterface[\"AudioVolumeControl\"] = \"AudioVolumeControl\";\n ScryptedInterface[\"Display\"] = \"Display\";\n ScryptedInterface[\"VideoCamera\"] = \"VideoCamera\";\n ScryptedInterface[\"VideoCameraMask\"] = \"VideoCameraMask\";\n ScryptedInterface[\"VideoTextOverlays\"] = \"VideoTextOverlays\";\n ScryptedInterface[\"VideoRecorder\"] = \"VideoRecorder\";\n ScryptedInterface[\"VideoRecorderManagement\"] = \"VideoRecorderManagement\";\n ScryptedInterface[\"PanTiltZoom\"] = \"PanTiltZoom\";\n ScryptedInterface[\"EventRecorder\"] = \"EventRecorder\";\n ScryptedInterface[\"VideoClips\"] = \"VideoClips\";\n ScryptedInterface[\"VideoCameraConfiguration\"] = \"VideoCameraConfiguration\";\n ScryptedInterface[\"Intercom\"] = \"Intercom\";\n ScryptedInterface[\"Lock\"] = \"Lock\";\n ScryptedInterface[\"PasswordStore\"] = \"PasswordStore\";\n ScryptedInterface[\"Scene\"] = \"Scene\";\n ScryptedInterface[\"Entry\"] = \"Entry\";\n ScryptedInterface[\"EntrySensor\"] = \"EntrySensor\";\n ScryptedInterface[\"DeviceProvider\"] = \"DeviceProvider\";\n ScryptedInterface[\"DeviceDiscovery\"] = \"DeviceDiscovery\";\n ScryptedInterface[\"DeviceCreator\"] = \"DeviceCreator\";\n ScryptedInterface[\"Battery\"] = \"Battery\";\n ScryptedInterface[\"Charger\"] = \"Charger\";\n ScryptedInterface[\"Reboot\"] = \"Reboot\";\n ScryptedInterface[\"Refresh\"] = \"Refresh\";\n ScryptedInterface[\"MediaPlayer\"] = \"MediaPlayer\";\n ScryptedInterface[\"Online\"] = \"Online\";\n ScryptedInterface[\"BufferConverter\"] = \"BufferConverter\";\n ScryptedInterface[\"MediaConverter\"] = \"MediaConverter\";\n ScryptedInterface[\"Settings\"] = \"Settings\";\n ScryptedInterface[\"BinarySensor\"] = \"BinarySensor\";\n ScryptedInterface[\"TamperSensor\"] = \"TamperSensor\";\n ScryptedInterface[\"Sleep\"] = \"Sleep\";\n ScryptedInterface[\"PowerSensor\"] = \"PowerSensor\";\n ScryptedInterface[\"AudioSensor\"] = \"AudioSensor\";\n ScryptedInterface[\"MotionSensor\"] = \"MotionSensor\";\n ScryptedInterface[\"AmbientLightSensor\"] = \"AmbientLightSensor\";\n ScryptedInterface[\"OccupancySensor\"] = \"OccupancySensor\";\n ScryptedInterface[\"FloodSensor\"] = \"FloodSensor\";\n ScryptedInterface[\"UltravioletSensor\"] = \"UltravioletSensor\";\n ScryptedInterface[\"LuminanceSensor\"] = \"LuminanceSensor\";\n ScryptedInterface[\"PositionSensor\"] = \"PositionSensor\";\n ScryptedInterface[\"SecuritySystem\"] = \"SecuritySystem\";\n ScryptedInterface[\"PM10Sensor\"] = \"PM10Sensor\";\n ScryptedInterface[\"PM25Sensor\"] = \"PM25Sensor\";\n ScryptedInterface[\"VOCSensor\"] = \"VOCSensor\";\n ScryptedInterface[\"NOXSensor\"] = \"NOXSensor\";\n ScryptedInterface[\"CO2Sensor\"] = \"CO2Sensor\";\n ScryptedInterface[\"AirQualitySensor\"] = \"AirQualitySensor\";\n ScryptedInterface[\"AirPurifier\"] = \"AirPurifier\";\n ScryptedInterface[\"FilterMaintenance\"] = \"FilterMaintenance\";\n ScryptedInterface[\"Readme\"] = \"Readme\";\n ScryptedInterface[\"OauthClient\"] = \"OauthClient\";\n ScryptedInterface[\"MixinProvider\"] = \"MixinProvider\";\n ScryptedInterface[\"HttpRequestHandler\"] = \"HttpRequestHandler\";\n ScryptedInterface[\"EngineIOHandler\"] = \"EngineIOHandler\";\n ScryptedInterface[\"PushHandler\"] = \"PushHandler\";\n ScryptedInterface[\"Program\"] = \"Program\";\n ScryptedInterface[\"Scriptable\"] = \"Scriptable\";\n ScryptedInterface[\"ClusterForkInterface\"] = \"ClusterForkInterface\";\n ScryptedInterface[\"ObjectTracker\"] = \"ObjectTracker\";\n ScryptedInterface[\"ObjectDetector\"] = \"ObjectDetector\";\n ScryptedInterface[\"ObjectDetection\"] = \"ObjectDetection\";\n ScryptedInterface[\"ObjectDetectionPreview\"] = \"ObjectDetectionPreview\";\n ScryptedInterface[\"ObjectDetectionGenerator\"] = \"ObjectDetectionGenerator\";\n ScryptedInterface[\"HumiditySetting\"] = \"HumiditySetting\";\n ScryptedInterface[\"Fan\"] = \"Fan\";\n ScryptedInterface[\"RTCSignalingChannel\"] = \"RTCSignalingChannel\";\n ScryptedInterface[\"RTCSignalingClient\"] = \"RTCSignalingClient\";\n ScryptedInterface[\"LauncherApplication\"] = \"LauncherApplication\";\n ScryptedInterface[\"ScryptedUser\"] = \"ScryptedUser\";\n ScryptedInterface[\"VideoFrameGenerator\"] = \"VideoFrameGenerator\";\n ScryptedInterface[\"StreamService\"] = \"StreamService\";\n ScryptedInterface[\"TTY\"] = \"TTY\";\n ScryptedInterface[\"TTYSettings\"] = \"TTYSettings\";\n ScryptedInterface[\"ScryptedSystemDevice\"] = \"ScryptedSystemDevice\";\n ScryptedInterface[\"ScryptedDeviceCreator\"] = \"ScryptedDeviceCreator\";\n ScryptedInterface[\"ScryptedSettings\"] = \"ScryptedSettings\";\n})(ScryptedInterface || (exports.ScryptedInterface = ScryptedInterface = {}));\nvar ScryptedMimeTypes;\n(function (ScryptedMimeTypes) {\n ScryptedMimeTypes[\"Url\"] = \"text/x-uri\";\n ScryptedMimeTypes[\"InsecureLocalUrl\"] = \"text/x-insecure-local-uri\";\n ScryptedMimeTypes[\"LocalUrl\"] = \"text/x-local-uri\";\n ScryptedMimeTypes[\"ServerId\"] = \"text/x-server-id\";\n ScryptedMimeTypes[\"PushEndpoint\"] = \"text/x-push-endpoint\";\n ScryptedMimeTypes[\"SchemePrefix\"] = \"x-scrypted/x-scrypted-scheme-\";\n ScryptedMimeTypes[\"MediaStreamUrl\"] = \"text/x-media-url\";\n ScryptedMimeTypes[\"MediaObject\"] = \"x-scrypted/x-scrypted-media-object\";\n ScryptedMimeTypes[\"RequestMediaObject\"] = \"x-scrypted/x-scrypted-request-media-object\";\n ScryptedMimeTypes[\"RequestMediaStream\"] = \"x-scrypted/x-scrypted-request-stream\";\n ScryptedMimeTypes[\"MediaStreamFeedback\"] = \"x-scrypted/x-media-stream-feedback\";\n ScryptedMimeTypes[\"FFmpegInput\"] = \"x-scrypted/x-ffmpeg-input\";\n ScryptedMimeTypes[\"FFmpegTranscodeStream\"] = \"x-scrypted/x-ffmpeg-transcode-stream\";\n ScryptedMimeTypes[\"RTCSignalingChannel\"] = \"x-scrypted/x-scrypted-rtc-signaling-channel\";\n ScryptedMimeTypes[\"RTCSignalingSession\"] = \"x-scrypted/x-scrypted-rtc-signaling-session\";\n ScryptedMimeTypes[\"RTCConnectionManagement\"] = \"x-scrypted/x-scrypted-rtc-connection-management\";\n ScryptedMimeTypes[\"Image\"] = \"x-scrypted/x-scrypted-image\";\n})(ScryptedMimeTypes || (exports.ScryptedMimeTypes = ScryptedMimeTypes = {}));\n//# sourceMappingURL=index.js.map","import sdk, {\n ScryptedDeviceBase,\n Settings,\n Setting,\n HttpRequestHandler,\n HttpRequest,\n HttpResponse,\n ScryptedInterface,\n ScryptedDeviceType,\n ObjectsDetected,\n ObjectDetector,\n} from '@scrypted/sdk';\nimport fs from 'fs';\nimport path from 'path';\n\nconst { systemManager, deviceManager, mediaManager } = sdk;\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst LABELS = ['person', 'animal', 'face', 'vehicle', 'plate', 'package', 'discard'] as const;\ntype Label = typeof LABELS[number];\n\nconst CAPTURE_CLASSES = new Set(['person', 'cat', 'dog', 'animal', 'bird', 'face', 'vehicle', 'car', 'truck', 'bus', 'motorcycle', 'bicycle', 'plate', 'package']);\n\nconst RATE_OPTIONS = ['disabled', '1 per minute', '1 per 10 seconds', 'every detection'] as const;\ntype RateOption = typeof RATE_OPTIONS[number];\n\nconst RATE_MS: Record<RateOption, number> = {\n 'disabled': Infinity,\n '1 per minute': 60_000,\n '1 per 10 seconds': 10_000,\n 'every detection': 0,\n};\n\nconst MAX_CAPTURES = 2000;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface CaptureRecord {\n id: string;\n cameraId: string;\n cameraName: string;\n timestamp: number;\n detectedClass: string;\n score: number;\n boundingBox: number[]; // [x, y, w, h] in pixels\n inputDimensions: number[]; // [width, height]\n detectionId?: string;\n label?: Label; // set after review\n reviewed: boolean;\n}\n\n// ─── Plugin ───────────────────────────────────────────────────────────────────\n\nclass DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpRequestHandler {\n // Map<cameraId, lastCaptureTimestamp>\n private lastCapture = new Map<string, number>();\n // Map<captureId, CaptureRecord>\n private captures = new Map<string, CaptureRecord>();\n // Active event listeners\n private listeners: (() => void)[] = [];\n // Directory for storing images on disk\n private imgDir: string;\n\n constructor(nativeId?: string) {\n super(nativeId);\n // Use a stable directory inside the plugin's volume\n this.imgDir = path.join(process.env.SCRYPTED_PLUGIN_VOLUME || '/tmp', 'detection-trainer-images');\n try { fs.mkdirSync(this.imgDir, { recursive: true }); } catch {}\n this.loadState();\n this.registerListeners();\n }\n\n // ── Persistence ──────────────────────────────────────────────────────────\n\n private loadState() {\n try {\n const raw = this.storage.getItem('captures');\n if (raw) {\n const arr: CaptureRecord[] = JSON.parse(raw);\n for (const r of arr) this.captures.set(r.id, r);\n this.console.log(`Loaded ${this.captures.size} captures from storage.`);\n }\n } catch (e) {\n this.console.warn('Could not load captures from storage:', e);\n }\n // Clean up any old base64 image entries from previous versions\n for (const [id] of this.captures) {\n try { this.storage.removeItem(`img:${id}`); } catch {}\n }\n }\n\n private saveCaptures() {\n try {\n this.storage.setItem('captures', JSON.stringify([...this.captures.values()]));\n } catch (e) {\n this.console.warn('Could not save captures:', e);\n }\n }\n\n private imgPath(id: string): string {\n return path.join(this.imgDir, `${id}.jpg`);\n }\n\n private saveImage(id: string, buf: Buffer) {\n try {\n fs.writeFileSync(this.imgPath(id), buf);\n } catch (e) {\n this.console.warn(`Could not save image ${id}:`, e);\n }\n }\n\n private loadImage(id: string): Buffer | undefined {\n try {\n const p = this.imgPath(id);\n if (fs.existsSync(p)) return fs.readFileSync(p);\n } catch {}\n return undefined;\n }\n\n private deleteCapture(id: string) {\n try { fs.unlinkSync(this.imgPath(id)); } catch {}\n this.captures.delete(id);\n this.saveCaptures();\n }\n\n // ── Settings ─────────────────────────────────────────────────────────────\n\n async getSettings(): Promise<Setting[]> {\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n );\n\n let uiUrl: string;\n try {\n uiUrl = await sdk.endpointManager.getLocalEndpoint(undefined, { public: true });\n } catch {\n uiUrl = '/endpoint/scrypted-detection-trainer/public/';\n }\n\n const settings: Setting[] = [\n {\n key: 'info',\n title: 'Detection Trainer',\n 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).`,\n readonly: true,\n value: '',\n },\n {\n key: 'open_ui',\n title: 'Review UI',\n description: 'Open the detection review and labeling interface.',\n type: 'html',\n readonly: true,\n value: `<a href=\"${uiUrl}\" target=\"_blank\" style=\"display:inline-block;padding:8px 16px;background:#1a4d8a;color:#fff;border-radius:6px;text-decoration:none;font-size:13px;\">Open Review UI ↗</a>`,\n },\n {\n key: 'autoCapture',\n title: 'Auto-Capture',\n description: 'Automatically capture detections in the background. Disable to use manual browsing only.',\n type: 'boolean',\n value: (this.storage.getItem('autoCapture') ?? 'true'),\n },\n ];\n\n for (const cam of cameras) {\n const key = `rate:${cam.id}`;\n settings.push({\n key,\n title: cam.name,\n group: 'Capture Rate per Camera',\n description: 'How often to capture detections from this camera.',\n value: this.storage.getItem(key) || '1 per minute',\n choices: [...RATE_OPTIONS],\n });\n }\n\n return settings;\n }\n\n async putSetting(key: string, value: string) {\n if (key === 'open_ui' || key === 'ui_link' || key === 'info') return;\n this.storage.setItem(key, value);\n if (key.startsWith('rate:')) {\n // Re-register listeners when rates change\n this.registerListeners();\n }\n }\n\n // ── Listeners ─────────────────────────────────────────────────────────────\n\n private registerListeners() {\n // Remove old listeners\n for (const remove of this.listeners) remove();\n this.listeners = [];\n\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n );\n\n for (const cam of cameras) {\n const rateKey = `rate:${cam.id}`;\n const rateLabel = (this.storage.getItem(rateKey) || '1 per minute') as RateOption;\n if (rateLabel === 'disabled') continue;\n\n const listener = cam.listen(ScryptedInterface.ObjectDetector, async (source, details, data) => {\n await this.onDetection(cam.id, cam.name, data as ObjectsDetected, RATE_MS[rateLabel]);\n });\n this.listeners.push(() => listener.removeListener());\n }\n\n this.console.log(`Listening to ${this.listeners.length} camera(s).`);\n }\n\n // ── Detection Handler ─────────────────────────────────────────────────────\n\n private async onDetection(cameraId: string, cameraName: string, data: ObjectsDetected, rateLimitMs: number) {\n if ((this.storage.getItem('autoCapture') ?? 'true') === 'false') return;\n if (!data?.detections?.length || !data.inputDimensions) return;\n\n // Rate limit per camera\n const now = Date.now();\n const last = this.lastCapture.get(cameraId) || 0;\n if (now - last < rateLimitMs) return;\n\n // Filter to target classes\n const targets = data.detections.filter(d =>\n d.className && CAPTURE_CLASSES.has(d.className.toLowerCase()) && d.boundingBox\n );\n if (!targets.length) return;\n\n // Pick the highest-confidence target detection\n const best = targets.sort((a, b) => (b.score || 0) - (a.score || 0))[0];\n\n // Enforce max storage\n if (this.captures.size >= MAX_CAPTURES) {\n // Evict oldest unreviewed capture\n const oldest = [...this.captures.values()]\n .filter(c => !c.reviewed)\n .sort((a, b) => a.timestamp - b.timestamp)[0];\n if (oldest) this.deleteCapture(oldest.id);\n else return; // All reviewed, don't evict labeled data\n }\n\n this.lastCapture.set(cameraId, now);\n\n // Try to get the detection image\n let jpeg: Buffer | undefined;\n try {\n if (data.detectionId) {\n const cam = systemManager.getDeviceById(cameraId) as unknown as ObjectDetector;\n const mo = await cam.getDetectionInput(data.detectionId);\n jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n }\n } catch (e) {\n this.console.warn(`Could not get detection image for ${cameraName}:`, e);\n }\n\n if (!jpeg) return; // Skip if no image\n\n const id = `${now}-${Math.random().toString(36).slice(2, 8)}`;\n const record: CaptureRecord = {\n id,\n cameraId,\n cameraName,\n timestamp: now,\n detectedClass: best.className!,\n score: best.score || 0,\n boundingBox: best.boundingBox as number[],\n inputDimensions: data.inputDimensions as number[],\n detectionId: data.detectionId,\n reviewed: false,\n };\n\n this.captures.set(id, record);\n this.saveImage(id, jpeg);\n this.saveCaptures();\n\n this.console.log(`Captured ${best.className} (${Math.round((best.score || 0) * 100)}%) from ${cameraName} [${this.captures.size} total]`);\n }\n\n // ── HTTP Handler ──────────────────────────────────────────────────────────\n\n async onRequest(request: HttpRequest, response: HttpResponse) {\n const url = new URL(request.url, 'http://localhost');\n const path = url.pathname.replace(request.rootPath, '');\n\n // Serve browse event image via getVideoClipThumbnail\n if (path === '/api/browse-img') {\n const params = new URL(request.url, 'http://localhost').searchParams;\n const cameraId = params.get('cameraId')?.replace(/[^a-zA-Z0-9_\\-]/g, '');\n const thumbnailId = params.get('thumbnailId')?.replace(/[^a-zA-Z0-9_\\-:.]/g, '');\n if (!cameraId || !thumbnailId) return response.send('Missing params', { code: 400 });\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n const mo = await cam.getVideoClipThumbnail(thumbnailId);\n const jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n return response.send(jpeg, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=3600' } });\n } catch (e) {\n return response.send('Image unavailable', { code: 404 });\n }\n }\n\n // Serve image\n if (path.startsWith('/img/')) {\n const id = path.slice(5).replace(/[^a-zA-Z0-9_\\-]/g, ''); // sanitize\n const img = this.loadImage(id);\n if (!img) return response.send('Not found', { code: 404 });\n return response.send(img, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=3600' } });\n }\n\n // API: label a capture\n if (path === '/api/label' && request.body) {\n const rawBody = request.body as any;\n const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));\n const record = this.captures.get(body.id);\n if (!record) return response.send('Not found', { code: 404 });\n record.label = body.label as Label;\n record.reviewed = true;\n if (body.label === 'discard') {\n this.deleteCapture(body.id);\n } else {\n this.captures.set(body.id, record);\n this.saveCaptures();\n }\n return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: list cameras for browse\n if (path === '/api/cameras') {\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n )\n .map(d => ({ id: d.id, name: d.name }));\n return response.send(JSON.stringify(cameras), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: browse recent events for a camera\n if (path === '/api/browse') {\n const params = new URL(request.url, 'http://localhost').searchParams;\n const cameraId = params.get('cameraId');\n const hours = parseInt(params.get('hours') || '24');\n if (!cameraId) return response.send('Missing cameraId', { code: 400 });\n\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n if (!cam) return response.send('Camera not found', { code: 404 });\n\n const endTime = Date.now();\n const startTime = endTime - hours * 3600 * 1000;\n const clips = await cam.getVideoClips({ startTime, endTime });\n\n const events = (clips || [])\n .filter((c: any) => c.detectionClasses?.length && c.thumbnailId)\n .slice(0, 100)\n .map((c: any) => ({\n clipId: c.id,\n thumbnailId: c.thumbnailId,\n timestamp: c.startTime,\n detectionClasses: c.detectionClasses || [],\n // bounding box not available at clip level — use full frame\n boundingBox: null,\n inputDimensions: null,\n cameraId,\n cameraName: cam.name,\n }));\n\n return response.send(JSON.stringify(events), { headers: { 'Content-Type': 'application/json' } });\n } catch (e: any) {\n return response.send(JSON.stringify({ error: e.message }), { headers: { 'Content-Type': 'application/json' }, code: 500 });\n }\n }\n\n // API: add a browsed event directly to dataset as labeled\n if (path === '/api/add-event' && request.body) {\n const rawBody = request.body as any;\n const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));\n const { cameraId, cameraName, thumbnailId, timestamp, detectedClass, boundingBox, inputDimensions, label } = body;\n\n if (!label || label === 'discard') return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });\n\n // Get image via thumbnail\n let jpeg: Buffer | undefined;\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n const mo = await cam.getVideoClipThumbnail(thumbnailId);\n jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n } catch (e) {\n this.console.warn(`Could not get thumbnail for browse event:`, e);\n }\n\n if (!jpeg) return response.send(JSON.stringify({ error: 'Could not retrieve image' }), { headers: { 'Content-Type': 'application/json' }, code: 500 });\n\n const id = `browse-${timestamp}-${Math.random().toString(36).slice(2, 6)}`;\n // For clips we don't have per-detection bounding boxes — store full frame dimensions\n const record: CaptureRecord = {\n id, cameraId, cameraName, timestamp,\n detectedClass: detectedClass || 'unknown',\n score: 1,\n boundingBox: boundingBox || [0, 0, inputDimensions?.[0] || 1920, inputDimensions?.[1] || 1080],\n inputDimensions: inputDimensions || [1920, 1080],\n reviewed: true, label,\n };\n this.captures.set(id, record);\n this.saveImage(id, jpeg);\n this.saveCaptures();\n\n return response.send(JSON.stringify({ ok: true, id }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: get pending captures\n if (path === '/api/pending') {\n const pending = [...this.captures.values()]\n .filter(c => !c.reviewed)\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, 50);\n return response.send(JSON.stringify(pending), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: get labeled captures\n if (path === '/api/labeled') {\n const page = parseInt(new URL(request.url, 'http://localhost').searchParams.get('page') || '0');\n const pageSize = 50;\n const labeled = [...this.captures.values()]\n .filter(c => c.reviewed)\n .sort((a, b) => b.timestamp - a.timestamp);\n const slice = labeled.slice(page * pageSize, (page + 1) * pageSize);\n return response.send(JSON.stringify({ items: slice, total: labeled.length, page, pageSize }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: stats\n if (path === '/api/stats') {\n const all = [...this.captures.values()];\n const stats = {\n total: all.length,\n pending: all.filter(c => !c.reviewed).length,\n labeled: all.filter(c => c.reviewed && c.label !== 'discard').length,\n byLabel: {} as Record<string, number>,\n byCamera: {} as Record<string, number>,\n byDetectedClass: {} as Record<string, number>,\n };\n for (const r of all) {\n if (r.label) stats.byLabel[r.label] = (stats.byLabel[r.label] || 0) + 1;\n stats.byCamera[r.cameraName] = (stats.byCamera[r.cameraName] || 0) + 1;\n stats.byDetectedClass[r.detectedClass] = (stats.byDetectedClass[r.detectedClass] || 0) + 1;\n }\n return response.send(JSON.stringify(stats), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: export YOLO dataset\n if (path === '/api/export') {\n const labeled = [...this.captures.values()].filter(c => c.reviewed && c.label && c.label !== 'discard');\n if (!labeled.length) return response.send(JSON.stringify({ error: 'No labeled data yet' }), { headers: { 'Content-Type': 'application/json' }, code: 400 });\n\n const classMap: Record<Label, number> = {\n person: 0, animal: 1, face: 2, vehicle: 3, plate: 4, package: 5, discard: -1,\n };\n\n // Build a simple tarball-like structure as JSON for download\n const dataset: { filename: string; content: string; encoding: string }[] = [];\n\n for (const record of labeled) {\n const img = this.loadImage(record.id);\n if (!img) continue;\n\n const fname = `${record.id}`;\n dataset.push({ filename: `images/${fname}.jpg`, content: img.toString('base64'), encoding: 'base64' });\n\n const [x, y, w, h] = record.boundingBox;\n const [imgW, imgH] = record.inputDimensions;\n const cx = (x + w / 2) / imgW;\n const cy = (y + h / 2) / imgH;\n const nw = w / imgW;\n const nh = h / imgH;\n const classId = classMap[record.label!];\n const labelLine = `${classId} ${cx.toFixed(6)} ${cy.toFixed(6)} ${nw.toFixed(6)} ${nh.toFixed(6)}\\n`;\n dataset.push({ filename: `labels/${fname}.txt`, content: labelLine, encoding: 'utf8' });\n }\n\n const yaml = [\n 'path: dataset',\n 'train: images',\n 'val: images',\n '',\n 'nc: 6',\n \"names: ['person', 'animal', 'face', 'vehicle', 'plate', 'package']\",\n '',\n '# Generated by Scrypted Detection Trainer',\n `# ${labeled.length} labeled samples`,\n ].join('\\n');\n dataset.push({ filename: 'data.yaml', content: yaml, encoding: 'utf8' });\n\n return response.send(JSON.stringify({ files: dataset, count: labeled.length }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Serve Web UI\n if (path === '/' || path === '' || path === '/index.html') {\n return response.send(this.renderUI(), { headers: { 'Content-Type': 'text/html' } });\n }\n\n response.send('Not found', { code: 404 });\n }\n\n // ── Web UI ────────────────────────────────────────────────────────────────\n\n private renderUI(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Detection Trainer</title>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js\"></script>\n<style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f0f0f; color: #e8e8e8; min-height: 100vh; }\n header { background: #1a1a1a; border-bottom: 1px solid #333; padding: 16px 24px; display: flex; align-items: center; justify-content: space-between; }\n header h1 { font-size: 18px; font-weight: 600; color: #fff; }\n .stats { display: flex; gap: 20px; font-size: 13px; color: #aaa; }\n .stat span { color: #fff; font-weight: 600; }\n .container { max-width: 1000px; margin: 0 auto; padding: 24px; }\n .card { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .card-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; display: flex; align-items: center; justify-content: space-between; }\n .card-header h2 { font-size: 15px; font-weight: 600; }\n .badge { background: #333; color: #aaa; font-size: 12px; padding: 2px 8px; border-radius: 20px; }\n .badge.orange { background: #3d2a00; color: #f90; }\n .badge.green { background: #0d2d0d; color: #4c4; }\n\n /* Detection card */\n .detection { display: grid; grid-template-columns: 420px 1fr; gap: 0; border-bottom: 1px solid #222; }\n .detection:last-child { border-bottom: none; }\n .detection-imgs { display: flex; gap: 8px; padding: 10px; background: #111; align-items: center; }\n .img-panel { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n .img-label { font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: .5px; }\n .det-canvas { border-radius: 6px; display: block; }\n .det-class-badge { display: inline-block; background: #3d2a00; color: #f90; font-size: 11px; padding: 2px 8px; border-radius: 4px; }\n .detection-info { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }\n .detection-meta { font-size: 12px; color: #888; display: flex; flex-wrap: wrap; gap: 10px; }\n .detection-meta strong { color: #ccc; }\n .label-buttons { display: flex; flex-wrap: wrap; gap: 8px; }\n .label-btn { padding: 7px 14px; border-radius: 8px; border: 1px solid #444; background: #222; color: #ccc; cursor: pointer; font-size: 13px; transition: all .15s; }\n .label-btn:hover { border-color: #666; background: #2a2a2a; color: #fff; }\n .label-btn.person { border-color: #2a6; color: #4d9; }\n .label-btn.person:hover { background: #0d2a1a; }\n .label-btn.animal { border-color: #a63; color: #d85; }\n .label-btn.animal:hover { background: #2a1a0d; }\n .label-btn.face { border-color: #49c; color: #6be; }\n .label-btn.face:hover { background: #0d1a2a; }\n .label-btn.vehicle { border-color: #76b; color: #99d; }\n .label-btn.vehicle:hover { background: #1a1a2a; }\n .label-btn.discard { border-color: #622; color: #a44; }\n .label-btn.discard:hover { background: #2a0d0d; }\n .detection.labeled { opacity: 0.4; pointer-events: none; }\n .labeled-tag { font-size: 11px; color: #4d9; background: #0d2a1a; border: 1px solid #2a6; padding: 2px 8px; border-radius: 4px; }\n\n /* Empty state */\n .empty { padding: 48px; text-align: center; color: #555; }\n .empty .icon { font-size: 48px; margin-bottom: 12px; }\n\n /* Export section */\n .export-btn { padding: 10px 20px; background: #1a4d8a; border: none; border-radius: 8px; color: #fff; cursor: pointer; font-size: 14px; font-weight: 500; }\n .export-btn:hover { background: #1e5ca0; }\n .export-btn:disabled { background: #333; color: #666; cursor: not-allowed; }\n .export-info { font-size: 13px; color: #888; padding: 12px 20px; }\n\n /* Progress bar */\n .progress { height: 4px; background: #222; border-radius: 2px; overflow: hidden; margin-top: 8px; }\n .progress-bar { height: 100%; background: #1a6; border-radius: 2px; transition: width .3s; }\n\n .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; }\n .toast.show { opacity: 1; }\n\n .tab-bar { display: flex; gap: 2px; padding: 12px 20px 0; border-bottom: 1px solid #2a2a2a; }\n .tab { padding: 8px 14px; font-size: 13px; color: #888; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }\n .tab.active { color: #fff; border-bottom-color: #4a9; }\n .tab-content { padding: 20px; }\n .tab-panel { display: none; }\n .tab-panel.active { display: block; }\n\n .breakdown-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }\n .breakdown-item { background: #222; border-radius: 8px; padding: 12px; }\n .breakdown-item .label { font-size: 12px; color: #888; margin-bottom: 4px; }\n .breakdown-item .value { font-size: 20px; font-weight: 600; color: #fff; }\n\n /* Lightbox */\n .lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.92); z-index: 1000; align-items: center; justify-content: center; flex-direction: column; gap: 12px; }\n .lightbox.open { display: flex; }\n .lightbox canvas { max-width: 92vw; max-height: 82vh; border-radius: 8px; cursor: zoom-out; }\n .lightbox-meta { color: #aaa; font-size: 13px; text-align: center; }\n .lightbox-close { position: absolute; top: 16px; right: 20px; font-size: 28px; color: #888; cursor: pointer; line-height: 1; }\n .lightbox-close:hover { color: #fff; }\n .det-canvas { cursor: zoom-in; }\n</style>\n</head>\n<body>\n<header>\n <h1>🎯 Detection Trainer</h1>\n <div class=\"stats\">\n <div>Pending <span id=\"stat-pending\">—</span></div>\n <div>Labeled <span id=\"stat-labeled\">—</span></div>\n <div>Total <span id=\"stat-total\">—</span></div>\n </div>\n</header>\n<div class=\"container\">\n\n <div class=\"card\">\n <div class=\"tab-bar\">\n <div class=\"tab active\" onclick=\"showTab('review')\">Review</div>\n <div class=\"tab\" onclick=\"showTab('browse')\">Browse Events</div>\n <div class=\"tab\" onclick=\"showTab('labeled')\">Labeled</div>\n <div class=\"tab\" onclick=\"showTab('stats')\">Stats</div>\n <div class=\"tab\" onclick=\"showTab('export')\">Export Dataset</div>\n </div>\n\n <!-- Review tab -->\n <div class=\"tab-panel active\" id=\"tab-review\">\n <div id=\"detections-list\"></div>\n </div>\n\n <!-- Browse tab -->\n <div class=\"tab-panel\" id=\"tab-browse\">\n <div class=\"tab-content\">\n <div style=\"display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:16px;\">\n <select id=\"browse-camera\" style=\"padding:8px 12px;background:#222;border:1px solid #444;color:#fff;border-radius:6px;font-size:13px;\">\n <option value=\"\">Select camera…</option>\n </select>\n <select id=\"browse-hours\" style=\"padding:8px 12px;background:#222;border:1px solid #444;color:#fff;border-radius:6px;font-size:13px;\">\n <option value=\"1\">Last 1 hour</option>\n <option value=\"6\">Last 6 hours</option>\n <option value=\"24\" selected>Last 24 hours</option>\n <option value=\"72\">Last 3 days</option>\n </select>\n <button class=\"export-btn\" onclick=\"loadBrowse()\" style=\"padding:8px 16px;\">Load Events</button>\n <span id=\"browse-status\" style=\"font-size:13px;color:#888;\"></span>\n </div>\n <div id=\"browse-list\"></div>\n </div>\n </div>\n\n <!-- Labeled tab -->\n <div class=\"tab-panel\" id=\"tab-labeled\">\n <div id=\"labeled-list\"></div>\n </div>\n <div class=\"tab-panel\" id=\"tab-stats\">\n <div class=\"tab-content\">\n <p style=\"font-size:13px;color:#888;margin-bottom:16px;\">Breakdown of captured and labeled detections.</p>\n <h3 style=\"font-size:13px;color:#aaa;margin-bottom:10px;\">By Detected Class (what the model said)</h3>\n <div class=\"breakdown-grid\" id=\"stats-detected\"></div>\n <h3 style=\"font-size:13px;color:#aaa;margin:20px 0 10px;\">By Corrected Label (what you said)</h3>\n <div class=\"breakdown-grid\" id=\"stats-label\"></div>\n <h3 style=\"font-size:13px;color:#aaa;margin:20px 0 10px;\">By Camera</h3>\n <div class=\"breakdown-grid\" id=\"stats-camera\"></div>\n </div>\n </div>\n\n <!-- Export tab -->\n <div class=\"tab-panel\" id=\"tab-export\">\n <div class=\"tab-content\">\n <p style=\"font-size:13px;color:#888;margin-bottom:16px;\">\n Exports a YOLO-format dataset (images + labels + data.yaml) as a downloadable bundle.\n Only labeled detections are included. Review more detections first to build a larger dataset.\n </p>\n <div id=\"export-stats\" class=\"export-info\">Loading…</div>\n <div style=\"display:flex;gap:12px;align-items:center;margin-top:12px;\">\n <button class=\"export-btn\" id=\"export-btn\" onclick=\"exportDataset()\">Download Dataset</button>\n <span id=\"export-status\" style=\"font-size:13px;color:#888;\"></span>\n </div>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"lightbox\" id=\"lightbox\" onclick=\"closeLightbox()\">\n <div class=\"lightbox-close\" onclick=\"closeLightbox()\">✕</div>\n <canvas id=\"lightbox-canvas\"></canvas>\n <div class=\"lightbox-meta\" id=\"lightbox-meta\"></div>\n</div>\n\n<div class=\"toast\" id=\"toast\"></div>\n\n<script>\nconst BASE = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname;\nlet pending = [];\nlet labeledCount = 0;\n\nfunction imgError(img) {\n img.parentElement.innerHTML = '<div style=\"padding:20px;color:#555;font-size:12px;text-align:center\">No image</div>';\n}\n\nfunction drawDetection(img, r) {\n const [bx, by, bw, bh] = r.boundingBox;\n const [iw, ih] = r.inputDimensions;\n\n // --- Full frame canvas with box overlay ---\n const fullCanvas = document.getElementById('canvas-full-' + r.id);\n if (fullCanvas) {\n const cw = fullCanvas.width, ch = fullCanvas.height;\n const scale = Math.min(cw / iw, ch / ih);\n const dw = iw * scale, dh = ih * scale;\n const ox = (cw - dw) / 2, oy = (ch - dh) / 2;\n const ctx = fullCanvas.getContext('2d');\n ctx.fillStyle = '#111';\n ctx.fillRect(0, 0, cw, ch);\n ctx.drawImage(img, ox, oy, dw, dh);\n // Draw bounding box\n const rx = ox + bx * scale, ry = oy + by * scale;\n const rw = bw * scale, rh = bh * scale;\n ctx.strokeStyle = '#f90';\n ctx.lineWidth = 2;\n ctx.strokeRect(rx, ry, rw, rh);\n // Label badge\n ctx.fillStyle = 'rgba(255,153,0,0.85)';\n ctx.fillRect(rx, ry - 18, rw, 18);\n ctx.fillStyle = '#000';\n ctx.font = 'bold 11px sans-serif';\n ctx.fillText(r.detectedClass + ' ' + Math.round(r.score * 100) + '%', rx + 3, ry - 4);\n }\n\n // --- Crop canvas ---\n const cropCanvas = document.getElementById('canvas-crop-' + r.id);\n if (cropCanvas) {\n const cc = cropCanvas.width, ch2 = cropCanvas.height;\n const ctx2 = cropCanvas.getContext('2d');\n ctx2.fillStyle = '#111';\n ctx2.fillRect(0, 0, cc, ch2);\n // Add padding around the crop\n const pad = Math.min(bw, bh) * 0.15;\n const sx = Math.max(0, bx - pad), sy = Math.max(0, by - pad);\n const sw = Math.min(iw - sx, bw + pad * 2), sh = Math.min(ih - sy, bh + pad * 2);\n const scale2 = Math.min(cc / sw, ch2 / sh);\n const dw2 = sw * scale2, dh2 = sh * scale2;\n const ox2 = (cc - dw2) / 2, oy2 = (ch2 - dh2) / 2;\n ctx2.drawImage(img, sx, sy, sw, sh, ox2, oy2, dw2, dh2);\n // Thin box outline on crop\n ctx2.strokeStyle = '#f90';\n ctx2.lineWidth = 1.5;\n const rx2 = ox2 + pad * scale2, ry2 = oy2 + pad * scale2;\n ctx2.strokeRect(rx2, ry2, bw * scale2, bh * scale2);\n }\n}\n\n// Cache loaded images so lightbox reuses them\nconst imgCache = new Map();\n\nfunction openLightbox(r) {\n const img = imgCache.get(r.id);\n if (!img) return;\n\n const lb = document.getElementById('lightbox');\n const lbCanvas = document.getElementById('lightbox-canvas');\n\n // Size canvas to image, capped at viewport\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.8;\n const [iw, ih] = r.inputDimensions;\n const scale = Math.min(maxW / iw, maxH / ih, 1);\n lbCanvas.width = Math.round(iw * scale);\n lbCanvas.height = Math.round(ih * scale);\n\n const ctx = lbCanvas.getContext('2d');\n ctx.drawImage(img, 0, 0, lbCanvas.width, lbCanvas.height);\n\n // Draw all bounding boxes for this detection (primary + others in same event if available)\n const boxes = r.allDetections || [{ boundingBox: r.boundingBox, detectedClass: r.detectedClass, score: r.score }];\n const colors = ['#f90', '#4af', '#f44', '#4f4', '#f4f', '#ff4'];\n boxes.forEach((d, i) => {\n const [bx, by, bw, bh] = d.boundingBox;\n const color = colors[i % colors.length];\n const rx = bx * scale, ry = by * scale, rw = bw * scale, rh = bh * scale;\n ctx.strokeStyle = color;\n ctx.lineWidth = 2;\n ctx.strokeRect(rx, ry, rw, rh);\n const label = d.detectedClass + ' ' + Math.round((d.score || 0) * 100) + '%';\n const textW = ctx.measureText(label).width + 8;\n ctx.fillStyle = color;\n ctx.globalAlpha = 0.85;\n ctx.fillRect(rx, Math.max(0, ry - 20), textW, 20);\n ctx.globalAlpha = 1;\n ctx.fillStyle = '#000';\n ctx.font = 'bold 12px sans-serif';\n ctx.fillText(label, rx + 4, Math.max(14, ry - 4));\n });\n\n document.getElementById('lightbox-meta').textContent =\n r.cameraName + ' · ' + new Date(r.timestamp).toLocaleString() + ' · ' + iw + '×' + ih;\n lb.classList.add('open');\n document.addEventListener('keydown', lbKeyHandler);\n}\n\nfunction openLightboxImg(img, cameraName, timestamp) {\n const lb = document.getElementById('lightbox');\n const lbCanvas = document.getElementById('lightbox-canvas');\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.8;\n const scale = Math.min(maxW / img.naturalWidth, maxH / img.naturalHeight, 1);\n lbCanvas.width = Math.round(img.naturalWidth * scale);\n lbCanvas.height = Math.round(img.naturalHeight * scale);\n const ctx = lbCanvas.getContext('2d');\n ctx.drawImage(img, 0, 0, lbCanvas.width, lbCanvas.height);\n document.getElementById('lightbox-meta').textContent =\n cameraName + ' · ' + new Date(timestamp).toLocaleString();\n lb.classList.add('open');\n document.addEventListener('keydown', lbKeyHandler);\n}\n\nfunction closeLightbox() {\n document.getElementById('lightbox').classList.remove('open');\n document.removeEventListener('keydown', lbKeyHandler);\n}\n\nfunction lbKeyHandler(e) {\n if (e.key === 'Escape') closeLightbox();\n}\n\nfunction showTab(name) {\n const names = ['review', 'browse', 'labeled', 'stats', 'export'];\n document.querySelectorAll('.tab').forEach((t, i) => {\n t.classList.toggle('active', names[i] === name);\n });\n document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));\n document.getElementById('tab-' + name).classList.add('active');\n if (name === 'stats') loadStats();\n if (name === 'export') loadExportInfo();\n if (name === 'labeled') loadLabeled(0);\n if (name === 'browse') initBrowse();\n}\n\nasync function initBrowse() {\n const sel = document.getElementById('browse-camera');\n if (sel.options.length > 1) return; // already loaded\n try {\n const res = await fetch(BASE + '/api/cameras');\n const cameras = await res.json();\n for (const cam of cameras) {\n const opt = document.createElement('option');\n opt.value = cam.id;\n opt.textContent = cam.name;\n sel.appendChild(opt);\n }\n if (cameras.length === 1) sel.value = cameras[0].id;\n } catch(e) {\n document.getElementById('browse-status').textContent = 'Error loading cameras';\n }\n}\n\n// Store browse events for addEvent closure\nlet browseEvents = [];\n\nasync function loadBrowse() {\n const cameraId = document.getElementById('browse-camera').value;\n const hours = document.getElementById('browse-hours').value;\n const status = document.getElementById('browse-status');\n const list = document.getElementById('browse-list');\n\n if (!cameraId) { status.textContent = 'Select a camera first'; return; }\n\n status.textContent = 'Loading…';\n list.innerHTML = '';\n browseEvents = [];\n\n try {\n const res = await fetch(BASE + '/api/browse?cameraId=' + cameraId + '&hours=' + hours);\n const events = await res.json();\n\n if (events.error) { status.textContent = 'Error: ' + events.error; return; }\n if (!events.length) {\n status.textContent = 'No detection events found.';\n list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🔍</div><div>No ObjectDetector events in this time range.</div></div>';\n return;\n }\n\n browseEvents = events;\n status.textContent = events.length + ' events';\n\n list.innerHTML = events.map((ev, i) => {\n const date = new Date(ev.timestamp).toLocaleString();\n const dets = ev.detections || [];\n const allClasses = [...new Set(dets.map(d => d.className))].join(', ');\n return \\`\n <div class=\"detection\" id=\"bev-\\${i}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"bcanvas-full-\\${i}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"bcanvas-crop-\\${i}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${ev.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${allClasses}</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">Add to dataset as:</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"addBrowseEvent(\\${i},'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"addBrowseEvent(\\${i},'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"addBrowseEvent(\\${i},'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"addBrowseEvent(\\${i},'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"addBrowseEvent(\\${i},'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"addBrowseEvent(\\${i},'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"addBrowseEvent(\\${i},'discard')\">🗑 Skip</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Load thumbnails for each event\n for (let i = 0; i < events.length; i++) {\n loadBrowseImage(i, events[i]);\n }\n\n } catch(e) {\n status.textContent = 'Error: ' + e.message;\n }\n}\n\nfunction loadBrowseImage(i, ev) {\n fetch(BASE + '/api/browse-img?cameraId=' + ev.cameraId + '&thumbnailId=' + encodeURIComponent(ev.thumbnailId))\n .then(r => r.ok ? r.blob() : null)\n .then(blob => {\n if (!blob) return;\n const url = URL.createObjectURL(blob);\n const img = new Image();\n img.onload = () => {\n imgCache.set('browse-' + i, img);\n const fullCanvas = document.getElementById('bcanvas-full-' + i);\n const cropCanvas = document.getElementById('bcanvas-crop-' + i);\n const iw = img.naturalWidth, ih = img.naturalHeight;\n // No bounding box for clip thumbnails — just draw the full image\n if (fullCanvas) {\n const ctx = fullCanvas.getContext('2d');\n const cw = fullCanvas.width, ch = fullCanvas.height;\n const scale = Math.min(cw / iw, ch / ih);\n const dw = iw * scale, dh = ih * scale;\n ctx.fillStyle = '#111'; ctx.fillRect(0, 0, cw, ch);\n ctx.drawImage(img, (cw-dw)/2, (ch-dh)/2, dw, dh);\n // Label classes\n const labels = (ev.detectionClasses || []).join(', ');\n ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillRect(0, ch-20, cw, 20);\n ctx.fillStyle = '#f90'; ctx.font = 'bold 11px sans-serif';\n ctx.fillText(labels, 4, ch-5);\n fullCanvas.onclick = () => openLightboxImg(img, ev.cameraName, ev.timestamp);\n }\n // Hide crop panel — no bounding box available\n const cropPanel = document.getElementById('bcanvas-crop-' + i)?.closest('.img-panel');\n if (cropPanel) cropPanel.style.display = 'none';\n URL.revokeObjectURL(url);\n };\n img.src = url;\n }).catch(() => {});\n}\n\nasync function addBrowseEvent(i, label) {\n const ev = browseEvents[i];\n if (!ev) return;\n const el = document.getElementById('bev-' + i);\n if (el) { el.style.opacity = '0.4'; el.querySelectorAll('button').forEach(b => b.disabled = true); }\n\n if (label !== 'discard') {\n try {\n const res = await fetch(BASE + '/api/add-event', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n cameraId: ev.cameraId,\n cameraName: ev.cameraName,\n thumbnailId: ev.thumbnailId,\n timestamp: ev.timestamp,\n detectedClass: (ev.detectionClasses || [])[0] || 'unknown',\n boundingBox: null,\n inputDimensions: null,\n label,\n }),\n });\n const data = await res.json();\n if (data.error) { toast('Error: ' + data.error, '#633'); if (el) el.style.opacity = '1'; el?.querySelectorAll('button').forEach(b => b.disabled = false); return; }\n toast('Added: ' + label, '#1a6');\n } catch(e) {\n toast('Failed: ' + e.message, '#633');\n if (el) el.style.opacity = '1';\n el?.querySelectorAll('button').forEach(b => b.disabled = false);\n return;\n }\n } else {\n toast('Skipped', '#555');\n }\n\n // Remove from list after short delay\n setTimeout(() => { if (el) el.remove(); }, 400);\n\n // Update stats\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n}\n\nconst LABEL_COLORS = { person:'#4d9', animal:'#d85', face:'#6be', vehicle:'#99d', plate:'#cc9', package:'#fc9', discard:'#a44' };\n\nasync function loadLabeled(page) {\n const list = document.getElementById('labeled-list');\n list.innerHTML = '<div class=\"empty\"><div style=\"color:#888\">Loading…</div></div>';\n try {\n const res = await fetch(BASE + '/api/labeled?page=' + page);\n const data = await res.json();\n const { items, total, pageSize } = data;\n const totalPages = Math.ceil(total / pageSize);\n\n if (!items.length) {\n list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🏷️</div><div>No labeled detections yet.</div></div>';\n return;\n }\n\n const pagerHtml = totalPages > 1 ? \\`\n <div style=\"display:flex;align-items:center;gap:12px;padding:14px 16px;border-top:1px solid #222;font-size:13px;color:#888;\">\n \\${page > 0 ? \\`<button class=\"label-btn\" onclick=\"loadLabeled(\\${page-1})\">← Prev</button>\\` : ''}\n <span>Page \\${page+1} of \\${totalPages} · \\${total} total</span>\n \\${page < totalPages-1 ? \\`<button class=\"label-btn\" onclick=\"loadLabeled(\\${page+1})\">Next →</button>\\` : ''}\n </div>\\` : '';\n\n list.innerHTML = items.map(r => {\n const date = new Date(r.timestamp).toLocaleString();\n const score = Math.round(r.score * 100);\n const labelColor = LABEL_COLORS[r.label] || '#aaa';\n return \\`\n <div class=\"detection\" id=\"ldet-\\${r.id}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"lcanvas-full-\\${r.id}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"lcanvas-crop-\\${r.id}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${r.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${r.detectedClass} \\${score}%</div>\n <div style=\"display:inline-block;background:#1a1a1a;border:1px solid \\${labelColor};color:\\${labelColor};font-size:11px;padding:2px 8px;border-radius:4px;\">✓ \\${r.label}</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">Change label:</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"relabel('\\${r.id}', 'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"relabel('\\${r.id}', 'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"relabel('\\${r.id}', 'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"relabel('\\${r.id}', 'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"relabel('\\${r.id}', 'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"relabel('\\${r.id}', 'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"relabel('\\${r.id}', 'discard')\">🗑 Discard</button>\n </div>\n </div>\n </div>\\`;\n }).join('') + pagerHtml;\n\n // Draw bounding boxes\n for (const r of items) {\n const img = new Image();\n img.onload = () => {\n imgCache.set(r.id, img);\n // Reuse drawDetection with the labeled canvases\n const origFull = document.getElementById('canvas-full-' + r.id);\n const origCrop = document.getElementById('canvas-crop-' + r.id);\n // Temporarily point to labeled canvases\n const fakeFull = document.getElementById('lcanvas-full-' + r.id);\n const fakeCrop = document.getElementById('lcanvas-crop-' + r.id);\n if (fakeFull) fakeFull.id = 'canvas-full-' + r.id;\n if (fakeCrop) fakeCrop.id = 'canvas-crop-' + r.id;\n drawDetection(img, r);\n if (fakeFull) fakeFull.id = 'lcanvas-full-' + r.id;\n if (fakeCrop) fakeCrop.id = 'lcanvas-crop-' + r.id;\n if (fakeFull) fakeFull.onclick = () => openLightbox(r);\n if (fakeCrop) fakeCrop.onclick = () => openLightbox(r);\n };\n img.src = BASE + '/img/' + r.id;\n }\n } catch(e) {\n list.innerHTML = '<div class=\"empty\"><div style=\"color:#a44\">Error: ' + e.message + '</div></div>';\n }\n}\n\nasync function relabel(id, labelVal) {\n await fetch(BASE + '/api/label', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, label: labelVal }),\n });\n const el = document.getElementById('ldet-' + id);\n if (labelVal === 'discard') {\n if (el) el.remove();\n toast('Discarded', '#633');\n } else {\n // Update the label badge in place\n const badge = el && el.querySelector('[style*=\"✓\"]');\n const labelColor = LABEL_COLORS[labelVal] || '#aaa';\n if (el) {\n const badges = el.querySelectorAll('.detection-meta > div');\n badges.forEach(b => { if (b.textContent.startsWith('✓')) b.remove(); });\n const meta = el.querySelector('.detection-meta');\n const newBadge = document.createElement('div');\n newBadge.style.cssText = \\`display:inline-block;background:#1a1a1a;border:1px solid \\${labelColor};color:\\${labelColor};font-size:11px;padding:2px 8px;border-radius:4px;\\`;\n newBadge.textContent = '✓ ' + labelVal;\n meta.appendChild(newBadge);\n }\n toast('Re-labeled: ' + labelVal, '#1a6');\n }\n // Refresh stats\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-pending').textContent = stats.pending;\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n}\n\nfunction toast(msg, color='#1a3') {\n const el = document.getElementById('toast');\n el.textContent = msg;\n el.style.background = color;\n el.classList.add('show');\n setTimeout(() => el.classList.remove('show'), 2500);\n}\n\nasync function loadPending() {\n try {\n const res = await fetch(BASE + '/api/pending');\n pending = await res.json();\n\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-pending').textContent = stats.pending;\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n\n const list = document.getElementById('detections-list');\n if (!pending.length) {\n 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>';\n return;\n }\n\n list.innerHTML = pending.map(r => {\n const date = new Date(r.timestamp).toLocaleString();\n const score = Math.round(r.score * 100);\n const bb = r.boundingBox;\n const dim = r.inputDimensions;\n return \\`\n <div class=\"detection\" id=\"det-\\${r.id}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"canvas-full-\\${r.id}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"canvas-crop-\\${r.id}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${r.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${r.detectedClass} \\${score}%</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">What is this actually?</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"label('\\${r.id}', 'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"label('\\${r.id}', 'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"label('\\${r.id}', 'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"label('\\${r.id}', 'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"label('\\${r.id}', 'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"label('\\${r.id}', 'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"label('\\${r.id}', 'discard')\">🗑 Discard</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Draw bounding boxes and crops onto canvases after DOM is ready\n for (const r of pending) {\n const img = new Image();\n img.onload = () => {\n imgCache.set(r.id, img);\n drawDetection(img, r);\n // Wire up click to open lightbox\n const fullCanvas = document.getElementById('canvas-full-' + r.id);\n const cropCanvas = document.getElementById('canvas-crop-' + r.id);\n if (fullCanvas) fullCanvas.onclick = () => openLightbox(r);\n if (cropCanvas) cropCanvas.onclick = () => openLightbox(r);\n };\n img.onerror = () => {\n const c = document.getElementById('canvas-full-' + r.id);\n if (c) c.parentElement.innerHTML = '<div style=\"padding:10px;color:#555;font-size:11px\">No image</div>';\n };\n img.src = BASE + '/img/' + r.id;\n } } catch(e) {\n console.error('loadPending error', e);\n const list = document.getElementById('detections-list');\n if (list) list.innerHTML = '<div class=\"empty\"><div style=\"color:#a44\">Error loading captures: ' + e.message + '</div></div>';\n }\n}\n\nasync function label(id, labelVal) {\n const el = document.getElementById('det-' + id);\n if (el) {\n el.classList.add('labeled');\n const btns = el.querySelectorAll('.label-btn');\n btns.forEach(b => b.disabled = true);\n }\n await fetch(BASE + '/api/label', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, label: labelVal }),\n });\n toast(labelVal === 'discard' ? 'Discarded' : 'Labeled: ' + labelVal, labelVal === 'discard' ? '#633' : '#1a6');\n setTimeout(() => {\n if (el) el.remove();\n loadPending();\n }, 600);\n}\n\nasync function loadStats() {\n const res = await fetch(BASE + '/api/stats');\n const stats = await res.json();\n\n const renderBreakdown = (obj, container) => {\n const el = document.getElementById(container);\n const entries = Object.entries(obj).sort((a, b) => b[1] - a[1]);\n el.innerHTML = entries.length\n ? entries.map(([k, v]) => \\`<div class=\"breakdown-item\"><div class=\"label\">\\${k}</div><div class=\"value\">\\${v}</div></div>\\`).join('')\n : '<div style=\"color:#555;font-size:13px;\">None yet</div>';\n };\n\n renderBreakdown(stats.byDetectedClass, 'stats-detected');\n renderBreakdown(stats.byLabel, 'stats-label');\n renderBreakdown(stats.byCamera, 'stats-camera');\n}\n\nasync function loadExportInfo() {\n const res = await fetch(BASE + '/api/stats');\n const stats = await res.json();\n document.getElementById('export-stats').textContent =\n \\`\\${stats.labeled} labeled samples ready for export across \\${Object.keys(stats.byCamera).length} camera(s).\\`;\n}\n\nasync function exportDataset() {\n const btn = document.getElementById('export-btn');\n const status = document.getElementById('export-status');\n btn.disabled = true;\n status.textContent = 'Fetching data…';\n\n try {\n const res = await fetch(BASE + '/api/export');\n if (!res.ok) { status.textContent = 'Nothing to export yet.'; btn.disabled = false; return; }\n const data = await res.json();\n if (data.error) { status.textContent = data.error; btn.disabled = false; return; }\n\n status.textContent = 'Building zip…';\n\n const zip = new JSZip();\n for (const f of data.files) {\n if (f.encoding === 'base64') {\n zip.file(f.filename, f.content, { base64: true });\n } else {\n zip.file(f.filename, f.content);\n }\n }\n\n const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'scrypted_dataset_' + new Date().toISOString().slice(0,10) + '.zip';\n a.click();\n URL.revokeObjectURL(url);\n status.textContent = \\`Downloaded \\${data.count} samples.\\`;\n toast('Dataset downloaded!');\n } catch (e) {\n status.textContent = 'Export failed: ' + e.message;\n }\n btn.disabled = false;\n}\n\n// Initial load\nloadPending();\n// Auto-refresh pending every 30s\nsetInterval(loadPending, 30_000);\n</script>\n</body>\n</html>`;\n }\n}\n\nexport default DetectionTrainer;\n","module.exports = require(\"fs\");","module.exports = require(\"module\");","module.exports = require(\"path\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(927);\n"],"names":["__createBinding","this","Object","create","o","m","k","k2","undefined","desc","getOwnPropertyDescriptor","__esModule","writable","configurable","enumerable","get","defineProperty","__exportStar","exports","p","prototype","hasOwnProperty","call","value","sdk","MixinDeviceBase","ScryptedDeviceBase","index_1","DeviceBase","constructor","nativeId","super","storage","_storage","deviceManager","getDeviceStorage","log","_log","getDeviceLogger","console","_console","getDeviceConsole","createMediaObject","data","mimeType","mediaManager","sourceId","id","getMediaObjectConsole","mediaObject","getMixinConsole","_lazyLoadDeviceState","_deviceState","getDeviceState","onDeviceEvent","eventInterface","eventData","options","_listeners","Set","mixinDevice","mixinDeviceInterfaces","mixinStorageSuffix","mixinDeviceState","systemManager","getDeviceById","mixinProviderNativeId","__rpcproxy_traps_all_properties","createDeviceState","setState","mixinStorageKey","getMixinStorage","onMixinEvent","manageListener","listener","add","release","l","removeListener","_createGetState","state","_createSetState","warn","field","values","ScryptedInterfaceProperty","set","loaded","process","env","SCRYPTED_SDK_ES_MODULE","SCRYPTED_SDK_MODULE","cjsModule","SCRYPTED_SDK_CJS_MODULE","sdkModule","assign","getScryptedStatic","e","runtimeAPI","pluginRuntimeAPI","endpointManager","pluginHostAPI","setScryptedInterfaceDescriptors","TYPES_VERSION","ScryptedInterfaceDescriptors","catch","error","webpackEmptyContext","req","Error","code","keys","resolve","module","ScryptedMimeTypes","ScryptedInterface","MediaPlayerState","SecuritySystemObstruction","SecuritySystemMode","AirQuality","AirPurifierMode","AirPurifierStatus","ChargeState","LockState","PanTiltZoomMovement","ThermostatMode","TemperatureUnit","FanMode","HumidityMode","ScryptedDeviceType","ScryptedInterfaceMethod","CAPTURE_CLASSES","RATE_OPTIONS","RATE_MS","Infinity","DetectionTrainer","lastCapture","Map","captures","listeners","imgDir","join","SCRYPTED_PLUGIN_VOLUME","mkdirSync","recursive","loadState","registerListeners","raw","getItem","arr","JSON","parse","r","size","removeItem","saveCaptures","setItem","stringify","imgPath","saveImage","buf","writeFileSync","loadImage","existsSync","readFileSync","deleteCapture","unlinkSync","delete","getSettings","cameras","getSystemState","map","filter","d","type","Camera","Doorbell","interfaces","includes","ObjectDetector","uiUrl","getLocalEndpoint","public","settings","key","title","description","c","reviewed","length","label","readonly","cam","push","name","group","choices","putSetting","startsWith","remove","rateKey","rateLabel","listen","async","source","details","onDetection","cameraId","cameraName","rateLimitMs","detections","inputDimensions","now","Date","targets","className","has","toLowerCase","boundingBox","best","sort","a","b","score","oldest","timestamp","jpeg","detectionId","mo","getDetectionInput","convertMediaObjectToBuffer","Math","random","toString","slice","record","detectedClass","round","onRequest","request","response","path","URL","url","pathname","replace","rootPath","params","searchParams","thumbnailId","send","getVideoClipThumbnail","headers","img","body","rawBody","Buffer","isBuffer","String","ok","hours","parseInt","endTime","startTime","events","getVideoClips","detectionClasses","clipId","message","pending","page","pageSize","labeled","items","total","all","stats","byLabel","byCamera","byDetectedClass","classMap","person","animal","face","vehicle","plate","package","discard","dataset","fname","filename","content","encoding","x","y","w","h","imgW","imgH","cx","cy","nw","nh","labelLine","toFixed","yaml","files","count","renderUI","require","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","obj","prop","__webpack_exports__"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"main.nodejs.js","mappings":"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","sources":["../node_modules/@scrypted/sdk/dist/src/index.js","../node_modules/@scrypted/sdk/dist/src|sync","../node_modules/@scrypted/sdk/dist/types/gen/index.js","../src/main.ts","../external node-commonjs \"fs\"","../external node-commonjs \"module\"","../external node-commonjs \"path\"","../webpack/bootstrap","../webpack/runtime/hasOwnProperty shorthand","../webpack/startup"],"sourcesContent":["\"use strict\";\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n}));\nvar __exportStar = (this && this.__exportStar) || function(m, exports) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.sdk = exports.MixinDeviceBase = exports.ScryptedDeviceBase = void 0;\n__exportStar(require(\"../types/gen/index\"), exports);\nconst index_1 = require(\"../types/gen/index\");\nconst module_1 = require(\"module\");\n/**\n * @category Core Reference\n */\nclass ScryptedDeviceBase extends index_1.DeviceBase {\n constructor(nativeId) {\n super();\n this.nativeId = nativeId;\n }\n get storage() {\n if (!this._storage) {\n this._storage = exports.sdk.deviceManager.getDeviceStorage(this.nativeId);\n }\n return this._storage;\n }\n get log() {\n if (!this._log) {\n this._log = exports.sdk.deviceManager.getDeviceLogger(this.nativeId);\n }\n return this._log;\n }\n get console() {\n if (!this._console) {\n this._console = exports.sdk.deviceManager.getDeviceConsole(this.nativeId);\n }\n return this._console;\n }\n async createMediaObject(data, mimeType) {\n return exports.sdk.mediaManager.createMediaObject(data, mimeType, {\n sourceId: this.id,\n });\n }\n getMediaObjectConsole(mediaObject) {\n if (typeof mediaObject.sourceId !== 'string')\n return this.console;\n return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.nativeId);\n }\n _lazyLoadDeviceState() {\n if (!this._deviceState) {\n if (this.nativeId) {\n this._deviceState = exports.sdk.deviceManager.getDeviceState(this.nativeId);\n }\n else {\n this._deviceState = exports.sdk.deviceManager.getDeviceState();\n }\n }\n }\n /**\n * Fire an event for this device.\n */\n onDeviceEvent(eventInterface, eventData) {\n return exports.sdk.deviceManager.onDeviceEvent(this.nativeId, eventInterface, eventData);\n }\n}\nexports.ScryptedDeviceBase = ScryptedDeviceBase;\n/**\n * @category Mixin Reference\n */\nclass MixinDeviceBase extends index_1.DeviceBase {\n constructor(options) {\n super();\n this._listeners = new Set();\n this.mixinDevice = options.mixinDevice;\n this.mixinDeviceInterfaces = options.mixinDeviceInterfaces;\n this.mixinStorageSuffix = options.mixinStorageSuffix;\n this._deviceState = options.mixinDeviceState;\n this.nativeId = exports.sdk.systemManager.getDeviceById(this.id).nativeId;\n this.mixinProviderNativeId = options.mixinProviderNativeId;\n // RpcProxy will trap all properties, and the following check/hack will determine\n // if the device state came from another node worker thread.\n // This should ultimately be discouraged and warned at some point in the future.\n if (this._deviceState.__rpcproxy_traps_all_properties && typeof this._deviceState.id === 'string') {\n this._deviceState = exports.sdk.deviceManager.createDeviceState(this._deviceState.id, this._deviceState.setState);\n }\n }\n get storage() {\n if (!this._storage) {\n const mixinStorageSuffix = this.mixinStorageSuffix;\n const mixinStorageKey = this.id + (mixinStorageSuffix ? ':' + mixinStorageSuffix : '');\n this._storage = exports.sdk.deviceManager.getMixinStorage(mixinStorageKey, this.mixinProviderNativeId);\n }\n return this._storage;\n }\n get console() {\n if (!this._console) {\n if (exports.sdk.deviceManager.getMixinConsole)\n this._console = exports.sdk.deviceManager.getMixinConsole(this.id, this.mixinProviderNativeId);\n else\n this._console = exports.sdk.deviceManager.getDeviceConsole(this.mixinProviderNativeId);\n }\n return this._console;\n }\n async createMediaObject(data, mimeType) {\n return exports.sdk.mediaManager.createMediaObject(data, mimeType, {\n sourceId: this.id,\n });\n }\n getMediaObjectConsole(mediaObject) {\n if (typeof mediaObject.sourceId !== 'string')\n return this.console;\n return exports.sdk.deviceManager.getMixinConsole(mediaObject.sourceId, this.mixinProviderNativeId);\n }\n /**\n * Fire an event for this device.\n */\n onDeviceEvent(eventInterface, eventData) {\n return exports.sdk.deviceManager.onMixinEvent(this.id, this, eventInterface, eventData);\n }\n _lazyLoadDeviceState() {\n }\n manageListener(listener) {\n this._listeners.add(listener);\n }\n release() {\n for (const l of this._listeners) {\n l.removeListener();\n }\n }\n}\nexports.MixinDeviceBase = MixinDeviceBase;\n(function () {\n function _createGetState(state) {\n return function () {\n this._lazyLoadDeviceState();\n // @ts-ignore: accessing private property\n return this._deviceState?.[state];\n };\n }\n function _createSetState(state) {\n return function (value) {\n this._lazyLoadDeviceState();\n // @ts-ignore: accessing private property\n if (!this._deviceState) {\n console.warn('device state is unavailable. the device must be discovered with deviceManager.onDeviceDiscovered or deviceManager.onDevicesChanged before the state can be set.');\n }\n else {\n // @ts-ignore: accessing private property\n this._deviceState[state] = value;\n }\n };\n }\n for (const field of Object.values(index_1.ScryptedInterfaceProperty)) {\n if (field === index_1.ScryptedInterfaceProperty.nativeId)\n continue;\n Object.defineProperty(ScryptedDeviceBase.prototype, field, {\n set: _createSetState(field),\n get: _createGetState(field),\n });\n Object.defineProperty(MixinDeviceBase.prototype, field, {\n set: _createSetState(field),\n get: _createGetState(field),\n });\n }\n})();\nexports.sdk = {};\ntry {\n let loaded = false;\n try {\n // todo: remove usage of process.env.SCRYPTED_SDK_MODULE, only existed in prerelease builds.\n // import.meta is not a reliable way to detect es module support in webpack since webpack\n // evaluates that to true at runtime.\n const esModule = process.env.SCRYPTED_SDK_ES_MODULE || process.env.SCRYPTED_SDK_MODULE;\n const cjsModule = process.env.SCRYPTED_SDK_CJS_MODULE || process.env.SCRYPTED_SDK_MODULE;\n // @ts-expect-error\n if (esModule && typeof import.meta !== 'undefined') {\n // @ts-expect-error\n const require = (0, module_1.createRequire)(import.meta.url);\n const sdkModule = require(esModule);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n else if (cjsModule) {\n // @ts-expect-error\n if (typeof __non_webpack_require__ !== 'undefined') {\n // @ts-expect-error\n const sdkModule = __non_webpack_require__(process.env.SCRYPTED_SDK_MODULE);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n else {\n const sdkModule = require(cjsModule);\n Object.assign(exports.sdk, sdkModule.getScryptedStatic());\n loaded = true;\n }\n }\n }\n catch (e) {\n console.warn(\"failed to load sdk module\", e);\n throw e;\n }\n if (!loaded) {\n let runtimeAPI;\n try {\n runtimeAPI = pluginRuntimeAPI;\n }\n catch (e) {\n }\n Object.assign(exports.sdk, {\n log: deviceManager.getDeviceLogger(undefined),\n deviceManager,\n endpointManager,\n mediaManager,\n systemManager,\n pluginHostAPI,\n ...runtimeAPI,\n });\n }\n try {\n exports.sdk.systemManager.setScryptedInterfaceDescriptors?.(index_1.TYPES_VERSION, index_1.ScryptedInterfaceDescriptors)?.catch(() => { });\n }\n catch (e) {\n }\n}\ncatch (e) {\n console.error('sdk initialization error, import @scrypted/types or use @scrypted/client instead', e);\n}\nexports.default = exports.sdk;\n//# sourceMappingURL=index.js.map","function webpackEmptyContext(req) {\n\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\te.code = 'MODULE_NOT_FOUND';\n\tthrow e;\n}\nwebpackEmptyContext.keys = () => ([]);\nwebpackEmptyContext.resolve = webpackEmptyContext;\nwebpackEmptyContext.id = 891;\nmodule.exports = webpackEmptyContext;","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.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;\nexports.TYPES_VERSION = \"0.3.116\";\nclass DeviceBase {\n}\nexports.DeviceBase = DeviceBase;\nvar ScryptedInterfaceProperty;\n(function (ScryptedInterfaceProperty) {\n ScryptedInterfaceProperty[\"id\"] = \"id\";\n ScryptedInterfaceProperty[\"info\"] = \"info\";\n ScryptedInterfaceProperty[\"interfaces\"] = \"interfaces\";\n ScryptedInterfaceProperty[\"mixins\"] = \"mixins\";\n ScryptedInterfaceProperty[\"name\"] = \"name\";\n ScryptedInterfaceProperty[\"nativeId\"] = \"nativeId\";\n ScryptedInterfaceProperty[\"pluginId\"] = \"pluginId\";\n ScryptedInterfaceProperty[\"providedInterfaces\"] = \"providedInterfaces\";\n ScryptedInterfaceProperty[\"providedName\"] = \"providedName\";\n ScryptedInterfaceProperty[\"providedRoom\"] = \"providedRoom\";\n ScryptedInterfaceProperty[\"providedType\"] = \"providedType\";\n ScryptedInterfaceProperty[\"providerId\"] = \"providerId\";\n ScryptedInterfaceProperty[\"room\"] = \"room\";\n ScryptedInterfaceProperty[\"type\"] = \"type\";\n ScryptedInterfaceProperty[\"scryptedRuntimeArguments\"] = \"scryptedRuntimeArguments\";\n ScryptedInterfaceProperty[\"on\"] = \"on\";\n ScryptedInterfaceProperty[\"brightness\"] = \"brightness\";\n ScryptedInterfaceProperty[\"colorTemperature\"] = \"colorTemperature\";\n ScryptedInterfaceProperty[\"rgb\"] = \"rgb\";\n ScryptedInterfaceProperty[\"hsv\"] = \"hsv\";\n ScryptedInterfaceProperty[\"buttons\"] = \"buttons\";\n ScryptedInterfaceProperty[\"sensors\"] = \"sensors\";\n ScryptedInterfaceProperty[\"running\"] = \"running\";\n ScryptedInterfaceProperty[\"paused\"] = \"paused\";\n ScryptedInterfaceProperty[\"docked\"] = \"docked\";\n ScryptedInterfaceProperty[\"temperatureSetting\"] = \"temperatureSetting\";\n ScryptedInterfaceProperty[\"temperature\"] = \"temperature\";\n ScryptedInterfaceProperty[\"temperatureUnit\"] = \"temperatureUnit\";\n ScryptedInterfaceProperty[\"humidity\"] = \"humidity\";\n ScryptedInterfaceProperty[\"audioVolumes\"] = \"audioVolumes\";\n ScryptedInterfaceProperty[\"recordingActive\"] = \"recordingActive\";\n ScryptedInterfaceProperty[\"ptzCapabilities\"] = \"ptzCapabilities\";\n ScryptedInterfaceProperty[\"lockState\"] = \"lockState\";\n ScryptedInterfaceProperty[\"entryOpen\"] = \"entryOpen\";\n ScryptedInterfaceProperty[\"batteryLevel\"] = \"batteryLevel\";\n ScryptedInterfaceProperty[\"chargeState\"] = \"chargeState\";\n ScryptedInterfaceProperty[\"online\"] = \"online\";\n ScryptedInterfaceProperty[\"fromMimeType\"] = \"fromMimeType\";\n ScryptedInterfaceProperty[\"toMimeType\"] = \"toMimeType\";\n ScryptedInterfaceProperty[\"converters\"] = \"converters\";\n ScryptedInterfaceProperty[\"binaryState\"] = \"binaryState\";\n ScryptedInterfaceProperty[\"tampered\"] = \"tampered\";\n ScryptedInterfaceProperty[\"sleeping\"] = \"sleeping\";\n ScryptedInterfaceProperty[\"powerDetected\"] = \"powerDetected\";\n ScryptedInterfaceProperty[\"audioDetected\"] = \"audioDetected\";\n ScryptedInterfaceProperty[\"motionDetected\"] = \"motionDetected\";\n ScryptedInterfaceProperty[\"ambientLight\"] = \"ambientLight\";\n ScryptedInterfaceProperty[\"occupied\"] = \"occupied\";\n ScryptedInterfaceProperty[\"flooded\"] = \"flooded\";\n ScryptedInterfaceProperty[\"ultraviolet\"] = \"ultraviolet\";\n ScryptedInterfaceProperty[\"luminance\"] = \"luminance\";\n ScryptedInterfaceProperty[\"position\"] = \"position\";\n ScryptedInterfaceProperty[\"securitySystemState\"] = \"securitySystemState\";\n ScryptedInterfaceProperty[\"pm10Density\"] = \"pm10Density\";\n ScryptedInterfaceProperty[\"pm25Density\"] = \"pm25Density\";\n ScryptedInterfaceProperty[\"vocDensity\"] = \"vocDensity\";\n ScryptedInterfaceProperty[\"noxDensity\"] = \"noxDensity\";\n ScryptedInterfaceProperty[\"co2ppm\"] = \"co2ppm\";\n ScryptedInterfaceProperty[\"airQuality\"] = \"airQuality\";\n ScryptedInterfaceProperty[\"airPurifierState\"] = \"airPurifierState\";\n ScryptedInterfaceProperty[\"filterChangeIndication\"] = \"filterChangeIndication\";\n ScryptedInterfaceProperty[\"filterLifeLevel\"] = \"filterLifeLevel\";\n ScryptedInterfaceProperty[\"humiditySetting\"] = \"humiditySetting\";\n ScryptedInterfaceProperty[\"fan\"] = \"fan\";\n ScryptedInterfaceProperty[\"applicationInfo\"] = \"applicationInfo\";\n ScryptedInterfaceProperty[\"systemDevice\"] = \"systemDevice\";\n})(ScryptedInterfaceProperty || (exports.ScryptedInterfaceProperty = ScryptedInterfaceProperty = {}));\nvar ScryptedInterfaceMethod;\n(function (ScryptedInterfaceMethod) {\n ScryptedInterfaceMethod[\"listen\"] = \"listen\";\n ScryptedInterfaceMethod[\"probe\"] = \"probe\";\n ScryptedInterfaceMethod[\"setMixins\"] = \"setMixins\";\n ScryptedInterfaceMethod[\"setName\"] = \"setName\";\n ScryptedInterfaceMethod[\"setRoom\"] = \"setRoom\";\n ScryptedInterfaceMethod[\"setType\"] = \"setType\";\n ScryptedInterfaceMethod[\"getPluginJson\"] = \"getPluginJson\";\n ScryptedInterfaceMethod[\"turnOff\"] = \"turnOff\";\n ScryptedInterfaceMethod[\"turnOn\"] = \"turnOn\";\n ScryptedInterfaceMethod[\"setBrightness\"] = \"setBrightness\";\n ScryptedInterfaceMethod[\"getTemperatureMaxK\"] = \"getTemperatureMaxK\";\n ScryptedInterfaceMethod[\"getTemperatureMinK\"] = \"getTemperatureMinK\";\n ScryptedInterfaceMethod[\"setColorTemperature\"] = \"setColorTemperature\";\n ScryptedInterfaceMethod[\"setRgb\"] = \"setRgb\";\n ScryptedInterfaceMethod[\"setHsv\"] = \"setHsv\";\n ScryptedInterfaceMethod[\"pressButton\"] = \"pressButton\";\n ScryptedInterfaceMethod[\"sendNotification\"] = \"sendNotification\";\n ScryptedInterfaceMethod[\"start\"] = \"start\";\n ScryptedInterfaceMethod[\"stop\"] = \"stop\";\n ScryptedInterfaceMethod[\"pause\"] = \"pause\";\n ScryptedInterfaceMethod[\"resume\"] = \"resume\";\n ScryptedInterfaceMethod[\"dock\"] = \"dock\";\n ScryptedInterfaceMethod[\"setTemperature\"] = \"setTemperature\";\n ScryptedInterfaceMethod[\"setTemperatureUnit\"] = \"setTemperatureUnit\";\n ScryptedInterfaceMethod[\"getPictureOptions\"] = \"getPictureOptions\";\n ScryptedInterfaceMethod[\"takePicture\"] = \"takePicture\";\n ScryptedInterfaceMethod[\"getAudioStream\"] = \"getAudioStream\";\n ScryptedInterfaceMethod[\"setAudioVolumes\"] = \"setAudioVolumes\";\n ScryptedInterfaceMethod[\"startDisplay\"] = \"startDisplay\";\n ScryptedInterfaceMethod[\"stopDisplay\"] = \"stopDisplay\";\n ScryptedInterfaceMethod[\"getVideoStream\"] = \"getVideoStream\";\n ScryptedInterfaceMethod[\"getVideoStreamOptions\"] = \"getVideoStreamOptions\";\n ScryptedInterfaceMethod[\"getPrivacyMasks\"] = \"getPrivacyMasks\";\n ScryptedInterfaceMethod[\"setPrivacyMasks\"] = \"setPrivacyMasks\";\n ScryptedInterfaceMethod[\"getVideoTextOverlays\"] = \"getVideoTextOverlays\";\n ScryptedInterfaceMethod[\"setVideoTextOverlay\"] = \"setVideoTextOverlay\";\n ScryptedInterfaceMethod[\"getRecordingStream\"] = \"getRecordingStream\";\n ScryptedInterfaceMethod[\"getRecordingStreamCurrentTime\"] = \"getRecordingStreamCurrentTime\";\n ScryptedInterfaceMethod[\"getRecordingStreamOptions\"] = \"getRecordingStreamOptions\";\n ScryptedInterfaceMethod[\"getRecordingStreamThumbnail\"] = \"getRecordingStreamThumbnail\";\n ScryptedInterfaceMethod[\"deleteRecordingStream\"] = \"deleteRecordingStream\";\n ScryptedInterfaceMethod[\"setRecordingActive\"] = \"setRecordingActive\";\n ScryptedInterfaceMethod[\"ptzCommand\"] = \"ptzCommand\";\n ScryptedInterfaceMethod[\"getRecordedEvents\"] = \"getRecordedEvents\";\n ScryptedInterfaceMethod[\"getVideoClip\"] = \"getVideoClip\";\n ScryptedInterfaceMethod[\"getVideoClips\"] = \"getVideoClips\";\n ScryptedInterfaceMethod[\"getVideoClipThumbnail\"] = \"getVideoClipThumbnail\";\n ScryptedInterfaceMethod[\"removeVideoClips\"] = \"removeVideoClips\";\n ScryptedInterfaceMethod[\"setVideoStreamOptions\"] = \"setVideoStreamOptions\";\n ScryptedInterfaceMethod[\"startIntercom\"] = \"startIntercom\";\n ScryptedInterfaceMethod[\"stopIntercom\"] = \"stopIntercom\";\n ScryptedInterfaceMethod[\"lock\"] = \"lock\";\n ScryptedInterfaceMethod[\"unlock\"] = \"unlock\";\n ScryptedInterfaceMethod[\"addPassword\"] = \"addPassword\";\n ScryptedInterfaceMethod[\"getPasswords\"] = \"getPasswords\";\n ScryptedInterfaceMethod[\"removePassword\"] = \"removePassword\";\n ScryptedInterfaceMethod[\"activate\"] = \"activate\";\n ScryptedInterfaceMethod[\"deactivate\"] = \"deactivate\";\n ScryptedInterfaceMethod[\"isReversible\"] = \"isReversible\";\n ScryptedInterfaceMethod[\"closeEntry\"] = \"closeEntry\";\n ScryptedInterfaceMethod[\"openEntry\"] = \"openEntry\";\n ScryptedInterfaceMethod[\"getDevice\"] = \"getDevice\";\n ScryptedInterfaceMethod[\"releaseDevice\"] = \"releaseDevice\";\n ScryptedInterfaceMethod[\"adoptDevice\"] = \"adoptDevice\";\n ScryptedInterfaceMethod[\"discoverDevices\"] = \"discoverDevices\";\n ScryptedInterfaceMethod[\"createDevice\"] = \"createDevice\";\n ScryptedInterfaceMethod[\"getCreateDeviceSettings\"] = \"getCreateDeviceSettings\";\n ScryptedInterfaceMethod[\"reboot\"] = \"reboot\";\n ScryptedInterfaceMethod[\"getRefreshFrequency\"] = \"getRefreshFrequency\";\n ScryptedInterfaceMethod[\"refresh\"] = \"refresh\";\n ScryptedInterfaceMethod[\"getMediaStatus\"] = \"getMediaStatus\";\n ScryptedInterfaceMethod[\"load\"] = \"load\";\n ScryptedInterfaceMethod[\"seek\"] = \"seek\";\n ScryptedInterfaceMethod[\"skipNext\"] = \"skipNext\";\n ScryptedInterfaceMethod[\"skipPrevious\"] = \"skipPrevious\";\n ScryptedInterfaceMethod[\"convert\"] = \"convert\";\n ScryptedInterfaceMethod[\"convertMedia\"] = \"convertMedia\";\n ScryptedInterfaceMethod[\"getSettings\"] = \"getSettings\";\n ScryptedInterfaceMethod[\"putSetting\"] = \"putSetting\";\n ScryptedInterfaceMethod[\"armSecuritySystem\"] = \"armSecuritySystem\";\n ScryptedInterfaceMethod[\"disarmSecuritySystem\"] = \"disarmSecuritySystem\";\n ScryptedInterfaceMethod[\"setAirPurifierState\"] = \"setAirPurifierState\";\n ScryptedInterfaceMethod[\"getReadmeMarkdown\"] = \"getReadmeMarkdown\";\n ScryptedInterfaceMethod[\"getOauthUrl\"] = \"getOauthUrl\";\n ScryptedInterfaceMethod[\"onOauthCallback\"] = \"onOauthCallback\";\n ScryptedInterfaceMethod[\"canMixin\"] = \"canMixin\";\n ScryptedInterfaceMethod[\"getMixin\"] = \"getMixin\";\n ScryptedInterfaceMethod[\"releaseMixin\"] = \"releaseMixin\";\n ScryptedInterfaceMethod[\"onRequest\"] = \"onRequest\";\n ScryptedInterfaceMethod[\"onConnection\"] = \"onConnection\";\n ScryptedInterfaceMethod[\"onPush\"] = \"onPush\";\n ScryptedInterfaceMethod[\"run\"] = \"run\";\n ScryptedInterfaceMethod[\"eval\"] = \"eval\";\n ScryptedInterfaceMethod[\"loadScripts\"] = \"loadScripts\";\n ScryptedInterfaceMethod[\"saveScript\"] = \"saveScript\";\n ScryptedInterfaceMethod[\"forkInterface\"] = \"forkInterface\";\n ScryptedInterfaceMethod[\"trackObjects\"] = \"trackObjects\";\n ScryptedInterfaceMethod[\"getDetectionInput\"] = \"getDetectionInput\";\n ScryptedInterfaceMethod[\"getObjectTypes\"] = \"getObjectTypes\";\n ScryptedInterfaceMethod[\"detectObjects\"] = \"detectObjects\";\n ScryptedInterfaceMethod[\"generateObjectDetections\"] = \"generateObjectDetections\";\n ScryptedInterfaceMethod[\"getDetectionModel\"] = \"getDetectionModel\";\n ScryptedInterfaceMethod[\"setHumidity\"] = \"setHumidity\";\n ScryptedInterfaceMethod[\"setFan\"] = \"setFan\";\n ScryptedInterfaceMethod[\"startRTCSignalingSession\"] = \"startRTCSignalingSession\";\n ScryptedInterfaceMethod[\"createRTCSignalingSession\"] = \"createRTCSignalingSession\";\n ScryptedInterfaceMethod[\"getScryptedUserAccessControl\"] = \"getScryptedUserAccessControl\";\n ScryptedInterfaceMethod[\"generateVideoFrames\"] = \"generateVideoFrames\";\n ScryptedInterfaceMethod[\"connectStream\"] = \"connectStream\";\n ScryptedInterfaceMethod[\"getTTYSettings\"] = \"getTTYSettings\";\n})(ScryptedInterfaceMethod || (exports.ScryptedInterfaceMethod = ScryptedInterfaceMethod = {}));\nexports.ScryptedInterfaceDescriptors = {\n \"ScryptedDevice\": {\n \"name\": \"ScryptedDevice\",\n \"methods\": [\n \"listen\",\n \"probe\",\n \"setMixins\",\n \"setName\",\n \"setRoom\",\n \"setType\"\n ],\n \"properties\": [\n \"id\",\n \"info\",\n \"interfaces\",\n \"mixins\",\n \"name\",\n \"nativeId\",\n \"pluginId\",\n \"providedInterfaces\",\n \"providedName\",\n \"providedRoom\",\n \"providedType\",\n \"providerId\",\n \"room\",\n \"type\"\n ]\n },\n \"ScryptedPlugin\": {\n \"name\": \"ScryptedPlugin\",\n \"methods\": [\n \"getPluginJson\"\n ],\n \"properties\": []\n },\n \"ScryptedPluginRuntime\": {\n \"name\": \"ScryptedPluginRuntime\",\n \"methods\": [],\n \"properties\": [\n \"scryptedRuntimeArguments\"\n ]\n },\n \"OnOff\": {\n \"name\": \"OnOff\",\n \"methods\": [\n \"turnOff\",\n \"turnOn\"\n ],\n \"properties\": [\n \"on\"\n ]\n },\n \"Brightness\": {\n \"name\": \"Brightness\",\n \"methods\": [\n \"setBrightness\"\n ],\n \"properties\": [\n \"brightness\"\n ]\n },\n \"ColorSettingTemperature\": {\n \"name\": \"ColorSettingTemperature\",\n \"methods\": [\n \"getTemperatureMaxK\",\n \"getTemperatureMinK\",\n \"setColorTemperature\"\n ],\n \"properties\": [\n \"colorTemperature\"\n ]\n },\n \"ColorSettingRgb\": {\n \"name\": \"ColorSettingRgb\",\n \"methods\": [\n \"setRgb\"\n ],\n \"properties\": [\n \"rgb\"\n ]\n },\n \"ColorSettingHsv\": {\n \"name\": \"ColorSettingHsv\",\n \"methods\": [\n \"setHsv\"\n ],\n \"properties\": [\n \"hsv\"\n ]\n },\n \"Buttons\": {\n \"name\": \"Buttons\",\n \"methods\": [],\n \"properties\": [\n \"buttons\"\n ]\n },\n \"PressButtons\": {\n \"name\": \"PressButtons\",\n \"methods\": [\n \"pressButton\"\n ],\n \"properties\": []\n },\n \"Sensors\": {\n \"name\": \"Sensors\",\n \"methods\": [],\n \"properties\": [\n \"sensors\"\n ]\n },\n \"Notifier\": {\n \"name\": \"Notifier\",\n \"methods\": [\n \"sendNotification\"\n ],\n \"properties\": []\n },\n \"StartStop\": {\n \"name\": \"StartStop\",\n \"methods\": [\n \"start\",\n \"stop\"\n ],\n \"properties\": [\n \"running\"\n ]\n },\n \"Pause\": {\n \"name\": \"Pause\",\n \"methods\": [\n \"pause\",\n \"resume\"\n ],\n \"properties\": [\n \"paused\"\n ]\n },\n \"Dock\": {\n \"name\": \"Dock\",\n \"methods\": [\n \"dock\"\n ],\n \"properties\": [\n \"docked\"\n ]\n },\n \"TemperatureSetting\": {\n \"name\": \"TemperatureSetting\",\n \"methods\": [\n \"setTemperature\"\n ],\n \"properties\": [\n \"temperatureSetting\"\n ]\n },\n \"Thermometer\": {\n \"name\": \"Thermometer\",\n \"methods\": [\n \"setTemperatureUnit\"\n ],\n \"properties\": [\n \"temperature\",\n \"temperatureUnit\"\n ]\n },\n \"HumiditySensor\": {\n \"name\": \"HumiditySensor\",\n \"methods\": [],\n \"properties\": [\n \"humidity\"\n ]\n },\n \"Camera\": {\n \"name\": \"Camera\",\n \"methods\": [\n \"getPictureOptions\",\n \"takePicture\"\n ],\n \"properties\": []\n },\n \"Microphone\": {\n \"name\": \"Microphone\",\n \"methods\": [\n \"getAudioStream\"\n ],\n \"properties\": []\n },\n \"AudioVolumeControl\": {\n \"name\": \"AudioVolumeControl\",\n \"methods\": [\n \"setAudioVolumes\"\n ],\n \"properties\": [\n \"audioVolumes\"\n ]\n },\n \"Display\": {\n \"name\": \"Display\",\n \"methods\": [\n \"startDisplay\",\n \"stopDisplay\"\n ],\n \"properties\": []\n },\n \"VideoCamera\": {\n \"name\": \"VideoCamera\",\n \"methods\": [\n \"getVideoStream\",\n \"getVideoStreamOptions\"\n ],\n \"properties\": []\n },\n \"VideoCameraMask\": {\n \"name\": \"VideoCameraMask\",\n \"methods\": [\n \"getPrivacyMasks\",\n \"setPrivacyMasks\"\n ],\n \"properties\": []\n },\n \"VideoTextOverlays\": {\n \"name\": \"VideoTextOverlays\",\n \"methods\": [\n \"getVideoTextOverlays\",\n \"setVideoTextOverlay\"\n ],\n \"properties\": []\n },\n \"VideoRecorder\": {\n \"name\": \"VideoRecorder\",\n \"methods\": [\n \"getRecordingStream\",\n \"getRecordingStreamCurrentTime\",\n \"getRecordingStreamOptions\",\n \"getRecordingStreamThumbnail\"\n ],\n \"properties\": [\n \"recordingActive\"\n ]\n },\n \"VideoRecorderManagement\": {\n \"name\": \"VideoRecorderManagement\",\n \"methods\": [\n \"deleteRecordingStream\",\n \"setRecordingActive\"\n ],\n \"properties\": []\n },\n \"PanTiltZoom\": {\n \"name\": \"PanTiltZoom\",\n \"methods\": [\n \"ptzCommand\"\n ],\n \"properties\": [\n \"ptzCapabilities\"\n ]\n },\n \"EventRecorder\": {\n \"name\": \"EventRecorder\",\n \"methods\": [\n \"getRecordedEvents\"\n ],\n \"properties\": []\n },\n \"VideoClips\": {\n \"name\": \"VideoClips\",\n \"methods\": [\n \"getVideoClip\",\n \"getVideoClips\",\n \"getVideoClipThumbnail\",\n \"removeVideoClips\"\n ],\n \"properties\": []\n },\n \"VideoCameraConfiguration\": {\n \"name\": \"VideoCameraConfiguration\",\n \"methods\": [\n \"setVideoStreamOptions\"\n ],\n \"properties\": []\n },\n \"Intercom\": {\n \"name\": \"Intercom\",\n \"methods\": [\n \"startIntercom\",\n \"stopIntercom\"\n ],\n \"properties\": []\n },\n \"Lock\": {\n \"name\": \"Lock\",\n \"methods\": [\n \"lock\",\n \"unlock\"\n ],\n \"properties\": [\n \"lockState\"\n ]\n },\n \"PasswordStore\": {\n \"name\": \"PasswordStore\",\n \"methods\": [\n \"addPassword\",\n \"getPasswords\",\n \"removePassword\"\n ],\n \"properties\": []\n },\n \"Scene\": {\n \"name\": \"Scene\",\n \"methods\": [\n \"activate\",\n \"deactivate\",\n \"isReversible\"\n ],\n \"properties\": []\n },\n \"Entry\": {\n \"name\": \"Entry\",\n \"methods\": [\n \"closeEntry\",\n \"openEntry\"\n ],\n \"properties\": []\n },\n \"EntrySensor\": {\n \"name\": \"EntrySensor\",\n \"methods\": [],\n \"properties\": [\n \"entryOpen\"\n ]\n },\n \"DeviceProvider\": {\n \"name\": \"DeviceProvider\",\n \"methods\": [\n \"getDevice\",\n \"releaseDevice\"\n ],\n \"properties\": []\n },\n \"DeviceDiscovery\": {\n \"name\": \"DeviceDiscovery\",\n \"methods\": [\n \"adoptDevice\",\n \"discoverDevices\"\n ],\n \"properties\": []\n },\n \"DeviceCreator\": {\n \"name\": \"DeviceCreator\",\n \"methods\": [\n \"createDevice\",\n \"getCreateDeviceSettings\"\n ],\n \"properties\": []\n },\n \"Battery\": {\n \"name\": \"Battery\",\n \"methods\": [],\n \"properties\": [\n \"batteryLevel\"\n ]\n },\n \"Charger\": {\n \"name\": \"Charger\",\n \"methods\": [],\n \"properties\": [\n \"chargeState\"\n ]\n },\n \"Reboot\": {\n \"name\": \"Reboot\",\n \"methods\": [\n \"reboot\"\n ],\n \"properties\": []\n },\n \"Refresh\": {\n \"name\": \"Refresh\",\n \"methods\": [\n \"getRefreshFrequency\",\n \"refresh\"\n ],\n \"properties\": []\n },\n \"MediaPlayer\": {\n \"name\": \"MediaPlayer\",\n \"methods\": [\n \"getMediaStatus\",\n \"load\",\n \"seek\",\n \"skipNext\",\n \"skipPrevious\"\n ],\n \"properties\": []\n },\n \"Online\": {\n \"name\": \"Online\",\n \"methods\": [],\n \"properties\": [\n \"online\"\n ]\n },\n \"BufferConverter\": {\n \"name\": \"BufferConverter\",\n \"methods\": [\n \"convert\"\n ],\n \"properties\": [\n \"fromMimeType\",\n \"toMimeType\"\n ]\n },\n \"MediaConverter\": {\n \"name\": \"MediaConverter\",\n \"methods\": [\n \"convertMedia\"\n ],\n \"properties\": [\n \"converters\"\n ]\n },\n \"Settings\": {\n \"name\": \"Settings\",\n \"methods\": [\n \"getSettings\",\n \"putSetting\"\n ],\n \"properties\": []\n },\n \"BinarySensor\": {\n \"name\": \"BinarySensor\",\n \"methods\": [],\n \"properties\": [\n \"binaryState\"\n ]\n },\n \"TamperSensor\": {\n \"name\": \"TamperSensor\",\n \"methods\": [],\n \"properties\": [\n \"tampered\"\n ]\n },\n \"Sleep\": {\n \"name\": \"Sleep\",\n \"methods\": [],\n \"properties\": [\n \"sleeping\"\n ]\n },\n \"PowerSensor\": {\n \"name\": \"PowerSensor\",\n \"methods\": [],\n \"properties\": [\n \"powerDetected\"\n ]\n },\n \"AudioSensor\": {\n \"name\": \"AudioSensor\",\n \"methods\": [],\n \"properties\": [\n \"audioDetected\"\n ]\n },\n \"MotionSensor\": {\n \"name\": \"MotionSensor\",\n \"methods\": [],\n \"properties\": [\n \"motionDetected\"\n ]\n },\n \"AmbientLightSensor\": {\n \"name\": \"AmbientLightSensor\",\n \"methods\": [],\n \"properties\": [\n \"ambientLight\"\n ]\n },\n \"OccupancySensor\": {\n \"name\": \"OccupancySensor\",\n \"methods\": [],\n \"properties\": [\n \"occupied\"\n ]\n },\n \"FloodSensor\": {\n \"name\": \"FloodSensor\",\n \"methods\": [],\n \"properties\": [\n \"flooded\"\n ]\n },\n \"UltravioletSensor\": {\n \"name\": \"UltravioletSensor\",\n \"methods\": [],\n \"properties\": [\n \"ultraviolet\"\n ]\n },\n \"LuminanceSensor\": {\n \"name\": \"LuminanceSensor\",\n \"methods\": [],\n \"properties\": [\n \"luminance\"\n ]\n },\n \"PositionSensor\": {\n \"name\": \"PositionSensor\",\n \"methods\": [],\n \"properties\": [\n \"position\"\n ]\n },\n \"SecuritySystem\": {\n \"name\": \"SecuritySystem\",\n \"methods\": [\n \"armSecuritySystem\",\n \"disarmSecuritySystem\"\n ],\n \"properties\": [\n \"securitySystemState\"\n ]\n },\n \"PM10Sensor\": {\n \"name\": \"PM10Sensor\",\n \"methods\": [],\n \"properties\": [\n \"pm10Density\"\n ]\n },\n \"PM25Sensor\": {\n \"name\": \"PM25Sensor\",\n \"methods\": [],\n \"properties\": [\n \"pm25Density\"\n ]\n },\n \"VOCSensor\": {\n \"name\": \"VOCSensor\",\n \"methods\": [],\n \"properties\": [\n \"vocDensity\"\n ]\n },\n \"NOXSensor\": {\n \"name\": \"NOXSensor\",\n \"methods\": [],\n \"properties\": [\n \"noxDensity\"\n ]\n },\n \"CO2Sensor\": {\n \"name\": \"CO2Sensor\",\n \"methods\": [],\n \"properties\": [\n \"co2ppm\"\n ]\n },\n \"AirQualitySensor\": {\n \"name\": \"AirQualitySensor\",\n \"methods\": [],\n \"properties\": [\n \"airQuality\"\n ]\n },\n \"AirPurifier\": {\n \"name\": \"AirPurifier\",\n \"methods\": [\n \"setAirPurifierState\"\n ],\n \"properties\": [\n \"airPurifierState\"\n ]\n },\n \"FilterMaintenance\": {\n \"name\": \"FilterMaintenance\",\n \"methods\": [],\n \"properties\": [\n \"filterChangeIndication\",\n \"filterLifeLevel\"\n ]\n },\n \"Readme\": {\n \"name\": \"Readme\",\n \"methods\": [\n \"getReadmeMarkdown\"\n ],\n \"properties\": []\n },\n \"OauthClient\": {\n \"name\": \"OauthClient\",\n \"methods\": [\n \"getOauthUrl\",\n \"onOauthCallback\"\n ],\n \"properties\": []\n },\n \"MixinProvider\": {\n \"name\": \"MixinProvider\",\n \"methods\": [\n \"canMixin\",\n \"getMixin\",\n \"releaseMixin\"\n ],\n \"properties\": []\n },\n \"HttpRequestHandler\": {\n \"name\": \"HttpRequestHandler\",\n \"methods\": [\n \"onRequest\"\n ],\n \"properties\": []\n },\n \"EngineIOHandler\": {\n \"name\": \"EngineIOHandler\",\n \"methods\": [\n \"onConnection\"\n ],\n \"properties\": []\n },\n \"PushHandler\": {\n \"name\": \"PushHandler\",\n \"methods\": [\n \"onPush\"\n ],\n \"properties\": []\n },\n \"Program\": {\n \"name\": \"Program\",\n \"methods\": [\n \"run\"\n ],\n \"properties\": []\n },\n \"Scriptable\": {\n \"name\": \"Scriptable\",\n \"methods\": [\n \"eval\",\n \"loadScripts\",\n \"saveScript\"\n ],\n \"properties\": []\n },\n \"ClusterForkInterface\": {\n \"name\": \"ClusterForkInterface\",\n \"methods\": [\n \"forkInterface\"\n ],\n \"properties\": []\n },\n \"ObjectTracker\": {\n \"name\": \"ObjectTracker\",\n \"methods\": [\n \"trackObjects\"\n ],\n \"properties\": []\n },\n \"ObjectDetector\": {\n \"name\": \"ObjectDetector\",\n \"methods\": [\n \"getDetectionInput\",\n \"getObjectTypes\"\n ],\n \"properties\": []\n },\n \"ObjectDetection\": {\n \"name\": \"ObjectDetection\",\n \"methods\": [\n \"detectObjects\",\n \"generateObjectDetections\",\n \"getDetectionModel\"\n ],\n \"properties\": []\n },\n \"ObjectDetectionPreview\": {\n \"name\": \"ObjectDetectionPreview\",\n \"methods\": [],\n \"properties\": []\n },\n \"ObjectDetectionGenerator\": {\n \"name\": \"ObjectDetectionGenerator\",\n \"methods\": [],\n \"properties\": []\n },\n \"HumiditySetting\": {\n \"name\": \"HumiditySetting\",\n \"methods\": [\n \"setHumidity\"\n ],\n \"properties\": [\n \"humiditySetting\"\n ]\n },\n \"Fan\": {\n \"name\": \"Fan\",\n \"methods\": [\n \"setFan\"\n ],\n \"properties\": [\n \"fan\"\n ]\n },\n \"RTCSignalingChannel\": {\n \"name\": \"RTCSignalingChannel\",\n \"methods\": [\n \"startRTCSignalingSession\"\n ],\n \"properties\": []\n },\n \"RTCSignalingClient\": {\n \"name\": \"RTCSignalingClient\",\n \"methods\": [\n \"createRTCSignalingSession\"\n ],\n \"properties\": []\n },\n \"LauncherApplication\": {\n \"name\": \"LauncherApplication\",\n \"methods\": [],\n \"properties\": [\n \"applicationInfo\"\n ]\n },\n \"ScryptedUser\": {\n \"name\": \"ScryptedUser\",\n \"methods\": [\n \"getScryptedUserAccessControl\"\n ],\n \"properties\": []\n },\n \"VideoFrameGenerator\": {\n \"name\": \"VideoFrameGenerator\",\n \"methods\": [\n \"generateVideoFrames\"\n ],\n \"properties\": []\n },\n \"StreamService\": {\n \"name\": \"StreamService\",\n \"methods\": [\n \"connectStream\"\n ],\n \"properties\": []\n },\n \"TTY\": {\n \"name\": \"TTY\",\n \"methods\": [],\n \"properties\": []\n },\n \"TTYSettings\": {\n \"name\": \"TTYSettings\",\n \"methods\": [\n \"getTTYSettings\"\n ],\n \"properties\": []\n },\n \"ScryptedSystemDevice\": {\n \"name\": \"ScryptedSystemDevice\",\n \"methods\": [],\n \"properties\": [\n \"systemDevice\"\n ]\n },\n \"ScryptedDeviceCreator\": {\n \"name\": \"ScryptedDeviceCreator\",\n \"methods\": [],\n \"properties\": []\n },\n \"ScryptedSettings\": {\n \"name\": \"ScryptedSettings\",\n \"methods\": [],\n \"properties\": []\n }\n};\n/**\n * @category Core Reference\n */\nvar ScryptedDeviceType;\n(function (ScryptedDeviceType) {\n /**\n * @deprecated\n */\n ScryptedDeviceType[\"Builtin\"] = \"Builtin\";\n /**\n * Internal devices will not show up in device lists unless explicitly searched.\n */\n ScryptedDeviceType[\"Internal\"] = \"Internal\";\n ScryptedDeviceType[\"Camera\"] = \"Camera\";\n ScryptedDeviceType[\"Fan\"] = \"Fan\";\n ScryptedDeviceType[\"Light\"] = \"Light\";\n ScryptedDeviceType[\"Switch\"] = \"Switch\";\n ScryptedDeviceType[\"Outlet\"] = \"Outlet\";\n ScryptedDeviceType[\"Sensor\"] = \"Sensor\";\n ScryptedDeviceType[\"Scene\"] = \"Scene\";\n ScryptedDeviceType[\"Program\"] = \"Program\";\n ScryptedDeviceType[\"Automation\"] = \"Automation\";\n ScryptedDeviceType[\"Vacuum\"] = \"Vacuum\";\n ScryptedDeviceType[\"Notifier\"] = \"Notifier\";\n ScryptedDeviceType[\"Thermostat\"] = \"Thermostat\";\n ScryptedDeviceType[\"Lock\"] = \"Lock\";\n ScryptedDeviceType[\"PasswordControl\"] = \"PasswordControl\";\n /**\n * Displays have audio and video output.\n */\n ScryptedDeviceType[\"Display\"] = \"Display\";\n /**\n * Smart Displays have two way audio and video.\n */\n ScryptedDeviceType[\"SmartDisplay\"] = \"SmartDisplay\";\n ScryptedDeviceType[\"Speaker\"] = \"Speaker\";\n /**\n * Smart Speakers have two way audio.\n */\n ScryptedDeviceType[\"SmartSpeaker\"] = \"SmartSpeaker\";\n ScryptedDeviceType[\"Event\"] = \"Event\";\n ScryptedDeviceType[\"Entry\"] = \"Entry\";\n ScryptedDeviceType[\"Garage\"] = \"Garage\";\n ScryptedDeviceType[\"DeviceProvider\"] = \"DeviceProvider\";\n ScryptedDeviceType[\"DataSource\"] = \"DataSource\";\n ScryptedDeviceType[\"API\"] = \"API\";\n ScryptedDeviceType[\"Doorbell\"] = \"Doorbell\";\n ScryptedDeviceType[\"Irrigation\"] = \"Irrigation\";\n ScryptedDeviceType[\"Valve\"] = \"Valve\";\n ScryptedDeviceType[\"Person\"] = \"Person\";\n ScryptedDeviceType[\"SecuritySystem\"] = \"SecuritySystem\";\n ScryptedDeviceType[\"WindowCovering\"] = \"WindowCovering\";\n ScryptedDeviceType[\"Siren\"] = \"Siren\";\n ScryptedDeviceType[\"AirPurifier\"] = \"AirPurifier\";\n ScryptedDeviceType[\"Internet\"] = \"Internet\";\n ScryptedDeviceType[\"Network\"] = \"Network\";\n ScryptedDeviceType[\"Bridge\"] = \"Bridge\";\n ScryptedDeviceType[\"Unknown\"] = \"Unknown\";\n})(ScryptedDeviceType || (exports.ScryptedDeviceType = ScryptedDeviceType = {}));\nvar HumidityMode;\n(function (HumidityMode) {\n HumidityMode[\"Humidify\"] = \"Humidify\";\n HumidityMode[\"Dehumidify\"] = \"Dehumidify\";\n HumidityMode[\"Auto\"] = \"Auto\";\n HumidityMode[\"Off\"] = \"Off\";\n})(HumidityMode || (exports.HumidityMode = HumidityMode = {}));\nvar FanMode;\n(function (FanMode) {\n FanMode[\"Auto\"] = \"Auto\";\n FanMode[\"Manual\"] = \"Manual\";\n})(FanMode || (exports.FanMode = FanMode = {}));\nvar TemperatureUnit;\n(function (TemperatureUnit) {\n TemperatureUnit[\"C\"] = \"C\";\n TemperatureUnit[\"F\"] = \"F\";\n})(TemperatureUnit || (exports.TemperatureUnit = TemperatureUnit = {}));\nvar ThermostatMode;\n(function (ThermostatMode) {\n ThermostatMode[\"Off\"] = \"Off\";\n ThermostatMode[\"Cool\"] = \"Cool\";\n ThermostatMode[\"Heat\"] = \"Heat\";\n ThermostatMode[\"HeatCool\"] = \"HeatCool\";\n ThermostatMode[\"Auto\"] = \"Auto\";\n ThermostatMode[\"FanOnly\"] = \"FanOnly\";\n ThermostatMode[\"Purifier\"] = \"Purifier\";\n ThermostatMode[\"Eco\"] = \"Eco\";\n ThermostatMode[\"Dry\"] = \"Dry\";\n ThermostatMode[\"On\"] = \"On\";\n})(ThermostatMode || (exports.ThermostatMode = ThermostatMode = {}));\nvar PanTiltZoomMovement;\n(function (PanTiltZoomMovement) {\n PanTiltZoomMovement[\"Absolute\"] = \"Absolute\";\n PanTiltZoomMovement[\"Relative\"] = \"Relative\";\n PanTiltZoomMovement[\"Continuous\"] = \"Continuous\";\n PanTiltZoomMovement[\"Preset\"] = \"Preset\";\n PanTiltZoomMovement[\"Home\"] = \"Home\";\n})(PanTiltZoomMovement || (exports.PanTiltZoomMovement = PanTiltZoomMovement = {}));\nvar LockState;\n(function (LockState) {\n LockState[\"Locked\"] = \"Locked\";\n LockState[\"Unlocked\"] = \"Unlocked\";\n LockState[\"Jammed\"] = \"Jammed\";\n})(LockState || (exports.LockState = LockState = {}));\nvar ChargeState;\n(function (ChargeState) {\n ChargeState[\"Trickle\"] = \"trickle\";\n ChargeState[\"Charging\"] = \"charging\";\n ChargeState[\"NotCharging\"] = \"not-charging\";\n})(ChargeState || (exports.ChargeState = ChargeState = {}));\nvar AirPurifierStatus;\n(function (AirPurifierStatus) {\n AirPurifierStatus[\"Inactive\"] = \"Inactive\";\n AirPurifierStatus[\"Idle\"] = \"Idle\";\n AirPurifierStatus[\"Active\"] = \"Active\";\n AirPurifierStatus[\"ActiveNightMode\"] = \"ActiveNightMode\";\n})(AirPurifierStatus || (exports.AirPurifierStatus = AirPurifierStatus = {}));\nvar AirPurifierMode;\n(function (AirPurifierMode) {\n AirPurifierMode[\"Manual\"] = \"Manual\";\n AirPurifierMode[\"Automatic\"] = \"Automatic\";\n})(AirPurifierMode || (exports.AirPurifierMode = AirPurifierMode = {}));\nvar AirQuality;\n(function (AirQuality) {\n AirQuality[\"Unknown\"] = \"Unknown\";\n AirQuality[\"Excellent\"] = \"Excellent\";\n AirQuality[\"Good\"] = \"Good\";\n AirQuality[\"Fair\"] = \"Fair\";\n AirQuality[\"Inferior\"] = \"Inferior\";\n AirQuality[\"Poor\"] = \"Poor\";\n})(AirQuality || (exports.AirQuality = AirQuality = {}));\nvar SecuritySystemMode;\n(function (SecuritySystemMode) {\n SecuritySystemMode[\"Disarmed\"] = \"Disarmed\";\n SecuritySystemMode[\"HomeArmed\"] = \"HomeArmed\";\n SecuritySystemMode[\"AwayArmed\"] = \"AwayArmed\";\n SecuritySystemMode[\"NightArmed\"] = \"NightArmed\";\n})(SecuritySystemMode || (exports.SecuritySystemMode = SecuritySystemMode = {}));\nvar SecuritySystemObstruction;\n(function (SecuritySystemObstruction) {\n SecuritySystemObstruction[\"Sensor\"] = \"Sensor\";\n SecuritySystemObstruction[\"Occupied\"] = \"Occupied\";\n SecuritySystemObstruction[\"Time\"] = \"Time\";\n SecuritySystemObstruction[\"Error\"] = \"Error\";\n})(SecuritySystemObstruction || (exports.SecuritySystemObstruction = SecuritySystemObstruction = {}));\nvar MediaPlayerState;\n(function (MediaPlayerState) {\n MediaPlayerState[\"Idle\"] = \"Idle\";\n MediaPlayerState[\"Playing\"] = \"Playing\";\n MediaPlayerState[\"Paused\"] = \"Paused\";\n MediaPlayerState[\"Buffering\"] = \"Buffering\";\n})(MediaPlayerState || (exports.MediaPlayerState = MediaPlayerState = {}));\nvar ScryptedInterface;\n(function (ScryptedInterface) {\n ScryptedInterface[\"ScryptedDevice\"] = \"ScryptedDevice\";\n ScryptedInterface[\"ScryptedPlugin\"] = \"ScryptedPlugin\";\n ScryptedInterface[\"ScryptedPluginRuntime\"] = \"ScryptedPluginRuntime\";\n ScryptedInterface[\"OnOff\"] = \"OnOff\";\n ScryptedInterface[\"Brightness\"] = \"Brightness\";\n ScryptedInterface[\"ColorSettingTemperature\"] = \"ColorSettingTemperature\";\n ScryptedInterface[\"ColorSettingRgb\"] = \"ColorSettingRgb\";\n ScryptedInterface[\"ColorSettingHsv\"] = \"ColorSettingHsv\";\n ScryptedInterface[\"Buttons\"] = \"Buttons\";\n ScryptedInterface[\"PressButtons\"] = \"PressButtons\";\n ScryptedInterface[\"Sensors\"] = \"Sensors\";\n ScryptedInterface[\"Notifier\"] = \"Notifier\";\n ScryptedInterface[\"StartStop\"] = \"StartStop\";\n ScryptedInterface[\"Pause\"] = \"Pause\";\n ScryptedInterface[\"Dock\"] = \"Dock\";\n ScryptedInterface[\"TemperatureSetting\"] = \"TemperatureSetting\";\n ScryptedInterface[\"Thermometer\"] = \"Thermometer\";\n ScryptedInterface[\"HumiditySensor\"] = \"HumiditySensor\";\n ScryptedInterface[\"Camera\"] = \"Camera\";\n ScryptedInterface[\"Microphone\"] = \"Microphone\";\n ScryptedInterface[\"AudioVolumeControl\"] = \"AudioVolumeControl\";\n ScryptedInterface[\"Display\"] = \"Display\";\n ScryptedInterface[\"VideoCamera\"] = \"VideoCamera\";\n ScryptedInterface[\"VideoCameraMask\"] = \"VideoCameraMask\";\n ScryptedInterface[\"VideoTextOverlays\"] = \"VideoTextOverlays\";\n ScryptedInterface[\"VideoRecorder\"] = \"VideoRecorder\";\n ScryptedInterface[\"VideoRecorderManagement\"] = \"VideoRecorderManagement\";\n ScryptedInterface[\"PanTiltZoom\"] = \"PanTiltZoom\";\n ScryptedInterface[\"EventRecorder\"] = \"EventRecorder\";\n ScryptedInterface[\"VideoClips\"] = \"VideoClips\";\n ScryptedInterface[\"VideoCameraConfiguration\"] = \"VideoCameraConfiguration\";\n ScryptedInterface[\"Intercom\"] = \"Intercom\";\n ScryptedInterface[\"Lock\"] = \"Lock\";\n ScryptedInterface[\"PasswordStore\"] = \"PasswordStore\";\n ScryptedInterface[\"Scene\"] = \"Scene\";\n ScryptedInterface[\"Entry\"] = \"Entry\";\n ScryptedInterface[\"EntrySensor\"] = \"EntrySensor\";\n ScryptedInterface[\"DeviceProvider\"] = \"DeviceProvider\";\n ScryptedInterface[\"DeviceDiscovery\"] = \"DeviceDiscovery\";\n ScryptedInterface[\"DeviceCreator\"] = \"DeviceCreator\";\n ScryptedInterface[\"Battery\"] = \"Battery\";\n ScryptedInterface[\"Charger\"] = \"Charger\";\n ScryptedInterface[\"Reboot\"] = \"Reboot\";\n ScryptedInterface[\"Refresh\"] = \"Refresh\";\n ScryptedInterface[\"MediaPlayer\"] = \"MediaPlayer\";\n ScryptedInterface[\"Online\"] = \"Online\";\n ScryptedInterface[\"BufferConverter\"] = \"BufferConverter\";\n ScryptedInterface[\"MediaConverter\"] = \"MediaConverter\";\n ScryptedInterface[\"Settings\"] = \"Settings\";\n ScryptedInterface[\"BinarySensor\"] = \"BinarySensor\";\n ScryptedInterface[\"TamperSensor\"] = \"TamperSensor\";\n ScryptedInterface[\"Sleep\"] = \"Sleep\";\n ScryptedInterface[\"PowerSensor\"] = \"PowerSensor\";\n ScryptedInterface[\"AudioSensor\"] = \"AudioSensor\";\n ScryptedInterface[\"MotionSensor\"] = \"MotionSensor\";\n ScryptedInterface[\"AmbientLightSensor\"] = \"AmbientLightSensor\";\n ScryptedInterface[\"OccupancySensor\"] = \"OccupancySensor\";\n ScryptedInterface[\"FloodSensor\"] = \"FloodSensor\";\n ScryptedInterface[\"UltravioletSensor\"] = \"UltravioletSensor\";\n ScryptedInterface[\"LuminanceSensor\"] = \"LuminanceSensor\";\n ScryptedInterface[\"PositionSensor\"] = \"PositionSensor\";\n ScryptedInterface[\"SecuritySystem\"] = \"SecuritySystem\";\n ScryptedInterface[\"PM10Sensor\"] = \"PM10Sensor\";\n ScryptedInterface[\"PM25Sensor\"] = \"PM25Sensor\";\n ScryptedInterface[\"VOCSensor\"] = \"VOCSensor\";\n ScryptedInterface[\"NOXSensor\"] = \"NOXSensor\";\n ScryptedInterface[\"CO2Sensor\"] = \"CO2Sensor\";\n ScryptedInterface[\"AirQualitySensor\"] = \"AirQualitySensor\";\n ScryptedInterface[\"AirPurifier\"] = \"AirPurifier\";\n ScryptedInterface[\"FilterMaintenance\"] = \"FilterMaintenance\";\n ScryptedInterface[\"Readme\"] = \"Readme\";\n ScryptedInterface[\"OauthClient\"] = \"OauthClient\";\n ScryptedInterface[\"MixinProvider\"] = \"MixinProvider\";\n ScryptedInterface[\"HttpRequestHandler\"] = \"HttpRequestHandler\";\n ScryptedInterface[\"EngineIOHandler\"] = \"EngineIOHandler\";\n ScryptedInterface[\"PushHandler\"] = \"PushHandler\";\n ScryptedInterface[\"Program\"] = \"Program\";\n ScryptedInterface[\"Scriptable\"] = \"Scriptable\";\n ScryptedInterface[\"ClusterForkInterface\"] = \"ClusterForkInterface\";\n ScryptedInterface[\"ObjectTracker\"] = \"ObjectTracker\";\n ScryptedInterface[\"ObjectDetector\"] = \"ObjectDetector\";\n ScryptedInterface[\"ObjectDetection\"] = \"ObjectDetection\";\n ScryptedInterface[\"ObjectDetectionPreview\"] = \"ObjectDetectionPreview\";\n ScryptedInterface[\"ObjectDetectionGenerator\"] = \"ObjectDetectionGenerator\";\n ScryptedInterface[\"HumiditySetting\"] = \"HumiditySetting\";\n ScryptedInterface[\"Fan\"] = \"Fan\";\n ScryptedInterface[\"RTCSignalingChannel\"] = \"RTCSignalingChannel\";\n ScryptedInterface[\"RTCSignalingClient\"] = \"RTCSignalingClient\";\n ScryptedInterface[\"LauncherApplication\"] = \"LauncherApplication\";\n ScryptedInterface[\"ScryptedUser\"] = \"ScryptedUser\";\n ScryptedInterface[\"VideoFrameGenerator\"] = \"VideoFrameGenerator\";\n ScryptedInterface[\"StreamService\"] = \"StreamService\";\n ScryptedInterface[\"TTY\"] = \"TTY\";\n ScryptedInterface[\"TTYSettings\"] = \"TTYSettings\";\n ScryptedInterface[\"ScryptedSystemDevice\"] = \"ScryptedSystemDevice\";\n ScryptedInterface[\"ScryptedDeviceCreator\"] = \"ScryptedDeviceCreator\";\n ScryptedInterface[\"ScryptedSettings\"] = \"ScryptedSettings\";\n})(ScryptedInterface || (exports.ScryptedInterface = ScryptedInterface = {}));\nvar ScryptedMimeTypes;\n(function (ScryptedMimeTypes) {\n ScryptedMimeTypes[\"Url\"] = \"text/x-uri\";\n ScryptedMimeTypes[\"InsecureLocalUrl\"] = \"text/x-insecure-local-uri\";\n ScryptedMimeTypes[\"LocalUrl\"] = \"text/x-local-uri\";\n ScryptedMimeTypes[\"ServerId\"] = \"text/x-server-id\";\n ScryptedMimeTypes[\"PushEndpoint\"] = \"text/x-push-endpoint\";\n ScryptedMimeTypes[\"SchemePrefix\"] = \"x-scrypted/x-scrypted-scheme-\";\n ScryptedMimeTypes[\"MediaStreamUrl\"] = \"text/x-media-url\";\n ScryptedMimeTypes[\"MediaObject\"] = \"x-scrypted/x-scrypted-media-object\";\n ScryptedMimeTypes[\"RequestMediaObject\"] = \"x-scrypted/x-scrypted-request-media-object\";\n ScryptedMimeTypes[\"RequestMediaStream\"] = \"x-scrypted/x-scrypted-request-stream\";\n ScryptedMimeTypes[\"MediaStreamFeedback\"] = \"x-scrypted/x-media-stream-feedback\";\n ScryptedMimeTypes[\"FFmpegInput\"] = \"x-scrypted/x-ffmpeg-input\";\n ScryptedMimeTypes[\"FFmpegTranscodeStream\"] = \"x-scrypted/x-ffmpeg-transcode-stream\";\n ScryptedMimeTypes[\"RTCSignalingChannel\"] = \"x-scrypted/x-scrypted-rtc-signaling-channel\";\n ScryptedMimeTypes[\"RTCSignalingSession\"] = \"x-scrypted/x-scrypted-rtc-signaling-session\";\n ScryptedMimeTypes[\"RTCConnectionManagement\"] = \"x-scrypted/x-scrypted-rtc-connection-management\";\n ScryptedMimeTypes[\"Image\"] = \"x-scrypted/x-scrypted-image\";\n})(ScryptedMimeTypes || (exports.ScryptedMimeTypes = ScryptedMimeTypes = {}));\n//# sourceMappingURL=index.js.map","import sdk, {\n ScryptedDeviceBase,\n Settings,\n Setting,\n HttpRequestHandler,\n HttpRequest,\n HttpResponse,\n ScryptedInterface,\n ScryptedDeviceType,\n ObjectsDetected,\n ObjectDetector,\n} from '@scrypted/sdk';\nimport fs from 'fs';\nimport path from 'path';\n\nconst { systemManager, deviceManager, mediaManager } = sdk;\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst LABELS = ['person', 'animal', 'face', 'vehicle', 'plate', 'package', 'discard'] as const;\ntype Label = typeof LABELS[number];\n\nconst CAPTURE_CLASSES = new Set(['person', 'cat', 'dog', 'animal', 'bird', 'face', 'vehicle', 'car', 'truck', 'bus', 'motorcycle', 'bicycle', 'plate', 'package']);\n\nconst RATE_OPTIONS = ['disabled', '1 per minute', '1 per 10 seconds', 'every detection'] as const;\ntype RateOption = typeof RATE_OPTIONS[number];\n\nconst RATE_MS: Record<RateOption, number> = {\n 'disabled': Infinity,\n '1 per minute': 60_000,\n '1 per 10 seconds': 10_000,\n 'every detection': 0,\n};\n\nconst MAX_CAPTURES = 2000;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface CaptureRecord {\n id: string;\n cameraId: string;\n cameraName: string;\n timestamp: number;\n detectedClass: string;\n score: number;\n boundingBox: number[]; // [x, y, w, h] in pixels\n inputDimensions: number[]; // [width, height]\n detectionId?: string;\n label?: Label; // set after review\n reviewed: boolean;\n}\n\n// ─── Plugin ───────────────────────────────────────────────────────────────────\n\nclass DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpRequestHandler {\n // Map<cameraId, lastCaptureTimestamp>\n private lastCapture = new Map<string, number>();\n // Map<captureId, CaptureRecord>\n private captures = new Map<string, CaptureRecord>();\n // Active event listeners\n private listeners: (() => void)[] = [];\n // Directory for storing images on disk\n private imgDir: string;\n\n constructor(nativeId?: string) {\n super(nativeId);\n // Use a stable directory inside the plugin's volume\n this.imgDir = path.join(process.env.SCRYPTED_PLUGIN_VOLUME || '/tmp', 'detection-trainer-images');\n try { fs.mkdirSync(this.imgDir, { recursive: true }); } catch {}\n this.loadState();\n this.registerListeners();\n }\n\n // ── Persistence ──────────────────────────────────────────────────────────\n\n private loadState() {\n try {\n const raw = this.storage.getItem('captures');\n if (raw) {\n const arr: CaptureRecord[] = JSON.parse(raw);\n for (const r of arr) this.captures.set(r.id, r);\n this.console.log(`Loaded ${this.captures.size} captures from storage.`);\n }\n } catch (e) {\n this.console.warn('Could not load captures from storage:', e);\n }\n // Clean up any old base64 image entries from previous versions\n for (const [id] of this.captures) {\n try { this.storage.removeItem(`img:${id}`); } catch {}\n }\n }\n\n private saveCaptures() {\n try {\n this.storage.setItem('captures', JSON.stringify([...this.captures.values()]));\n } catch (e) {\n this.console.warn('Could not save captures:', e);\n }\n }\n\n private imgPath(id: string): string {\n return path.join(this.imgDir, `${id}.jpg`);\n }\n\n private saveImage(id: string, buf: Buffer) {\n try {\n fs.writeFileSync(this.imgPath(id), buf);\n } catch (e) {\n this.console.warn(`Could not save image ${id}:`, e);\n }\n }\n\n private loadImage(id: string): Buffer | undefined {\n try {\n const p = this.imgPath(id);\n if (fs.existsSync(p)) return fs.readFileSync(p);\n } catch {}\n return undefined;\n }\n\n private deleteCapture(id: string) {\n try { fs.unlinkSync(this.imgPath(id)); } catch {}\n this.captures.delete(id);\n this.saveCaptures();\n }\n\n // ── Settings ─────────────────────────────────────────────────────────────\n\n async getSettings(): Promise<Setting[]> {\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n );\n\n let uiUrl: string;\n try {\n uiUrl = await sdk.endpointManager.getLocalEndpoint(undefined, { public: true });\n } catch {\n uiUrl = '/endpoint/scrypted-detection-trainer/public/';\n }\n\n const settings: Setting[] = [\n {\n key: 'info',\n title: 'Detection Trainer',\n 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).`,\n readonly: true,\n value: '',\n },\n {\n key: 'open_ui',\n title: 'Review UI',\n description: 'Open the detection review and labeling interface.',\n type: 'html',\n readonly: true,\n value: `<a href=\"${uiUrl}\" target=\"_blank\" style=\"display:inline-block;padding:8px 16px;background:#1a4d8a;color:#fff;border-radius:6px;text-decoration:none;font-size:13px;\">Open Review UI ↗</a>`,\n },\n {\n key: 'autoCapture',\n title: 'Auto-Capture',\n description: 'Automatically capture detections in the background. Disable to use manual browsing only.',\n type: 'boolean',\n value: (this.storage.getItem('autoCapture') ?? 'true'),\n },\n ];\n\n for (const cam of cameras) {\n const key = `rate:${cam.id}`;\n settings.push({\n key,\n title: cam.name,\n group: 'Capture Rate per Camera',\n description: 'How often to capture detections from this camera.',\n value: this.storage.getItem(key) || '1 per minute',\n choices: [...RATE_OPTIONS],\n });\n }\n\n return settings;\n }\n\n async putSetting(key: string, value: string) {\n if (key === 'open_ui' || key === 'ui_link' || key === 'info') return;\n this.storage.setItem(key, value);\n if (key.startsWith('rate:')) {\n // Re-register listeners when rates change\n this.registerListeners();\n }\n }\n\n // ── Listeners ─────────────────────────────────────────────────────────────\n\n private registerListeners() {\n // Remove old listeners\n for (const remove of this.listeners) remove();\n this.listeners = [];\n\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n );\n\n for (const cam of cameras) {\n const rateKey = `rate:${cam.id}`;\n const rateLabel = (this.storage.getItem(rateKey) || '1 per minute') as RateOption;\n if (rateLabel === 'disabled') continue;\n\n const listener = cam.listen(ScryptedInterface.ObjectDetector, async (source, details, data) => {\n await this.onDetection(cam.id, cam.name, data as ObjectsDetected, RATE_MS[rateLabel]);\n });\n this.listeners.push(() => listener.removeListener());\n }\n\n this.console.log(`Listening to ${this.listeners.length} camera(s).`);\n }\n\n // ── Detection Handler ─────────────────────────────────────────────────────\n\n private async onDetection(cameraId: string, cameraName: string, data: ObjectsDetected, rateLimitMs: number) {\n if ((this.storage.getItem('autoCapture') ?? 'true') === 'false') return;\n if (!data?.detections?.length || !data.inputDimensions) return;\n\n // Rate limit per camera\n const now = Date.now();\n const last = this.lastCapture.get(cameraId) || 0;\n if (now - last < rateLimitMs) return;\n\n // Filter to target classes\n const targets = data.detections.filter(d =>\n d.className && CAPTURE_CLASSES.has(d.className.toLowerCase()) && d.boundingBox\n );\n if (!targets.length) return;\n\n // Pick the highest-confidence target detection\n const best = targets.sort((a, b) => (b.score || 0) - (a.score || 0))[0];\n\n // Enforce max storage\n if (this.captures.size >= MAX_CAPTURES) {\n // Evict oldest unreviewed capture\n const oldest = [...this.captures.values()]\n .filter(c => !c.reviewed)\n .sort((a, b) => a.timestamp - b.timestamp)[0];\n if (oldest) this.deleteCapture(oldest.id);\n else return; // All reviewed, don't evict labeled data\n }\n\n this.lastCapture.set(cameraId, now);\n\n // Try to get the detection image\n let jpeg: Buffer | undefined;\n try {\n if (data.detectionId) {\n const cam = systemManager.getDeviceById(cameraId) as unknown as ObjectDetector;\n const mo = await cam.getDetectionInput(data.detectionId);\n jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n }\n } catch (e) {\n this.console.warn(`Could not get detection image for ${cameraName}:`, e);\n }\n\n if (!jpeg) return; // Skip if no image\n\n const id = `${now}-${Math.random().toString(36).slice(2, 8)}`;\n const record: CaptureRecord = {\n id,\n cameraId,\n cameraName,\n timestamp: now,\n detectedClass: best.className!,\n score: best.score || 0,\n boundingBox: best.boundingBox as number[],\n inputDimensions: data.inputDimensions as number[],\n detectionId: data.detectionId,\n reviewed: false,\n };\n\n this.captures.set(id, record);\n this.saveImage(id, jpeg);\n this.saveCaptures();\n\n this.console.log(`Captured ${best.className} (${Math.round((best.score || 0) * 100)}%) from ${cameraName} [${this.captures.size} total]`);\n }\n\n // ── HTTP Handler ──────────────────────────────────────────────────────────\n\n async onRequest(request: HttpRequest, response: HttpResponse) {\n const url = new URL(request.url, 'http://localhost');\n const path = url.pathname.replace(request.rootPath, '');\n\n // Serve browse event image via getVideoClipThumbnail\n if (path === '/api/browse-img' && request.body) {\n const rawBody = request.body as any;\n const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));\n const { cameraId, thumbnailId } = body;\n if (!cameraId || thumbnailId === undefined) return response.send('Missing params', { code: 400 });\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n const mo = await cam.getVideoClipThumbnail(thumbnailId);\n const jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n return response.send(jpeg, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=3600' } });\n } catch (e) {\n return response.send('Image unavailable', { code: 404 });\n }\n }\n\n // Serve image\n if (path.startsWith('/img/')) {\n const id = path.slice(5).replace(/[^a-zA-Z0-9_\\-]/g, ''); // sanitize\n const img = this.loadImage(id);\n if (!img) return response.send('Not found', { code: 404 });\n return response.send(img, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=3600' } });\n }\n\n // API: label a capture\n if (path === '/api/label' && request.body) {\n const rawBody = request.body as any;\n const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));\n const record = this.captures.get(body.id);\n if (!record) return response.send('Not found', { code: 404 });\n record.label = body.label as Label;\n record.reviewed = true;\n if (body.label === 'discard') {\n this.deleteCapture(body.id);\n } else {\n this.captures.set(body.id, record);\n this.saveCaptures();\n }\n return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: list cameras for browse\n if (path === '/api/cameras') {\n const cameras = Object.keys(systemManager.getSystemState())\n .map(id => systemManager.getDeviceById(id))\n .filter(d => d &&\n (d.type === ScryptedDeviceType.Camera || d.type === ScryptedDeviceType.Doorbell) &&\n d.interfaces?.includes(ScryptedInterface.ObjectDetector)\n )\n .map(d => ({ id: d.id, name: d.name }));\n return response.send(JSON.stringify(cameras), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: browse recent events for a camera\n if (path === '/api/browse') {\n const params = new URL(request.url, 'http://localhost').searchParams;\n const cameraId = params.get('cameraId');\n const hours = parseInt(params.get('hours') || '24');\n if (!cameraId) return response.send('Missing cameraId', { code: 400 });\n\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n if (!cam) return response.send('Camera not found', { code: 404 });\n\n const endTime = Date.now();\n const startTime = endTime - hours * 3600 * 1000;\n const clips = await cam.getVideoClips({ startTime, endTime });\n\n const limit = parseInt(params.get('limit') || '100');\n const events = (clips || [])\n .filter((c: any) => c.detectionClasses?.length && c.thumbnailId)\n .slice(0, limit)\n .map((c: any) => ({\n clipId: c.id,\n thumbnailId: c.thumbnailId,\n timestamp: c.startTime,\n detectionClasses: c.detectionClasses || [],\n // bounding box not available at clip level — use full frame\n boundingBox: null,\n inputDimensions: null,\n cameraId,\n cameraName: cam.name,\n }));\n\n return response.send(JSON.stringify(events), { headers: { 'Content-Type': 'application/json' } });\n } catch (e: any) {\n return response.send(JSON.stringify({ error: e.message }), { headers: { 'Content-Type': 'application/json' }, code: 500 });\n }\n }\n\n // API: add a browsed event directly to dataset as labeled\n if (path === '/api/add-event' && request.body) {\n const rawBody = request.body as any;\n const body = JSON.parse(typeof rawBody === 'string' ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString() : String(rawBody));\n const { cameraId, cameraName, thumbnailId, timestamp, detectedClass, boundingBox, inputDimensions, label } = body;\n\n if (!label || label === 'discard') return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });\n\n // Get image via thumbnail\n let jpeg: Buffer | undefined;\n try {\n const cam = systemManager.getDeviceById(cameraId) as any;\n const mo = await cam.getVideoClipThumbnail(thumbnailId);\n jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n } catch (e) {\n this.console.warn(`Could not get thumbnail for browse event:`, e);\n }\n\n if (!jpeg) return response.send(JSON.stringify({ error: 'Could not retrieve image' }), { headers: { 'Content-Type': 'application/json' }, code: 500 });\n\n const id = `browse-${timestamp}-${Math.random().toString(36).slice(2, 6)}`;\n // For clips we don't have per-detection bounding boxes — store full frame dimensions\n const record: CaptureRecord = {\n id, cameraId, cameraName, timestamp,\n detectedClass: detectedClass || 'unknown',\n score: 1,\n boundingBox: boundingBox || [0, 0, inputDimensions?.[0] || 1920, inputDimensions?.[1] || 1080],\n inputDimensions: inputDimensions || [1920, 1080],\n reviewed: true, label,\n };\n this.captures.set(id, record);\n this.saveImage(id, jpeg);\n this.saveCaptures();\n\n return response.send(JSON.stringify({ ok: true, id }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: get pending captures\n if (path === '/api/pending') {\n const pending = [...this.captures.values()]\n .filter(c => !c.reviewed)\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, 50);\n return response.send(JSON.stringify(pending), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: get labeled captures\n if (path === '/api/labeled') {\n const page = parseInt(new URL(request.url, 'http://localhost').searchParams.get('page') || '0');\n const pageSize = 50;\n const labeled = [...this.captures.values()]\n .filter(c => c.reviewed)\n .sort((a, b) => b.timestamp - a.timestamp);\n const slice = labeled.slice(page * pageSize, (page + 1) * pageSize);\n return response.send(JSON.stringify({ items: slice, total: labeled.length, page, pageSize }), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: stats\n if (path === '/api/stats') {\n const all = [...this.captures.values()];\n const stats = {\n total: all.length,\n pending: all.filter(c => !c.reviewed).length,\n labeled: all.filter(c => c.reviewed && c.label !== 'discard').length,\n byLabel: {} as Record<string, number>,\n byCamera: {} as Record<string, number>,\n byDetectedClass: {} as Record<string, number>,\n };\n for (const r of all) {\n if (r.label) stats.byLabel[r.label] = (stats.byLabel[r.label] || 0) + 1;\n stats.byCamera[r.cameraName] = (stats.byCamera[r.cameraName] || 0) + 1;\n stats.byDetectedClass[r.detectedClass] = (stats.byDetectedClass[r.detectedClass] || 0) + 1;\n }\n return response.send(JSON.stringify(stats), { headers: { 'Content-Type': 'application/json' } });\n }\n\n // API: export YOLO dataset\n if (path === '/api/export') {\n const labeled = [...this.captures.values()].filter(c => c.reviewed && c.label && c.label !== 'discard');\n if (!labeled.length) return response.send(JSON.stringify({ error: 'No labeled data yet' }), { headers: { 'Content-Type': 'application/json' }, code: 400 });\n\n const classMap: Record<Label, number> = {\n person: 0, animal: 1, face: 2, vehicle: 3, plate: 4, package: 5, discard: -1,\n };\n\n // Build a simple tarball-like structure as JSON for download\n const dataset: { filename: string; content: string; encoding: string }[] = [];\n\n for (const record of labeled) {\n const img = this.loadImage(record.id);\n if (!img) continue;\n\n const fname = `${record.id}`;\n dataset.push({ filename: `images/${fname}.jpg`, content: img.toString('base64'), encoding: 'base64' });\n\n const [x, y, w, h] = record.boundingBox;\n const [imgW, imgH] = record.inputDimensions;\n const cx = (x + w / 2) / imgW;\n const cy = (y + h / 2) / imgH;\n const nw = w / imgW;\n const nh = h / imgH;\n const classId = classMap[record.label!];\n const labelLine = `${classId} ${cx.toFixed(6)} ${cy.toFixed(6)} ${nw.toFixed(6)} ${nh.toFixed(6)}\\n`;\n dataset.push({ filename: `labels/${fname}.txt`, content: labelLine, encoding: 'utf8' });\n }\n\n const yaml = [\n 'path: dataset',\n 'train: images',\n 'val: images',\n '',\n 'nc: 6',\n \"names: ['person', 'animal', 'face', 'vehicle', 'plate', 'package']\",\n '',\n '# Generated by Scrypted Detection Trainer',\n `# ${labeled.length} labeled samples`,\n ].join('\\n');\n dataset.push({ filename: 'data.yaml', content: yaml, encoding: 'utf8' });\n\n return response.send(JSON.stringify({ files: dataset, count: labeled.length }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Serve Web UI\n if (path === '/' || path === '' || path === '/index.html') {\n return response.send(this.renderUI(), { headers: { 'Content-Type': 'text/html' } });\n }\n\n response.send('Not found', { code: 404 });\n }\n\n // ── Web UI ────────────────────────────────────────────────────────────────\n\n private renderUI(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Detection Trainer</title>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js\"></script>\n<style>\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f0f0f; color: #e8e8e8; min-height: 100vh; }\n header { background: #1a1a1a; border-bottom: 1px solid #333; padding: 16px 24px; display: flex; align-items: center; justify-content: space-between; }\n header h1 { font-size: 18px; font-weight: 600; color: #fff; }\n .stats { display: flex; gap: 20px; font-size: 13px; color: #aaa; }\n .stat span { color: #fff; font-weight: 600; }\n .container { max-width: 1000px; margin: 0 auto; padding: 24px; }\n .card { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .card-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; display: flex; align-items: center; justify-content: space-between; }\n .card-header h2 { font-size: 15px; font-weight: 600; }\n .badge { background: #333; color: #aaa; font-size: 12px; padding: 2px 8px; border-radius: 20px; }\n .badge.orange { background: #3d2a00; color: #f90; }\n .badge.green { background: #0d2d0d; color: #4c4; }\n\n /* Detection card */\n .detection { display: grid; grid-template-columns: 420px 1fr; gap: 0; border-bottom: 1px solid #222; }\n .detection:last-child { border-bottom: none; }\n .detection-imgs { display: flex; gap: 8px; padding: 10px; background: #111; align-items: center; }\n .img-panel { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n .img-label { font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: .5px; }\n .det-canvas { border-radius: 6px; display: block; }\n .det-class-badge { display: inline-block; background: #3d2a00; color: #f90; font-size: 11px; padding: 2px 8px; border-radius: 4px; }\n .detection-info { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }\n .detection-meta { font-size: 12px; color: #888; display: flex; flex-wrap: wrap; gap: 10px; }\n .detection-meta strong { color: #ccc; }\n .label-buttons { display: flex; flex-wrap: wrap; gap: 8px; }\n .label-btn { padding: 7px 14px; border-radius: 8px; border: 1px solid #444; background: #222; color: #ccc; cursor: pointer; font-size: 13px; transition: all .15s; }\n .label-btn:hover { border-color: #666; background: #2a2a2a; color: #fff; }\n .label-btn.person { border-color: #2a6; color: #4d9; }\n .label-btn.person:hover { background: #0d2a1a; }\n .label-btn.animal { border-color: #a63; color: #d85; }\n .label-btn.animal:hover { background: #2a1a0d; }\n .label-btn.face { border-color: #49c; color: #6be; }\n .label-btn.face:hover { background: #0d1a2a; }\n .label-btn.vehicle { border-color: #76b; color: #99d; }\n .label-btn.vehicle:hover { background: #1a1a2a; }\n .label-btn.discard { border-color: #622; color: #a44; }\n .label-btn.discard:hover { background: #2a0d0d; }\n .detection.labeled { opacity: 0.4; pointer-events: none; }\n .labeled-tag { font-size: 11px; color: #4d9; background: #0d2a1a; border: 1px solid #2a6; padding: 2px 8px; border-radius: 4px; }\n\n /* Empty state */\n .empty { padding: 48px; text-align: center; color: #555; }\n .empty .icon { font-size: 48px; margin-bottom: 12px; }\n\n /* Export section */\n .export-btn { padding: 10px 20px; background: #1a4d8a; border: none; border-radius: 8px; color: #fff; cursor: pointer; font-size: 14px; font-weight: 500; }\n .export-btn:hover { background: #1e5ca0; }\n .export-btn:disabled { background: #333; color: #666; cursor: not-allowed; }\n .export-info { font-size: 13px; color: #888; padding: 12px 20px; }\n\n /* Progress bar */\n .progress { height: 4px; background: #222; border-radius: 2px; overflow: hidden; margin-top: 8px; }\n .progress-bar { height: 100%; background: #1a6; border-radius: 2px; transition: width .3s; }\n\n .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; }\n .toast.show { opacity: 1; }\n\n .tab-bar { display: flex; gap: 2px; padding: 12px 20px 0; border-bottom: 1px solid #2a2a2a; }\n .tab { padding: 8px 14px; font-size: 13px; color: #888; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }\n .tab.active { color: #fff; border-bottom-color: #4a9; }\n .tab-content { padding: 20px; }\n .tab-panel { display: none; }\n .tab-panel.active { display: block; }\n\n .breakdown-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }\n .breakdown-item { background: #222; border-radius: 8px; padding: 12px; }\n .breakdown-item .label { font-size: 12px; color: #888; margin-bottom: 4px; }\n .breakdown-item .value { font-size: 20px; font-weight: 600; color: #fff; }\n\n /* Lightbox */\n .lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.92); z-index: 1000; align-items: center; justify-content: center; flex-direction: column; gap: 12px; }\n .lightbox.open { display: flex; }\n .lightbox canvas { max-width: 92vw; max-height: 82vh; border-radius: 8px; cursor: zoom-out; }\n .lightbox-meta { color: #aaa; font-size: 13px; text-align: center; }\n .lightbox-close { position: absolute; top: 16px; right: 20px; font-size: 28px; color: #888; cursor: pointer; line-height: 1; }\n .lightbox-close:hover { color: #fff; }\n .det-canvas { cursor: zoom-in; }\n</style>\n</head>\n<body>\n<header>\n <h1>🎯 Detection Trainer</h1>\n <div class=\"stats\">\n <div>Pending <span id=\"stat-pending\">—</span></div>\n <div>Labeled <span id=\"stat-labeled\">—</span></div>\n <div>Total <span id=\"stat-total\">—</span></div>\n </div>\n</header>\n<div class=\"container\">\n\n <div class=\"card\">\n <div class=\"tab-bar\">\n <div class=\"tab active\" onclick=\"showTab('review')\">Review</div>\n <div class=\"tab\" onclick=\"showTab('browse')\">Browse Events</div>\n <div class=\"tab\" onclick=\"showTab('labeled')\">Labeled</div>\n <div class=\"tab\" onclick=\"showTab('stats')\">Stats</div>\n <div class=\"tab\" onclick=\"showTab('export')\">Export Dataset</div>\n </div>\n\n <!-- Review tab -->\n <div class=\"tab-panel active\" id=\"tab-review\">\n <div id=\"detections-list\"></div>\n </div>\n\n <!-- Browse tab -->\n <div class=\"tab-panel\" id=\"tab-browse\">\n <div class=\"tab-content\">\n <div style=\"display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:16px;\">\n <select id=\"browse-camera\" style=\"padding:8px 12px;background:#222;border:1px solid #444;color:#fff;border-radius:6px;font-size:13px;\">\n <option value=\"\">Select camera…</option>\n </select>\n <select id=\"browse-hours\" style=\"padding:8px 12px;background:#222;border:1px solid #444;color:#fff;border-radius:6px;font-size:13px;\">\n <option value=\"1\">Last 1 hour</option>\n <option value=\"6\">Last 6 hours</option>\n <option value=\"24\" selected>Last 24 hours</option>\n <option value=\"72\">Last 3 days</option>\n </select>\n <select id=\"browse-limit\" style=\"padding:8px 12px;background:#222;border:1px solid #444;color:#fff;border-radius:6px;font-size:13px;\">\n <option value=\"50\">50 events</option>\n <option value=\"100\" selected>100 events</option>\n <option value=\"250\">250 events</option>\n <option value=\"500\">500 events</option>\n </select>\n <button class=\"export-btn\" onclick=\"loadBrowse()\" style=\"padding:8px 16px;\">Load Events</button>\n <span id=\"browse-status\" style=\"font-size:13px;color:#888;\"></span>\n </div>\n <div id=\"browse-list\"></div>\n </div>\n </div>\n\n <!-- Labeled tab -->\n <div class=\"tab-panel\" id=\"tab-labeled\">\n <div id=\"labeled-list\"></div>\n </div>\n <div class=\"tab-panel\" id=\"tab-stats\">\n <div class=\"tab-content\">\n <p style=\"font-size:13px;color:#888;margin-bottom:16px;\">Breakdown of captured and labeled detections.</p>\n <h3 style=\"font-size:13px;color:#aaa;margin-bottom:10px;\">By Detected Class (what the model said)</h3>\n <div class=\"breakdown-grid\" id=\"stats-detected\"></div>\n <h3 style=\"font-size:13px;color:#aaa;margin:20px 0 10px;\">By Corrected Label (what you said)</h3>\n <div class=\"breakdown-grid\" id=\"stats-label\"></div>\n <h3 style=\"font-size:13px;color:#aaa;margin:20px 0 10px;\">By Camera</h3>\n <div class=\"breakdown-grid\" id=\"stats-camera\"></div>\n </div>\n </div>\n\n <!-- Export tab -->\n <div class=\"tab-panel\" id=\"tab-export\">\n <div class=\"tab-content\">\n <p style=\"font-size:13px;color:#888;margin-bottom:16px;\">\n Exports a YOLO-format dataset (images + labels + data.yaml) as a downloadable bundle.\n Only labeled detections are included. Review more detections first to build a larger dataset.\n </p>\n <div id=\"export-stats\" class=\"export-info\">Loading…</div>\n <div style=\"display:flex;gap:12px;align-items:center;margin-top:12px;\">\n <button class=\"export-btn\" id=\"export-btn\" onclick=\"exportDataset()\">Download Dataset</button>\n <span id=\"export-status\" style=\"font-size:13px;color:#888;\"></span>\n </div>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"lightbox\" id=\"lightbox\" onclick=\"closeLightbox()\">\n <div class=\"lightbox-close\" onclick=\"closeLightbox()\">✕</div>\n <canvas id=\"lightbox-canvas\"></canvas>\n <div class=\"lightbox-meta\" id=\"lightbox-meta\"></div>\n</div>\n\n<div class=\"toast\" id=\"toast\"></div>\n\n<script>\nconst BASE = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname;\nlet pending = [];\nlet labeledCount = 0;\n\nfunction imgError(img) {\n img.parentElement.innerHTML = '<div style=\"padding:20px;color:#555;font-size:12px;text-align:center\">No image</div>';\n}\n\nfunction drawDetection(img, r) {\n const [bx, by, bw, bh] = r.boundingBox;\n const [iw, ih] = r.inputDimensions;\n\n // --- Full frame canvas with box overlay ---\n const fullCanvas = document.getElementById('canvas-full-' + r.id);\n if (fullCanvas) {\n const cw = fullCanvas.width, ch = fullCanvas.height;\n const scale = Math.min(cw / iw, ch / ih);\n const dw = iw * scale, dh = ih * scale;\n const ox = (cw - dw) / 2, oy = (ch - dh) / 2;\n const ctx = fullCanvas.getContext('2d');\n ctx.fillStyle = '#111';\n ctx.fillRect(0, 0, cw, ch);\n ctx.drawImage(img, ox, oy, dw, dh);\n // Draw bounding box\n const rx = ox + bx * scale, ry = oy + by * scale;\n const rw = bw * scale, rh = bh * scale;\n ctx.strokeStyle = '#f90';\n ctx.lineWidth = 2;\n ctx.strokeRect(rx, ry, rw, rh);\n // Label badge\n ctx.fillStyle = 'rgba(255,153,0,0.85)';\n ctx.fillRect(rx, ry - 18, rw, 18);\n ctx.fillStyle = '#000';\n ctx.font = 'bold 11px sans-serif';\n ctx.fillText(r.detectedClass + ' ' + Math.round(r.score * 100) + '%', rx + 3, ry - 4);\n }\n\n // --- Crop canvas ---\n const cropCanvas = document.getElementById('canvas-crop-' + r.id);\n if (cropCanvas) {\n const cc = cropCanvas.width, ch2 = cropCanvas.height;\n const ctx2 = cropCanvas.getContext('2d');\n ctx2.fillStyle = '#111';\n ctx2.fillRect(0, 0, cc, ch2);\n // Add padding around the crop\n const pad = Math.min(bw, bh) * 0.15;\n const sx = Math.max(0, bx - pad), sy = Math.max(0, by - pad);\n const sw = Math.min(iw - sx, bw + pad * 2), sh = Math.min(ih - sy, bh + pad * 2);\n const scale2 = Math.min(cc / sw, ch2 / sh);\n const dw2 = sw * scale2, dh2 = sh * scale2;\n const ox2 = (cc - dw2) / 2, oy2 = (ch2 - dh2) / 2;\n ctx2.drawImage(img, sx, sy, sw, sh, ox2, oy2, dw2, dh2);\n // Thin box outline on crop\n ctx2.strokeStyle = '#f90';\n ctx2.lineWidth = 1.5;\n const rx2 = ox2 + pad * scale2, ry2 = oy2 + pad * scale2;\n ctx2.strokeRect(rx2, ry2, bw * scale2, bh * scale2);\n }\n}\n\n// Cache loaded images so lightbox reuses them\nconst imgCache = new Map();\n\nfunction openLightbox(r) {\n const img = imgCache.get(r.id);\n if (!img) return;\n\n const lb = document.getElementById('lightbox');\n const lbCanvas = document.getElementById('lightbox-canvas');\n\n // Size canvas to image, capped at viewport\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.8;\n const [iw, ih] = r.inputDimensions;\n const scale = Math.min(maxW / iw, maxH / ih, 1);\n lbCanvas.width = Math.round(iw * scale);\n lbCanvas.height = Math.round(ih * scale);\n\n const ctx = lbCanvas.getContext('2d');\n ctx.drawImage(img, 0, 0, lbCanvas.width, lbCanvas.height);\n\n // Draw all bounding boxes for this detection (primary + others in same event if available)\n const boxes = r.allDetections || [{ boundingBox: r.boundingBox, detectedClass: r.detectedClass, score: r.score }];\n const colors = ['#f90', '#4af', '#f44', '#4f4', '#f4f', '#ff4'];\n boxes.forEach((d, i) => {\n const [bx, by, bw, bh] = d.boundingBox;\n const color = colors[i % colors.length];\n const rx = bx * scale, ry = by * scale, rw = bw * scale, rh = bh * scale;\n ctx.strokeStyle = color;\n ctx.lineWidth = 2;\n ctx.strokeRect(rx, ry, rw, rh);\n const label = d.detectedClass + ' ' + Math.round((d.score || 0) * 100) + '%';\n const textW = ctx.measureText(label).width + 8;\n ctx.fillStyle = color;\n ctx.globalAlpha = 0.85;\n ctx.fillRect(rx, Math.max(0, ry - 20), textW, 20);\n ctx.globalAlpha = 1;\n ctx.fillStyle = '#000';\n ctx.font = 'bold 12px sans-serif';\n ctx.fillText(label, rx + 4, Math.max(14, ry - 4));\n });\n\n document.getElementById('lightbox-meta').textContent =\n r.cameraName + ' · ' + new Date(r.timestamp).toLocaleString() + ' · ' + iw + '×' + ih;\n lb.classList.add('open');\n document.addEventListener('keydown', lbKeyHandler);\n}\n\nfunction openLightboxImg(img, cameraName, timestamp) {\n const lb = document.getElementById('lightbox');\n const lbCanvas = document.getElementById('lightbox-canvas');\n const maxW = window.innerWidth * 0.9;\n const maxH = window.innerHeight * 0.8;\n const scale = Math.min(maxW / img.naturalWidth, maxH / img.naturalHeight, 1);\n lbCanvas.width = Math.round(img.naturalWidth * scale);\n lbCanvas.height = Math.round(img.naturalHeight * scale);\n const ctx = lbCanvas.getContext('2d');\n ctx.drawImage(img, 0, 0, lbCanvas.width, lbCanvas.height);\n document.getElementById('lightbox-meta').textContent =\n cameraName + ' · ' + new Date(timestamp).toLocaleString();\n lb.classList.add('open');\n document.addEventListener('keydown', lbKeyHandler);\n}\n\nfunction closeLightbox() {\n document.getElementById('lightbox').classList.remove('open');\n document.removeEventListener('keydown', lbKeyHandler);\n}\n\nfunction lbKeyHandler(e) {\n if (e.key === 'Escape') closeLightbox();\n}\n\nfunction showTab(name) {\n const names = ['review', 'browse', 'labeled', 'stats', 'export'];\n document.querySelectorAll('.tab').forEach((t, i) => {\n t.classList.toggle('active', names[i] === name);\n });\n document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));\n document.getElementById('tab-' + name).classList.add('active');\n if (name === 'stats') loadStats();\n if (name === 'export') loadExportInfo();\n if (name === 'labeled') loadLabeled(0);\n if (name === 'browse') initBrowse();\n}\n\nasync function initBrowse() {\n const sel = document.getElementById('browse-camera');\n if (sel.options.length > 1) return; // already loaded\n try {\n const res = await fetch(BASE + '/api/cameras');\n const cameras = await res.json();\n for (const cam of cameras) {\n const opt = document.createElement('option');\n opt.value = cam.id;\n opt.textContent = cam.name;\n sel.appendChild(opt);\n }\n if (cameras.length === 1) sel.value = cameras[0].id;\n } catch(e) {\n document.getElementById('browse-status').textContent = 'Error loading cameras';\n }\n}\n\n// Store browse events for addEvent closure\nlet browseEvents = [];\n\nasync function loadBrowse() {\n const cameraId = document.getElementById('browse-camera').value;\n const hours = document.getElementById('browse-hours').value;\n const limit = document.getElementById('browse-limit').value;\n const status = document.getElementById('browse-status');\n const list = document.getElementById('browse-list');\n\n if (!cameraId) { status.textContent = 'Select a camera first'; return; }\n\n status.textContent = 'Loading…';\n list.innerHTML = '';\n browseEvents = [];\n\n try {\n const res = await fetch(BASE + '/api/browse?cameraId=' + cameraId + '&hours=' + hours + '&limit=' + limit);\n const events = await res.json();\n\n if (events.error) { status.textContent = 'Error: ' + events.error; return; }\n if (!events.length) {\n status.textContent = 'No detection events found.';\n list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🔍</div><div>No ObjectDetector events in this time range.</div></div>';\n return;\n }\n\n browseEvents = events;\n status.textContent = events.length + ' events';\n\n list.innerHTML = events.map((ev, i) => {\n const date = new Date(ev.timestamp).toLocaleString();\n const dets = ev.detections || [];\n const allClasses = [...new Set(dets.map(d => d.className))].join(', ');\n return \\`\n <div class=\"detection\" id=\"bev-\\${i}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"bcanvas-full-\\${i}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"bcanvas-crop-\\${i}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${ev.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${allClasses}</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">Add to dataset as:</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"addBrowseEvent(\\${i},'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"addBrowseEvent(\\${i},'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"addBrowseEvent(\\${i},'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"addBrowseEvent(\\${i},'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"addBrowseEvent(\\${i},'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"addBrowseEvent(\\${i},'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"addBrowseEvent(\\${i},'discard')\">🗑 Skip</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Load thumbnails for each event\n for (let i = 0; i < events.length; i++) {\n loadBrowseImage(i, events[i]);\n }\n\n } catch(e) {\n status.textContent = 'Error: ' + e.message;\n }\n}\n\nfunction loadBrowseImage(i, ev) {\n fetch(BASE + '/api/browse-img', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ cameraId: ev.cameraId, thumbnailId: ev.thumbnailId }),\n })\n .then(r => r.ok ? r.blob() : null)\n .then(blob => {\n if (!blob) return;\n const url = URL.createObjectURL(blob);\n const img = new Image();\n img.onload = () => {\n imgCache.set('browse-' + i, img);\n const fullCanvas = document.getElementById('bcanvas-full-' + i);\n const cropCanvas = document.getElementById('bcanvas-crop-' + i);\n const iw = img.naturalWidth, ih = img.naturalHeight;\n // No bounding box for clip thumbnails — just draw the full image\n if (fullCanvas) {\n const ctx = fullCanvas.getContext('2d');\n const cw = fullCanvas.width, ch = fullCanvas.height;\n const scale = Math.min(cw / iw, ch / ih);\n const dw = iw * scale, dh = ih * scale;\n ctx.fillStyle = '#111'; ctx.fillRect(0, 0, cw, ch);\n ctx.drawImage(img, (cw-dw)/2, (ch-dh)/2, dw, dh);\n // Label classes\n const labels = (ev.detectionClasses || []).join(', ');\n ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillRect(0, ch-20, cw, 20);\n ctx.fillStyle = '#f90'; ctx.font = 'bold 11px sans-serif';\n ctx.fillText(labels, 4, ch-5);\n fullCanvas.onclick = () => openLightboxImg(img, ev.cameraName, ev.timestamp);\n }\n // Hide crop panel — no bounding box available\n const cropPanel = document.getElementById('bcanvas-crop-' + i)?.closest('.img-panel');\n if (cropPanel) cropPanel.style.display = 'none';\n URL.revokeObjectURL(url);\n };\n img.src = url;\n }).catch(() => {});\n}\n\nasync function addBrowseEvent(i, label) {\n const ev = browseEvents[i];\n if (!ev) return;\n const el = document.getElementById('bev-' + i);\n if (el) { el.style.opacity = '0.4'; el.querySelectorAll('button').forEach(b => b.disabled = true); }\n\n if (label !== 'discard') {\n try {\n const res = await fetch(BASE + '/api/add-event', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n cameraId: ev.cameraId,\n cameraName: ev.cameraName,\n thumbnailId: ev.thumbnailId,\n timestamp: ev.timestamp,\n detectedClass: (ev.detectionClasses || [])[0] || 'unknown',\n boundingBox: null,\n inputDimensions: null,\n label,\n }),\n });\n const data = await res.json();\n if (data.error) { toast('Error: ' + data.error, '#633'); if (el) el.style.opacity = '1'; el?.querySelectorAll('button').forEach(b => b.disabled = false); return; }\n toast('Added: ' + label, '#1a6');\n } catch(e) {\n toast('Failed: ' + e.message, '#633');\n if (el) el.style.opacity = '1';\n el?.querySelectorAll('button').forEach(b => b.disabled = false);\n return;\n }\n } else {\n toast('Skipped', '#555');\n }\n\n // Remove from list after short delay\n setTimeout(() => { if (el) el.remove(); }, 400);\n\n // Update stats\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n}\n\nconst LABEL_COLORS = { person:'#4d9', animal:'#d85', face:'#6be', vehicle:'#99d', plate:'#cc9', package:'#fc9', discard:'#a44' };\n\nasync function loadLabeled(page) {\n const list = document.getElementById('labeled-list');\n list.innerHTML = '<div class=\"empty\"><div style=\"color:#888\">Loading…</div></div>';\n try {\n const res = await fetch(BASE + '/api/labeled?page=' + page);\n const data = await res.json();\n const { items, total, pageSize } = data;\n const totalPages = Math.ceil(total / pageSize);\n\n if (!items.length) {\n list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🏷️</div><div>No labeled detections yet.</div></div>';\n return;\n }\n\n const pagerHtml = totalPages > 1 ? \\`\n <div style=\"display:flex;align-items:center;gap:12px;padding:14px 16px;border-top:1px solid #222;font-size:13px;color:#888;\">\n \\${page > 0 ? \\`<button class=\"label-btn\" onclick=\"loadLabeled(\\${page-1})\">← Prev</button>\\` : ''}\n <span>Page \\${page+1} of \\${totalPages} · \\${total} total</span>\n \\${page < totalPages-1 ? \\`<button class=\"label-btn\" onclick=\"loadLabeled(\\${page+1})\">Next →</button>\\` : ''}\n </div>\\` : '';\n\n list.innerHTML = items.map(r => {\n const date = new Date(r.timestamp).toLocaleString();\n const score = Math.round(r.score * 100);\n const labelColor = LABEL_COLORS[r.label] || '#aaa';\n return \\`\n <div class=\"detection\" id=\"ldet-\\${r.id}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"lcanvas-full-\\${r.id}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"lcanvas-crop-\\${r.id}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${r.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${r.detectedClass} \\${score}%</div>\n <div style=\"display:inline-block;background:#1a1a1a;border:1px solid \\${labelColor};color:\\${labelColor};font-size:11px;padding:2px 8px;border-radius:4px;\">✓ \\${r.label}</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">Change label:</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"relabel('\\${r.id}', 'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"relabel('\\${r.id}', 'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"relabel('\\${r.id}', 'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"relabel('\\${r.id}', 'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"relabel('\\${r.id}', 'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"relabel('\\${r.id}', 'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"relabel('\\${r.id}', 'discard')\">🗑 Discard</button>\n </div>\n </div>\n </div>\\`;\n }).join('') + pagerHtml;\n\n // Draw bounding boxes\n for (const r of items) {\n const img = new Image();\n img.onload = () => {\n imgCache.set(r.id, img);\n // Reuse drawDetection with the labeled canvases\n const origFull = document.getElementById('canvas-full-' + r.id);\n const origCrop = document.getElementById('canvas-crop-' + r.id);\n // Temporarily point to labeled canvases\n const fakeFull = document.getElementById('lcanvas-full-' + r.id);\n const fakeCrop = document.getElementById('lcanvas-crop-' + r.id);\n if (fakeFull) fakeFull.id = 'canvas-full-' + r.id;\n if (fakeCrop) fakeCrop.id = 'canvas-crop-' + r.id;\n drawDetection(img, r);\n if (fakeFull) fakeFull.id = 'lcanvas-full-' + r.id;\n if (fakeCrop) fakeCrop.id = 'lcanvas-crop-' + r.id;\n if (fakeFull) fakeFull.onclick = () => openLightbox(r);\n if (fakeCrop) fakeCrop.onclick = () => openLightbox(r);\n };\n img.src = BASE + '/img/' + r.id;\n }\n } catch(e) {\n list.innerHTML = '<div class=\"empty\"><div style=\"color:#a44\">Error: ' + e.message + '</div></div>';\n }\n}\n\nasync function relabel(id, labelVal) {\n await fetch(BASE + '/api/label', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, label: labelVal }),\n });\n const el = document.getElementById('ldet-' + id);\n if (labelVal === 'discard') {\n if (el) el.remove();\n toast('Discarded', '#633');\n } else {\n // Update the label badge in place\n const badge = el && el.querySelector('[style*=\"✓\"]');\n const labelColor = LABEL_COLORS[labelVal] || '#aaa';\n if (el) {\n const badges = el.querySelectorAll('.detection-meta > div');\n badges.forEach(b => { if (b.textContent.startsWith('✓')) b.remove(); });\n const meta = el.querySelector('.detection-meta');\n const newBadge = document.createElement('div');\n newBadge.style.cssText = \\`display:inline-block;background:#1a1a1a;border:1px solid \\${labelColor};color:\\${labelColor};font-size:11px;padding:2px 8px;border-radius:4px;\\`;\n newBadge.textContent = '✓ ' + labelVal;\n meta.appendChild(newBadge);\n }\n toast('Re-labeled: ' + labelVal, '#1a6');\n }\n // Refresh stats\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-pending').textContent = stats.pending;\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n}\n\nfunction toast(msg, color='#1a3') {\n const el = document.getElementById('toast');\n el.textContent = msg;\n el.style.background = color;\n el.classList.add('show');\n setTimeout(() => el.classList.remove('show'), 2500);\n}\n\nasync function loadPending() {\n try {\n const res = await fetch(BASE + '/api/pending');\n pending = await res.json();\n\n const statsRes = await fetch(BASE + '/api/stats');\n const stats = await statsRes.json();\n document.getElementById('stat-pending').textContent = stats.pending;\n document.getElementById('stat-labeled').textContent = stats.labeled;\n document.getElementById('stat-total').textContent = stats.total;\n\n const list = document.getElementById('detections-list');\n if (!pending.length) {\n 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>';\n return;\n }\n\n list.innerHTML = pending.map(r => {\n const date = new Date(r.timestamp).toLocaleString();\n const score = Math.round(r.score * 100);\n const bb = r.boundingBox;\n const dim = r.inputDimensions;\n return \\`\n <div class=\"detection\" id=\"det-\\${r.id}\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"canvas-full-\\${r.id}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\">\n <div class=\"img-label\">Crop</div>\n <canvas id=\"canvas-crop-\\${r.id}\" class=\"det-canvas\" width=\"160\" height=\"160\"></canvas>\n </div>\n </div>\n <div class=\"detection-info\">\n <div class=\"detection-meta\">\n <div><strong>\\${r.cameraName}</strong></div>\n <div>\\${date}</div>\n <div class=\"det-class-badge\">\\${r.detectedClass} \\${score}%</div>\n </div>\n <div style=\"font-size:12px;color:#888;\">What is this actually?</div>\n <div class=\"label-buttons\">\n <button class=\"label-btn person\" onclick=\"label('\\${r.id}', 'person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"label('\\${r.id}', 'animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"label('\\${r.id}', 'face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"label('\\${r.id}', 'vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"label('\\${r.id}', 'plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"label('\\${r.id}', 'package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"label('\\${r.id}', 'discard')\">🗑 Discard</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Draw bounding boxes and crops onto canvases after DOM is ready\n for (const r of pending) {\n const img = new Image();\n img.onload = () => {\n imgCache.set(r.id, img);\n drawDetection(img, r);\n // Wire up click to open lightbox\n const fullCanvas = document.getElementById('canvas-full-' + r.id);\n const cropCanvas = document.getElementById('canvas-crop-' + r.id);\n if (fullCanvas) fullCanvas.onclick = () => openLightbox(r);\n if (cropCanvas) cropCanvas.onclick = () => openLightbox(r);\n };\n img.onerror = () => {\n const c = document.getElementById('canvas-full-' + r.id);\n if (c) c.parentElement.innerHTML = '<div style=\"padding:10px;color:#555;font-size:11px\">No image</div>';\n };\n img.src = BASE + '/img/' + r.id;\n } } catch(e) {\n console.error('loadPending error', e);\n const list = document.getElementById('detections-list');\n if (list) list.innerHTML = '<div class=\"empty\"><div style=\"color:#a44\">Error loading captures: ' + e.message + '</div></div>';\n }\n}\n\nasync function label(id, labelVal) {\n const el = document.getElementById('det-' + id);\n if (el) {\n el.classList.add('labeled');\n const btns = el.querySelectorAll('.label-btn');\n btns.forEach(b => b.disabled = true);\n }\n await fetch(BASE + '/api/label', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, label: labelVal }),\n });\n toast(labelVal === 'discard' ? 'Discarded' : 'Labeled: ' + labelVal, labelVal === 'discard' ? '#633' : '#1a6');\n setTimeout(() => {\n if (el) el.remove();\n loadPending();\n }, 600);\n}\n\nasync function loadStats() {\n const res = await fetch(BASE + '/api/stats');\n const stats = await res.json();\n\n const renderBreakdown = (obj, container) => {\n const el = document.getElementById(container);\n const entries = Object.entries(obj).sort((a, b) => b[1] - a[1]);\n el.innerHTML = entries.length\n ? entries.map(([k, v]) => \\`<div class=\"breakdown-item\"><div class=\"label\">\\${k}</div><div class=\"value\">\\${v}</div></div>\\`).join('')\n : '<div style=\"color:#555;font-size:13px;\">None yet</div>';\n };\n\n renderBreakdown(stats.byDetectedClass, 'stats-detected');\n renderBreakdown(stats.byLabel, 'stats-label');\n renderBreakdown(stats.byCamera, 'stats-camera');\n}\n\nasync function loadExportInfo() {\n const res = await fetch(BASE + '/api/stats');\n const stats = await res.json();\n document.getElementById('export-stats').textContent =\n \\`\\${stats.labeled} labeled samples ready for export across \\${Object.keys(stats.byCamera).length} camera(s).\\`;\n}\n\nasync function exportDataset() {\n const btn = document.getElementById('export-btn');\n const status = document.getElementById('export-status');\n btn.disabled = true;\n status.textContent = 'Fetching data…';\n\n try {\n const res = await fetch(BASE + '/api/export');\n if (!res.ok) { status.textContent = 'Nothing to export yet.'; btn.disabled = false; return; }\n const data = await res.json();\n if (data.error) { status.textContent = data.error; btn.disabled = false; return; }\n\n status.textContent = 'Building zip…';\n\n const zip = new JSZip();\n for (const f of data.files) {\n if (f.encoding === 'base64') {\n zip.file(f.filename, f.content, { base64: true });\n } else {\n zip.file(f.filename, f.content);\n }\n }\n\n const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'scrypted_dataset_' + new Date().toISOString().slice(0,10) + '.zip';\n a.click();\n URL.revokeObjectURL(url);\n status.textContent = \\`Downloaded \\${data.count} samples.\\`;\n toast('Dataset downloaded!');\n } catch (e) {\n status.textContent = 'Export failed: ' + e.message;\n }\n btn.disabled = false;\n}\n\n// Initial load\nloadPending();\n// Auto-refresh pending every 30s\nsetInterval(loadPending, 30_000);\n</script>\n</body>\n</html>`;\n }\n}\n\nexport default DetectionTrainer;\n","module.exports = require(\"fs\");","module.exports = require(\"module\");","module.exports = require(\"path\");","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(927);\n"],"names":["__createBinding","this","Object","create","o","m","k","k2","undefined","desc","getOwnPropertyDescriptor","__esModule","writable","configurable","enumerable","get","defineProperty","__exportStar","exports","p","prototype","hasOwnProperty","call","value","sdk","MixinDeviceBase","ScryptedDeviceBase","index_1","DeviceBase","constructor","nativeId","super","storage","_storage","deviceManager","getDeviceStorage","log","_log","getDeviceLogger","console","_console","getDeviceConsole","createMediaObject","data","mimeType","mediaManager","sourceId","id","getMediaObjectConsole","mediaObject","getMixinConsole","_lazyLoadDeviceState","_deviceState","getDeviceState","onDeviceEvent","eventInterface","eventData","options","_listeners","Set","mixinDevice","mixinDeviceInterfaces","mixinStorageSuffix","mixinDeviceState","systemManager","getDeviceById","mixinProviderNativeId","__rpcproxy_traps_all_properties","createDeviceState","setState","mixinStorageKey","getMixinStorage","onMixinEvent","manageListener","listener","add","release","l","removeListener","_createGetState","state","_createSetState","warn","field","values","ScryptedInterfaceProperty","set","loaded","process","env","SCRYPTED_SDK_ES_MODULE","SCRYPTED_SDK_MODULE","cjsModule","SCRYPTED_SDK_CJS_MODULE","sdkModule","assign","getScryptedStatic","e","runtimeAPI","pluginRuntimeAPI","endpointManager","pluginHostAPI","setScryptedInterfaceDescriptors","TYPES_VERSION","ScryptedInterfaceDescriptors","catch","error","webpackEmptyContext","req","Error","code","keys","resolve","module","ScryptedMimeTypes","ScryptedInterface","MediaPlayerState","SecuritySystemObstruction","SecuritySystemMode","AirQuality","AirPurifierMode","AirPurifierStatus","ChargeState","LockState","PanTiltZoomMovement","ThermostatMode","TemperatureUnit","FanMode","HumidityMode","ScryptedDeviceType","ScryptedInterfaceMethod","CAPTURE_CLASSES","RATE_OPTIONS","RATE_MS","Infinity","DetectionTrainer","lastCapture","Map","captures","listeners","imgDir","join","SCRYPTED_PLUGIN_VOLUME","mkdirSync","recursive","loadState","registerListeners","raw","getItem","arr","JSON","parse","r","size","removeItem","saveCaptures","setItem","stringify","imgPath","saveImage","buf","writeFileSync","loadImage","existsSync","readFileSync","deleteCapture","unlinkSync","delete","getSettings","cameras","getSystemState","map","filter","d","type","Camera","Doorbell","interfaces","includes","ObjectDetector","uiUrl","getLocalEndpoint","public","settings","key","title","description","c","reviewed","length","label","readonly","cam","push","name","group","choices","putSetting","startsWith","remove","rateKey","rateLabel","listen","async","source","details","onDetection","cameraId","cameraName","rateLimitMs","detections","inputDimensions","now","Date","targets","className","has","toLowerCase","boundingBox","best","sort","a","b","score","oldest","timestamp","jpeg","detectionId","mo","getDetectionInput","convertMediaObjectToBuffer","Math","random","toString","slice","record","detectedClass","round","onRequest","request","response","path","URL","url","pathname","replace","rootPath","body","rawBody","Buffer","isBuffer","String","thumbnailId","send","getVideoClipThumbnail","headers","img","ok","params","searchParams","hours","parseInt","endTime","startTime","clips","getVideoClips","limit","events","detectionClasses","clipId","message","pending","page","pageSize","labeled","items","total","all","stats","byLabel","byCamera","byDetectedClass","classMap","person","animal","face","vehicle","plate","package","discard","dataset","fname","filename","content","encoding","x","y","w","h","imgW","imgH","cx","cy","nw","nh","labelLine","toFixed","yaml","files","count","renderUI","require","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","obj","prop","__webpack_exports__"],"sourceRoot":""}
|