scrypted-detection-trainer 0.1.10 → 0.1.11

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.
@@ -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 (fetch on demand from camera)\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 detectionId = params.get('detectionId')?.replace(/[^a-zA-Z0-9_\\-]/g, '');\n if (!cameraId || !detectionId) return response.send('Missing params', { code: 400 });\n try {\n const cam = systemManager.getDeviceById(cameraId) as unknown as ObjectDetector;\n const mo = await cam.getDetectionInput(detectionId);\n const jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n return response.send(jpeg, { headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'max-age=60' } });\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 events = await cam.getRecordedEvents({ startTime, endTime });\n\n // Filter to ObjectDetector events only and limit to 100\n const detectionEvents = (events || [])\n .filter((e: any) => e.details?.eventInterface === 'ObjectDetector' && e.data?.detections?.length)\n .slice(0, 100)\n .map((e: any) => ({\n detectionId: e.data?.detectionId,\n timestamp: e.details?.eventTime || e.data?.timestamp,\n detections: (e.data?.detections || []).map((d: any) => ({\n className: d.className,\n score: d.score,\n boundingBox: d.boundingBox,\n })),\n inputDimensions: e.data?.inputDimensions,\n cameraId,\n cameraName: cam.name,\n }))\n .filter((e: any) => e.detectionId && e.inputDimensions);\n\n return response.send(JSON.stringify(detectionEvents), { 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, detectionId, timestamp, detectedClass, score, boundingBox, inputDimensions, label } = body;\n\n if (!label || label === 'discard') return response.send(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });\n\n // Try to get the image\n let jpeg: Buffer | undefined;\n try {\n const cam = systemManager.getDeviceById(cameraId) as unknown as ObjectDetector;\n const mo = await cam.getDetectionInput(detectionId);\n jpeg = await mediaManager.convertMediaObjectToBuffer(mo, 'image/jpeg');\n } catch (e) {\n this.console.warn(`Could not get image for browse event ${detectionId}:`, 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 const record: CaptureRecord = {\n id, cameraId, cameraName, timestamp, detectedClass, score,\n boundingBox, inputDimensions, detectionId, 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 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\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\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) { status.textContent = 'No detection events found.'; list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🔍</div><div>No ObjectDetector events in this time range.</div></div>'; return; }\n\n status.textContent = events.length + ' events found';\n\n list.innerHTML = events.map((ev, i) => {\n const date = new Date(ev.timestamp).toLocaleString();\n const dets = ev.detections || [];\n const primary = dets[0] || {};\n const score = Math.round((primary.score || 0) * 100);\n const allClasses = dets.map(d => d.className + ' ' + Math.round((d.score||0)*100) + '%').join(', ');\n return \\`\n <div class=\"detection\" id=\"bev-\\${i}\" style=\"opacity:1;transition:opacity .3s\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"bcanvas-\\${i}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\" id=\"bcrop-panel-\\${i}\">\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\" id=\"blabels-\\${i}\">\n <button class=\"label-btn person\" onclick=\"addEvent(\\${i})('person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"addEvent(\\${i})('animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"addEvent(\\${i})('face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"addEvent(\\${i})('vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"addEvent(\\${i})('plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"addEvent(\\${i})('package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"addEvent(\\${i})('discard')\">🗑 Skip</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Load images for each event\n for (let i = 0; i < events.length; i++) {\n const ev = events[i];\n loadBrowseImage(i, ev);\n }\n\n } catch(e) {\n status.textContent = 'Error: ' + e.message;\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 const primary = (ev.detections || [])[0];\n if (!primary?.boundingBox) return;\n // Request image via the img endpoint using detectionId as the key\n // We store a browse-prefixed image server-side only after adding — for preview\n // use a placeholder fetch to trigger server-side caching\n fetch(BASE + '/api/browse-img?cameraId=' + ev.cameraId + '&detectionId=' + ev.detectionId)\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 // Draw on full canvas\n const fullCanvas = document.getElementById('bcanvas-full-' + i);\n const cropCanvas = document.getElementById('bcanvas-crop-' + i);\n if (fullCanvas) fullCanvas.id = 'canvas-full-browse' + i;\n if (cropCanvas) cropCanvas.id = 'canvas-crop-browse' + i;\n const fakeR = { ...ev, id: 'browse' + i, boundingBox: primary.boundingBox, detectedClass: primary.className, score: primary.score };\n drawDetection(img, fakeR);\n if (fullCanvas) { fullCanvas.id = 'bcanvas-full-' + i; fullCanvas.onclick = () => openLightbox(fakeR); }\n if (cropCanvas) { cropCanvas.id = 'bcanvas-crop-' + i; cropCanvas.onclick = () => openLightbox(fakeR); }\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 const primary = (ev.detections || [])[0];\n if (!primary) return;\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 detectionId: ev.detectionId,\n timestamp: ev.timestamp,\n detectedClass: primary.className,\n score: primary.score,\n boundingBox: primary.boundingBox,\n inputDimensions: ev.inputDimensions,\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","send","headers","img","body","rawBody","Buffer","isBuffer","String","ok","hours","parseInt","endTime","startTime","detectionEvents","getRecordedEvents","eventTime","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') {\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\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\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) { status.textContent = 'No detection events found.'; list.innerHTML = '<div class=\"empty\"><div class=\"icon\">🔍</div><div>No ObjectDetector events in this time range.</div></div>'; return; }\n\n status.textContent = events.length + ' events found';\n\n list.innerHTML = events.map((ev, i) => {\n const date = new Date(ev.timestamp).toLocaleString();\n const dets = ev.detections || [];\n const primary = dets[0] || {};\n const score = Math.round((primary.score || 0) * 100);\n const allClasses = dets.map(d => d.className + ' ' + Math.round((d.score||0)*100) + '%').join(', ');\n return \\`\n <div class=\"detection\" id=\"bev-\\${i}\" style=\"opacity:1;transition:opacity .3s\">\n <div class=\"detection-imgs\">\n <div class=\"img-panel\">\n <div class=\"img-label\">Full frame</div>\n <canvas id=\"bcanvas-\\${i}\" class=\"det-canvas\" width=\"240\" height=\"160\"></canvas>\n </div>\n <div class=\"img-panel\" id=\"bcrop-panel-\\${i}\">\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\" id=\"blabels-\\${i}\">\n <button class=\"label-btn person\" onclick=\"addEvent(\\${i})('person')\">👤 Person</button>\n <button class=\"label-btn animal\" onclick=\"addEvent(\\${i})('animal')\">🐾 Animal</button>\n <button class=\"label-btn face\" onclick=\"addEvent(\\${i})('face')\">😀 Face</button>\n <button class=\"label-btn vehicle\" onclick=\"addEvent(\\${i})('vehicle')\">🚗 Vehicle</button>\n <button class=\"label-btn\" onclick=\"addEvent(\\${i})('plate')\">🔢 Plate</button>\n <button class=\"label-btn\" onclick=\"addEvent(\\${i})('package')\">📦 Package</button>\n <button class=\"label-btn discard\" onclick=\"addEvent(\\${i})('discard')\">🗑 Skip</button>\n </div>\n </div>\n </div>\\`;\n }).join('');\n\n // Load images for each event\n for (let i = 0; i < events.length; i++) {\n const ev = events[i];\n loadBrowseImage(i, ev);\n }\n\n } catch(e) {\n status.textContent = 'Error: ' + e.message;\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') as HTMLElement;\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":""}