From 082c75ea4bdd1fa8c957a0e3b3e6e5a2135b5192 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 19:12:30 -0800 Subject: [PATCH 1/9] unifi-protect: bug fixes for non-cameras --- plugins/unifi-protect/package-lock.json | 4 ++-- plugins/unifi-protect/package.json | 2 +- plugins/unifi-protect/src/lock.ts | 8 ++++++-- plugins/unifi-protect/src/main.ts | 8 ++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/unifi-protect/package-lock.json b/plugins/unifi-protect/package-lock.json index 0d5d6db89..a74f893bf 100644 --- a/plugins/unifi-protect/package-lock.json +++ b/plugins/unifi-protect/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/unifi-protect", - "version": "0.0.84", + "version": "0.0.88", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/unifi-protect", - "version": "0.0.84", + "version": "0.0.88", "license": "Apache", "dependencies": { "@koush/unifi-protect": "file:../../external/unifi-protect", diff --git a/plugins/unifi-protect/package.json b/plugins/unifi-protect/package.json index 67ceaf1c5..4476d09ca 100644 --- a/plugins/unifi-protect/package.json +++ b/plugins/unifi-protect/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/unifi-protect", - "version": "0.0.84", + "version": "0.0.88", "description": "Unifi Protect Plugin for Scrypted", "author": "Scrypted", "license": "Apache", diff --git a/plugins/unifi-protect/src/lock.ts b/plugins/unifi-protect/src/lock.ts index 1e12f71ef..fd2b23e5a 100644 --- a/plugins/unifi-protect/src/lock.ts +++ b/plugins/unifi-protect/src/lock.ts @@ -10,11 +10,15 @@ export class UnifiLock extends ScryptedDeviceBase implements Lock { } async lock(): Promise { - await this.protect.loginFetch(this.protect.api.doorlocksUrl() + `/${this.nativeId}/close`); + await this.protect.loginFetch(this.protect.api.doorlocksUrl() + `/${this.nativeId}/close`, { + method: 'POST', + }); } async unlock(): Promise { - await this.protect.loginFetch(this.protect.api.doorlocksUrl() + `/${this.nativeId}/open`); + await this.protect.loginFetch(this.protect.api.doorlocksUrl() + `/${this.nativeId}/open`, { + method: 'POST', + }); } findLock() { diff --git a/plugins/unifi-protect/src/main.ts b/plugins/unifi-protect/src/main.ts index 7d653ff3b..a2f4dd65e 100644 --- a/plugins/unifi-protect/src/main.ts +++ b/plugins/unifi-protect/src/main.ts @@ -30,9 +30,6 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device } handleUpdatePacket(packet: any): void { - if (packet.action?.modelKey !== "camera") { - return; - } if (packet.action.action !== "update") { return; } @@ -42,6 +39,7 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device const device = this.api.cameras?.find(c => c.id === packet.action.id) || this.api.lights?.find(c => c.id === packet.action.id) + || this.api.doorlocks?.find(c => c.id === packet.action.id) || this.api.sensors?.find(c => c.id === packet.action.id); if (!device) { @@ -69,6 +67,8 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device listener = (event: Buffer) => { const updatePacket = ProtectApiUpdates.decodeUpdatePacket(this.console, event); + this.console.log('update', updatePacket); + this.handleUpdatePacket(updatePacket); if (!updatePacket) { @@ -270,7 +270,7 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device } if (!this.api) { - this.api = new ProtectApi(ip, username, password, this.console); + this.api = new ProtectApi(ip, username, password); } try { From 227ed1de75bd5de741682ffee62e67902887ab56 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 19:12:59 -0800 Subject: [PATCH 2/9] homekit: add more supported sensors --- plugins/homekit/package-lock.json | 4 ++-- plugins/homekit/package.json | 2 +- plugins/homekit/src/types/sensor.ts | 12 +++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/homekit/package-lock.json b/plugins/homekit/package-lock.json index be30bf7ca..158064204 100644 --- a/plugins/homekit/package-lock.json +++ b/plugins/homekit/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/homekit", - "version": "0.0.173", + "version": "0.0.174", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/homekit", - "version": "0.0.173", + "version": "0.0.174", "dependencies": { "hap-nodejs": "file:../../external/HAP-NodeJS", "lodash": "^4.17.21", diff --git a/plugins/homekit/package.json b/plugins/homekit/package.json index 68e4ac8a1..4c46bf227 100644 --- a/plugins/homekit/package.json +++ b/plugins/homekit/package.json @@ -40,5 +40,5 @@ "@types/qrcode": "^1.4.1", "@types/url-parse": "^1.4.3" }, - "version": "0.0.173" + "version": "0.0.174" } diff --git a/plugins/homekit/src/types/sensor.ts b/plugins/homekit/src/types/sensor.ts index 39e5806c6..27b225551 100644 --- a/plugins/homekit/src/types/sensor.ts +++ b/plugins/homekit/src/types/sensor.ts @@ -1,5 +1,5 @@ -import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer } from '@scrypted/sdk' +import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer, HumiditySensor } from '@scrypted/sdk' import { addSupportedType, bindCharacteristic, DummyDevice } from '../common' import { Characteristic, Service } from '../hap'; import { makeAccessory } from './common'; @@ -9,20 +9,17 @@ addSupportedType({ probe(device: DummyDevice) { return device.interfaces.includes(ScryptedInterface.Thermometer) || device.interfaces.includes(ScryptedInterface.BinarySensor) || device.interfaces.includes(ScryptedInterface.MotionSensor); }, - getAccessory: async (device: ScryptedDevice & BinarySensor & MotionSensor & Thermometer) => { + getAccessory: async (device: ScryptedDevice & BinarySensor & MotionSensor & Thermometer & HumiditySensor) => { const accessory = makeAccessory(device); if (device.interfaces.includes(ScryptedInterface.BinarySensor)) { const contactSensorService = accessory.addService(Service.ContactSensor, device.name); - contactSensorService.getCharacteristic(Characteristic.ContactSensorState) - bindCharacteristic(device, ScryptedInterface.BinarySensor, contactSensorService, Characteristic.ContactSensorState, () => !!device.binaryState); } if (device.interfaces.includes(ScryptedInterface.MotionSensor)) { const motionSensorService = accessory.addService(Service.MotionSensor, device.name); - bindCharacteristic(device, ScryptedInterface.MotionSensor, motionSensorService, Characteristic.MotionDetected, () => !!device.motionDetected, true); } @@ -31,7 +28,12 @@ addSupportedType({ const service = accessory.addService(Service.TemperatureSensor, device.name); bindCharacteristic(device, ScryptedInterface.Thermometer, service, Characteristic.CurrentTemperature, () => device.temperature || 0); + } + if (device.interfaces.includes(ScryptedInterface.HumiditySensor)) { + const service = accessory.addService(Service.HumiditySensor, device.name); + bindCharacteristic(device, ScryptedInterface.HumiditySensor, service, Characteristic.CurrentRelativeHumidity, + () => device.humidity || 0); } // todo: more sensors. From 71ba97b5f6a4ec3094e06194ae92669081b60c84 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 19:52:22 -0800 Subject: [PATCH 3/9] hikvision: DoS fix --- plugins/hikvision/package-lock.json | 4 +- plugins/hikvision/package.json | 2 +- plugins/hikvision/src/hikvision-camera-api.ts | 9 ++-- plugins/hikvision/src/main.ts | 50 +++++++++++++------ 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/plugins/hikvision/package-lock.json b/plugins/hikvision/package-lock.json index f0f2538f1..2a2837d10 100644 --- a/plugins/hikvision/package-lock.json +++ b/plugins/hikvision/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/hikvision", - "version": "0.0.84", + "version": "0.0.85", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/hikvision", - "version": "0.0.84", + "version": "0.0.85", "license": "Apache", "dependencies": { "@koush/axios-digest-auth": "^0.8.5", diff --git a/plugins/hikvision/package.json b/plugins/hikvision/package.json index 903bb2a6e..8216dd8f5 100644 --- a/plugins/hikvision/package.json +++ b/plugins/hikvision/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/hikvision", - "version": "0.0.84", + "version": "0.0.85", "description": "HikVision Plugin for Scrypted", "author": "Scrypted", "license": "Apache", diff --git a/plugins/hikvision/src/hikvision-camera-api.ts b/plugins/hikvision/src/hikvision-camera-api.ts index a6836bca0..ff052f3db 100644 --- a/plugins/hikvision/src/hikvision-camera-api.ts +++ b/plugins/hikvision/src/hikvision-camera-api.ts @@ -52,21 +52,22 @@ export class HikVisionCameraAPI { resolve(deviceModel); } catch (e) { this.console.error('error checking NVR model', e); - resolve("unknown"); + resolve(undefined); } }); } return await this.deviceModel; } - async checkIsOldModel(): Promise { + async checkIsOldModel() { // The old Hikvision DS-7608NI-E2 doesn't support channel capability checks, and the requests cause errors const model = await this.checkDeviceModel(); + if (!model) + return; return !!model?.match(/DS-7608NI-E2/); } - async checkStreamSetup(channel: string): Promise { - const isOld = await this.checkIsOldModel(); + async checkStreamSetup(channel: string, isOld: boolean): Promise { if (isOld) { this.console.error('NVR is old version. Defaulting camera capabilities to H.264/AAC'); return { diff --git a/plugins/hikvision/src/main.ts b/plugins/hikvision/src/main.ts index 5d2d16f14..a61391de5 100644 --- a/plugins/hikvision/src/main.ts +++ b/plugins/hikvision/src/main.ts @@ -2,14 +2,33 @@ import sdk, { MediaObject, Camera, ScryptedInterface } from "@scrypted/sdk"; import { EventEmitter } from "stream"; import { HikVisionCameraAPI } from "./hikvision-camera-api"; import { Destroyable, UrlMediaStreamOptions, RtspProvider, RtspSmartCamera } from "../../rtsp/src/rtsp"; +import { sleep } from "../../../common/src/sleep"; import { HikVisionCameraEvent } from "./hikvision-camera-api"; const { mediaManager } = sdk; class HikVisionCamera extends RtspSmartCamera implements Camera { - hasCheckedCodec = false; channelIds: Promise; client: HikVisionCameraAPI; + // bad hack, but whatever. + codecCheck = (async () => { + while (true) { + try { + const streamSetup = await this.client.checkStreamSetup(this.getRtspChannel(), await this.isOld()); + if (streamSetup.videoCodecType !== 'H.264') { + this.log.a(`This camera is configured for ${streamSetup.videoCodecType} on the main channel. Configuring it it for H.264 is recommended for optimal performance.`); + } + if (!this.isAudioDisabled() && streamSetup.audioCodecType && streamSetup.audioCodecType !== 'AAC') { + this.log.a(`This camera is configured for ${streamSetup.audioCodecType} on the main channel. Configuring it for AAC is recommended for optimal performance.`); + } + break; + } + catch (e) { + await sleep(60000); + } + } + })(); + listenEvents() { let motionTimeout: NodeJS.Timeout; const ret = new EventEmitter() as (EventEmitter & Destroyable); @@ -84,19 +103,6 @@ class HikVisionCamera extends RtspSmartCamera implements Camera { getClient() { if (!this.client) this.client = this.createClient(); - (async () => { - if (this.hasCheckedCodec) - return; - const streamSetup = await this.client.checkStreamSetup(this.getRtspChannel()); - this.hasCheckedCodec = true; - if (streamSetup.videoCodecType !== 'H.264') { - this.log.a(`This camera is configured for ${streamSetup.videoCodecType} on the main channel. Configuring it it for H.264 is recommended for optimal performance.`); - } - if (!this.isAudioDisabled() && streamSetup.audioCodecType && streamSetup.audioCodecType !== 'AAC') { - this.log.a(`This camera is configured for ${streamSetup.audioCodecType} on the main channel. Configuring it for AAC is recommended for optimal performance.`); - } - })(); - return this.client; } @@ -142,11 +148,25 @@ class HikVisionCamera extends RtspSmartCamera implements Camera { return this.storage.getItem('rtspUrlParams') || '?transportmode=unicast'; } + async isOld() { + const client = this.getClient(); + let isOld: boolean; + if (this.storage.getItem('isOld')) { + isOld = this.storage.getItem('isOld') === 'true'; + } + else { + isOld = await client.checkIsOldModel(); + this.storage.setItem('isOld', isOld?.toString()); + } + return isOld; + } + async getConstructedVideoStreamOptions(): Promise { if (!this.channelIds) { const client = this.getClient(); this.channelIds = new Promise(async (resolve, reject) => { - const isOld = await client.checkIsOldModel(); + const isOld = await this.isOld(); + if (isOld) { this.console.error('Old NVR. Defaulting to two camera configuration'); const camNumber = this.getCameraNumber() || '1'; From c57cbe00fa48e570a7fd73916f3bad2557e17ce7 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 19:56:57 -0800 Subject: [PATCH 4/9] homekit: audio sensor --- plugins/homekit/package-lock.json | 4 ++-- plugins/homekit/package.json | 2 +- plugins/homekit/src/types/sensor.ts | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/homekit/package-lock.json b/plugins/homekit/package-lock.json index 158064204..a4a2949a8 100644 --- a/plugins/homekit/package-lock.json +++ b/plugins/homekit/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/homekit", - "version": "0.0.174", + "version": "0.0.175", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/homekit", - "version": "0.0.174", + "version": "0.0.175", "dependencies": { "hap-nodejs": "file:../../external/HAP-NodeJS", "lodash": "^4.17.21", diff --git a/plugins/homekit/package.json b/plugins/homekit/package.json index 4c46bf227..d0c6e4c24 100644 --- a/plugins/homekit/package.json +++ b/plugins/homekit/package.json @@ -40,5 +40,5 @@ "@types/qrcode": "^1.4.1", "@types/url-parse": "^1.4.3" }, - "version": "0.0.174" + "version": "0.0.175" } diff --git a/plugins/homekit/src/types/sensor.ts b/plugins/homekit/src/types/sensor.ts index 27b225551..297b2e06f 100644 --- a/plugins/homekit/src/types/sensor.ts +++ b/plugins/homekit/src/types/sensor.ts @@ -1,5 +1,5 @@ -import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer, HumiditySensor } from '@scrypted/sdk' +import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer, HumiditySensor, AudioSensor } from '@scrypted/sdk' import { addSupportedType, bindCharacteristic, DummyDevice } from '../common' import { Characteristic, Service } from '../hap'; import { makeAccessory } from './common'; @@ -9,7 +9,7 @@ addSupportedType({ probe(device: DummyDevice) { return device.interfaces.includes(ScryptedInterface.Thermometer) || device.interfaces.includes(ScryptedInterface.BinarySensor) || device.interfaces.includes(ScryptedInterface.MotionSensor); }, - getAccessory: async (device: ScryptedDevice & BinarySensor & MotionSensor & Thermometer & HumiditySensor) => { + getAccessory: async (device: ScryptedDevice & AudioSensor & BinarySensor & MotionSensor & Thermometer & HumiditySensor) => { const accessory = makeAccessory(device); if (device.interfaces.includes(ScryptedInterface.BinarySensor)) { @@ -18,6 +18,12 @@ addSupportedType({ () => !!device.binaryState); } + if (device.interfaces.includes(ScryptedInterface.AudioSensor)) { + const service = accessory.addService(Service.ContactSensor, device.name + ' Alarm Sound', 'AudioSensor'); + bindCharacteristic(device, ScryptedInterface.AudioSensor, service, Characteristic.ContactSensorState, + () => !!device.audioDetected); + } + if (device.interfaces.includes(ScryptedInterface.MotionSensor)) { const motionSensorService = accessory.addService(Service.MotionSensor, device.name); bindCharacteristic(device, ScryptedInterface.MotionSensor, motionSensorService, Characteristic.MotionDetected, From bd07d393391edf0b9bc705b7a28e177e918ebeed Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 19:59:11 -0800 Subject: [PATCH 5/9] sdk: ambient light --- sdk/gen/types.input.ts | 7 +++++++ sdk/package-lock.json | 4 ++-- sdk/package.json | 2 +- sdk/scrypted_python/scrypted_sdk/types.py | 13 +++++++++++++ sdk/types.d.ts | 10 ++++++++++ sdk/types.js | 9 +++++++++ sdk/types.ts | 17 +++++++++++++++++ 7 files changed, 59 insertions(+), 3 deletions(-) diff --git a/sdk/gen/types.input.ts b/sdk/gen/types.input.ts index 4f0ecf491..033166c0a 100644 --- a/sdk/gen/types.input.ts +++ b/sdk/gen/types.input.ts @@ -633,6 +633,12 @@ export interface AudioSensor { export interface MotionSensor { motionDetected?: boolean; } +export interface AmbientLightSensor { + /** + * The ambient light in lumens. + */ + ambientLight: number; +} export interface OccupancySensor { occupied?: boolean; } @@ -1230,6 +1236,7 @@ export enum ScryptedInterface { PowerSensor = "PowerSensor", AudioSensor = "AudioSensor", MotionSensor = "MotionSensor", + AmbientLightSensor = "AmbientLightSensor", OccupancySensor = "OccupancySensor", FloodSensor = "FloodSensor", UltravioletSensor = "UltravioletSensor", diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 21c5fd2a3..7ec121936 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sdk", - "version": "0.0.148", + "version": "0.0.150", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sdk", - "version": "0.0.148", + "version": "0.0.150", "license": "ISC", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.14.5", diff --git a/sdk/package.json b/sdk/package.json index ef4a09474..b0d6f972b 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sdk", - "version": "0.0.148", + "version": "0.0.150", "description": "", "main": "index.js", "scripts": { diff --git a/sdk/scrypted_python/scrypted_sdk/types.py b/sdk/scrypted_python/scrypted_sdk/types.py index 786abeeea..eb4baf482 100644 --- a/sdk/scrypted_python/scrypted_sdk/types.py +++ b/sdk/scrypted_python/scrypted_sdk/types.py @@ -59,6 +59,7 @@ class ScryptedDeviceType(Enum): Valve = "Valve" class ScryptedInterface(Enum): + AmbientLightSensor = "AmbientLightSensor" AudioSensor = "AudioSensor" Authenticator = "Authenticator" Battery = "Battery" @@ -417,6 +418,10 @@ class Setting(TypedDict): value: SettingValue pass +class AmbientLightSensor: + ambientLight: float + pass + class AudioSensor: audioDetected: bool pass @@ -935,6 +940,7 @@ class ScryptedInterfaceProperty(Enum): powerDetected = "powerDetected" audioDetected = "audioDetected" motionDetected = "motionDetected" + ambientLight = "ambientLight" occupied = "occupied" flooded = "flooded" ultraviolet = "ultraviolet" @@ -1236,6 +1242,13 @@ class DeviceState: def motionDetected(self, value: bool): self.setScryptedProperty("motionDetected", value) + @property + def ambientLight(self) -> float: + self.getScryptedProperty("ambientLight") + @ambientLight.setter + def ambientLight(self, value: float): + self.setScryptedProperty("ambientLight", value) + @property def occupied(self) -> bool: self.getScryptedProperty("occupied") diff --git a/sdk/types.d.ts b/sdk/types.d.ts index f9cc3841b..7594a31ba 100644 --- a/sdk/types.d.ts +++ b/sdk/types.d.ts @@ -41,6 +41,7 @@ export interface DeviceState { powerDetected?: boolean; audioDetected?: boolean; motionDetected?: boolean; + ambientLight?: number; occupied?: boolean; flooded?: boolean; ultraviolet?: number; @@ -91,6 +92,7 @@ export declare class DeviceBase implements DeviceState { powerDetected?: boolean; audioDetected?: boolean; motionDetected?: boolean; + ambientLight?: number; occupied?: boolean; flooded?: boolean; ultraviolet?: number; @@ -141,6 +143,7 @@ export declare enum ScryptedInterfaceProperty { powerDetected = "powerDetected", audioDetected = "audioDetected", motionDetected = "motionDetected", + ambientLight = "ambientLight", occupied = "occupied", flooded = "flooded", ultraviolet = "ultraviolet", @@ -728,6 +731,12 @@ export interface AudioSensor { export interface MotionSensor { motionDetected?: boolean; } +export interface AmbientLightSensor { + /** + * The ambient light in lumens. + */ + ambientLight: number; +} export interface OccupancySensor { occupied?: boolean; } @@ -1271,6 +1280,7 @@ export declare enum ScryptedInterface { PowerSensor = "PowerSensor", AudioSensor = "AudioSensor", MotionSensor = "MotionSensor", + AmbientLightSensor = "AmbientLightSensor", OccupancySensor = "OccupancySensor", FloodSensor = "FloodSensor", UltravioletSensor = "UltravioletSensor", diff --git a/sdk/types.js b/sdk/types.js index a97fc6b94..ae462187f 100644 --- a/sdk/types.js +++ b/sdk/types.js @@ -47,6 +47,7 @@ var ScryptedInterfaceProperty; ScryptedInterfaceProperty["powerDetected"] = "powerDetected"; ScryptedInterfaceProperty["audioDetected"] = "audioDetected"; ScryptedInterfaceProperty["motionDetected"] = "motionDetected"; + ScryptedInterfaceProperty["ambientLight"] = "ambientLight"; ScryptedInterfaceProperty["occupied"] = "occupied"; ScryptedInterfaceProperty["flooded"] = "flooded"; ScryptedInterfaceProperty["ultraviolet"] = "ultraviolet"; @@ -404,6 +405,13 @@ exports.ScryptedInterfaceDescriptors = { 'motionDetected' ] }, + AmbientLightSensor: { + name: 'AmbientLightSensor', + methods: [], + properties: [ + 'ambientLight' + ] + }, OccupancySensor: { name: 'OccupancySensor', methods: [], @@ -650,6 +658,7 @@ var ScryptedInterface; ScryptedInterface["PowerSensor"] = "PowerSensor"; ScryptedInterface["AudioSensor"] = "AudioSensor"; ScryptedInterface["MotionSensor"] = "MotionSensor"; + ScryptedInterface["AmbientLightSensor"] = "AmbientLightSensor"; ScryptedInterface["OccupancySensor"] = "OccupancySensor"; ScryptedInterface["FloodSensor"] = "FloodSensor"; ScryptedInterface["UltravioletSensor"] = "UltravioletSensor"; diff --git a/sdk/types.ts b/sdk/types.ts index 7bb7f2563..b76832843 100644 --- a/sdk/types.ts +++ b/sdk/types.ts @@ -42,6 +42,7 @@ export interface DeviceState { powerDetected?: boolean audioDetected?: boolean motionDetected?: boolean + ambientLight?: number occupied?: boolean flooded?: boolean ultraviolet?: number @@ -93,6 +94,7 @@ export class DeviceBase implements DeviceState { powerDetected?: boolean audioDetected?: boolean motionDetected?: boolean + ambientLight?: number occupied?: boolean flooded?: boolean ultraviolet?: number @@ -145,6 +147,7 @@ export enum ScryptedInterfaceProperty { powerDetected = "powerDetected", audioDetected = "audioDetected", motionDetected = "motionDetected", + ambientLight = "ambientLight", occupied = "occupied", flooded = "flooded", ultraviolet = "ultraviolet", @@ -505,6 +508,13 @@ export const ScryptedInterfaceDescriptors: { [scryptedInterface: string]: Scrypt 'motionDetected' ] }, + AmbientLightSensor: { + name: 'AmbientLightSensor', + methods: [], + properties: [ + 'ambientLight' + ] + }, OccupancySensor: { name: 'OccupancySensor', methods: [], @@ -1272,6 +1282,12 @@ export interface AudioSensor { export interface MotionSensor { motionDetected?: boolean; } +export interface AmbientLightSensor { + /** + * The ambient light in lumens. + */ + ambientLight: number; +} export interface OccupancySensor { occupied?: boolean; } @@ -1869,6 +1885,7 @@ export enum ScryptedInterface { PowerSensor = "PowerSensor", AudioSensor = "AudioSensor", MotionSensor = "MotionSensor", + AmbientLightSensor = "AmbientLightSensor", OccupancySensor = "OccupancySensor", FloodSensor = "FloodSensor", UltravioletSensor = "UltravioletSensor", From 9e2dc14103826999d4ef208158470deeca693790 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 20:02:40 -0800 Subject: [PATCH 6/9] sdk/homekit/server: ambient light sensor support --- plugins/homekit/package-lock.json | 4 ++-- plugins/homekit/package.json | 2 +- plugins/homekit/src/types/sensor.ts | 18 ++++++++++++------ sdk/gen/types.input.ts | 2 +- sdk/package-lock.json | 4 ++-- sdk/package.json | 2 +- sdk/types.d.ts | 2 +- sdk/types.ts | 2 +- server/package-lock.json | 18 +++++++++--------- server/package.json | 4 ++-- 10 files changed, 32 insertions(+), 26 deletions(-) diff --git a/plugins/homekit/package-lock.json b/plugins/homekit/package-lock.json index a4a2949a8..92e7be2e0 100644 --- a/plugins/homekit/package-lock.json +++ b/plugins/homekit/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/homekit", - "version": "0.0.175", + "version": "0.0.176", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/homekit", - "version": "0.0.175", + "version": "0.0.176", "dependencies": { "hap-nodejs": "file:../../external/HAP-NodeJS", "lodash": "^4.17.21", diff --git a/plugins/homekit/package.json b/plugins/homekit/package.json index d0c6e4c24..9efa483e3 100644 --- a/plugins/homekit/package.json +++ b/plugins/homekit/package.json @@ -40,5 +40,5 @@ "@types/qrcode": "^1.4.1", "@types/url-parse": "^1.4.3" }, - "version": "0.0.175" + "version": "0.0.176" } diff --git a/plugins/homekit/src/types/sensor.ts b/plugins/homekit/src/types/sensor.ts index 297b2e06f..dfe8ba95f 100644 --- a/plugins/homekit/src/types/sensor.ts +++ b/plugins/homekit/src/types/sensor.ts @@ -1,5 +1,5 @@ -import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer, HumiditySensor, AudioSensor } from '@scrypted/sdk' +import { MotionSensor, BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, Thermometer, HumiditySensor, AudioSensor, AmbientLightSensor } from '@scrypted/sdk' import { addSupportedType, bindCharacteristic, DummyDevice } from '../common' import { Characteristic, Service } from '../hap'; import { makeAccessory } from './common'; @@ -9,15 +9,21 @@ addSupportedType({ probe(device: DummyDevice) { return device.interfaces.includes(ScryptedInterface.Thermometer) || device.interfaces.includes(ScryptedInterface.BinarySensor) || device.interfaces.includes(ScryptedInterface.MotionSensor); }, - getAccessory: async (device: ScryptedDevice & AudioSensor & BinarySensor & MotionSensor & Thermometer & HumiditySensor) => { + getAccessory: async (device: ScryptedDevice & AmbientLightSensor & AudioSensor & BinarySensor & MotionSensor & Thermometer & HumiditySensor) => { const accessory = makeAccessory(device); if (device.interfaces.includes(ScryptedInterface.BinarySensor)) { - const contactSensorService = accessory.addService(Service.ContactSensor, device.name); - bindCharacteristic(device, ScryptedInterface.BinarySensor, contactSensorService, Characteristic.ContactSensorState, + const service = accessory.addService(Service.ContactSensor, device.name); + bindCharacteristic(device, ScryptedInterface.BinarySensor, service, Characteristic.ContactSensorState, () => !!device.binaryState); } + if (device.interfaces.includes(ScryptedInterface.AmbientLightSensor)) { + const service = accessory.addService(Service.LightSensor, device.name); + bindCharacteristic(device, ScryptedInterface.AmbientLightSensor, service, Characteristic.CurrentAmbientLightLevel, + () => device.ambientLight || 0); + } + if (device.interfaces.includes(ScryptedInterface.AudioSensor)) { const service = accessory.addService(Service.ContactSensor, device.name + ' Alarm Sound', 'AudioSensor'); bindCharacteristic(device, ScryptedInterface.AudioSensor, service, Characteristic.ContactSensorState, @@ -25,8 +31,8 @@ addSupportedType({ } if (device.interfaces.includes(ScryptedInterface.MotionSensor)) { - const motionSensorService = accessory.addService(Service.MotionSensor, device.name); - bindCharacteristic(device, ScryptedInterface.MotionSensor, motionSensorService, Characteristic.MotionDetected, + const service = accessory.addService(Service.MotionSensor, device.name); + bindCharacteristic(device, ScryptedInterface.MotionSensor, service, Characteristic.MotionDetected, () => !!device.motionDetected, true); } diff --git a/sdk/gen/types.input.ts b/sdk/gen/types.input.ts index 033166c0a..d47325cc3 100644 --- a/sdk/gen/types.input.ts +++ b/sdk/gen/types.input.ts @@ -635,7 +635,7 @@ export interface MotionSensor { } export interface AmbientLightSensor { /** - * The ambient light in lumens. + * The ambient light in lux. */ ambientLight: number; } diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 7ec121936..9659dc3f9 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sdk", - "version": "0.0.150", + "version": "0.0.151", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sdk", - "version": "0.0.150", + "version": "0.0.151", "license": "ISC", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.14.5", diff --git a/sdk/package.json b/sdk/package.json index b0d6f972b..3f88c4d49 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sdk", - "version": "0.0.150", + "version": "0.0.151", "description": "", "main": "index.js", "scripts": { diff --git a/sdk/types.d.ts b/sdk/types.d.ts index 7594a31ba..8cbc4061d 100644 --- a/sdk/types.d.ts +++ b/sdk/types.d.ts @@ -733,7 +733,7 @@ export interface MotionSensor { } export interface AmbientLightSensor { /** - * The ambient light in lumens. + * The ambient light in lux. */ ambientLight: number; } diff --git a/sdk/types.ts b/sdk/types.ts index b76832843..a22b7c40f 100644 --- a/sdk/types.ts +++ b/sdk/types.ts @@ -1284,7 +1284,7 @@ export interface MotionSensor { } export interface AmbientLightSensor { /** - * The ambient light in lumens. + * The ambient light in lux. */ ambientLight: number; } diff --git a/server/package-lock.json b/server/package-lock.json index 5b79c3503..74e24a0cd 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,17 +1,17 @@ { "name": "@scrypted/server", - "version": "0.0.133", + "version": "0.0.134", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/server", - "version": "0.0.133", + "version": "0.0.134", "license": "ISC", "dependencies": { "@mapbox/node-pre-gyp": "^1.0.5", "@scrypted/ffmpeg": "^1.0.9", - "@scrypted/sdk": "^0.0.148", + "@scrypted/sdk": "^0.0.151", "adm-zip": "^0.5.3", "axios": "^0.21.1", "body-parser": "^1.19.0", @@ -858,9 +858,9 @@ } }, "node_modules/@scrypted/sdk": { - "version": "0.0.148", - "resolved": "https://registry.npmjs.org/@scrypted/sdk/-/sdk-0.0.148.tgz", - "integrity": "sha512-yXa7C99D4yLBIx3A4Ed8yAsxK3JeZKJ4X/rmjsWRYQHqGufy1Nl1Yza3eTubsyzPQHWQdrS58CmEcLDfa1+Z9Q==", + "version": "0.0.151", + "resolved": "https://registry.npmjs.org/@scrypted/sdk/-/sdk-0.0.151.tgz", + "integrity": "sha512-0xVuMvQcc76Df2T1BS7zpNpdFxwWJxX0XjMhBDxdArA+4kxr2oRXNUSY4g3sqIXG0Z5tLHJUSd3FZ1Mf1Uqm2g==", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", @@ -5891,9 +5891,9 @@ } }, "@scrypted/sdk": { - "version": "0.0.148", - "resolved": "https://registry.npmjs.org/@scrypted/sdk/-/sdk-0.0.148.tgz", - "integrity": "sha512-yXa7C99D4yLBIx3A4Ed8yAsxK3JeZKJ4X/rmjsWRYQHqGufy1Nl1Yza3eTubsyzPQHWQdrS58CmEcLDfa1+Z9Q==", + "version": "0.0.151", + "resolved": "https://registry.npmjs.org/@scrypted/sdk/-/sdk-0.0.151.tgz", + "integrity": "sha512-0xVuMvQcc76Df2T1BS7zpNpdFxwWJxX0XjMhBDxdArA+4kxr2oRXNUSY4g3sqIXG0Z5tLHJUSd3FZ1Mf1Uqm2g==", "requires": { "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", diff --git a/server/package.json b/server/package.json index a425bac57..3845bce20 100644 --- a/server/package.json +++ b/server/package.json @@ -1,11 +1,11 @@ { "name": "@scrypted/server", - "version": "0.0.133", + "version": "0.0.134", "description": "", "dependencies": { "@mapbox/node-pre-gyp": "^1.0.5", "@scrypted/ffmpeg": "^1.0.9", - "@scrypted/sdk": "^0.0.148", + "@scrypted/sdk": "^0.0.151", "adm-zip": "^0.5.3", "axios": "^0.21.1", "body-parser": "^1.19.0", From 80909addc820c1b0147c65a3490004ccf08cbd2c Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 20:25:57 -0800 Subject: [PATCH 7/9] mqtt: filter allowed devices --- plugins/mqtt/package-lock.json | 4 ++-- plugins/mqtt/package.json | 2 +- plugins/mqtt/src/publishable-types.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/mqtt/package-lock.json b/plugins/mqtt/package-lock.json index f24b3ad26..74d6ed1b5 100644 --- a/plugins/mqtt/package-lock.json +++ b/plugins/mqtt/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/mqtt", - "version": "0.0.36", + "version": "0.0.37", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/mqtt", - "version": "0.0.36", + "version": "0.0.37", "dependencies": { "@types/node": "^16.6.1", "aedes": "^0.46.1", diff --git a/plugins/mqtt/package.json b/plugins/mqtt/package.json index 420852c86..8ff621d85 100644 --- a/plugins/mqtt/package.json +++ b/plugins/mqtt/package.json @@ -38,5 +38,5 @@ "@scrypted/sdk": "file:../../sdk", "@types/nunjucks": "^3.2.0" }, - "version": "0.0.36" + "version": "0.0.37" } diff --git a/plugins/mqtt/src/publishable-types.ts b/plugins/mqtt/src/publishable-types.ts index ec19f8323..b568d4e55 100644 --- a/plugins/mqtt/src/publishable-types.ts +++ b/plugins/mqtt/src/publishable-types.ts @@ -13,5 +13,6 @@ export function canMixin(type: ScryptedDeviceType, interfaces: string[]): boolea set.delete(ScryptedInterface.Settings); set.delete(ScryptedInterface.Readme); set.delete(ScryptedInterface.BufferConverter); + set.delete(ScryptedInterface.ScryptedPlugin); return !!set.size; } From 68e8578b508d88e179fc668c67d26c5d645072df Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 20:28:15 -0800 Subject: [PATCH 8/9] webhook: filter allowed devices --- plugins/mqtt/src/publishable-types.ts | 2 +- plugins/webhook/package-lock.json | 4 ++-- plugins/webhook/package.json | 2 +- plugins/webhook/src/main.ts | 16 +++------------- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/plugins/mqtt/src/publishable-types.ts b/plugins/mqtt/src/publishable-types.ts index b568d4e55..30782f884 100644 --- a/plugins/mqtt/src/publishable-types.ts +++ b/plugins/mqtt/src/publishable-types.ts @@ -1,6 +1,6 @@ import { ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk"; -export function canMixin(type: ScryptedDeviceType, interfaces: string[]): boolean { +export function isPublishable(type: ScryptedDeviceType, interfaces: string[]): boolean { const set = new Set(interfaces); set.delete(ScryptedInterface.ObjectDetection); set.delete(ScryptedInterface.DeviceDiscovery); diff --git a/plugins/webhook/package-lock.json b/plugins/webhook/package-lock.json index 8869a4535..daab21fc3 100644 --- a/plugins/webhook/package-lock.json +++ b/plugins/webhook/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/webhook", - "version": "0.0.16", + "version": "0.0.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/webhook", - "version": "0.0.16", + "version": "0.0.17", "dependencies": { "@types/node": "^16.6.1" }, diff --git a/plugins/webhook/package.json b/plugins/webhook/package.json index d4e342917..70373a6f3 100644 --- a/plugins/webhook/package.json +++ b/plugins/webhook/package.json @@ -33,5 +33,5 @@ "devDependencies": { "@scrypted/sdk": "file:../../sdk" }, - "version": "0.0.16" + "version": "0.0.17" } diff --git a/plugins/webhook/src/main.ts b/plugins/webhook/src/main.ts index e1dbbac04..1f167add9 100644 --- a/plugins/webhook/src/main.ts +++ b/plugins/webhook/src/main.ts @@ -6,6 +6,8 @@ import { randomBytes } from 'crypto'; const allInterfaceMethods: string[] = [].concat(...Object.values(ScryptedInterfaceDescriptors).map((type: any) => type.methods)); const allInterfaceProperties: string[] = [].concat(...Object.values(ScryptedInterfaceDescriptors).map((type: any) => type.properties)); +import { isPublishable} from '../../mqtt/src/publishable-types'; + const { systemManager, endpointManager, mediaManager } = sdk; const mediaObjectMethods = [ @@ -187,19 +189,7 @@ class WebhookPlugin extends ScryptedDeviceBase implements Settings, MixinProvide } async canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise { - const set = new Set(interfaces); - set.delete(ScryptedInterface.ObjectDetection); - set.delete(ScryptedInterface.DeviceDiscovery); - set.delete(ScryptedInterface.DeviceCreator); - set.delete(ScryptedInterface.DeviceProvider); - set.delete(ScryptedInterface.MixinProvider); - set.delete(ScryptedInterface.PushHandler); - set.delete(ScryptedInterface.EngineIOHandler); - set.delete(ScryptedInterface.HttpRequestHandler); - set.delete(ScryptedInterface.Settings); - set.delete(ScryptedInterface.Readme); - set.delete(ScryptedInterface.BufferConverter); - if (!set.size) + if (!isPublishable(type, interfaces)) return; return [ ScryptedInterface.Settings, From 6abc353248bd6bf6833915d43d01c61e7a2c1a84 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 27 Jan 2022 20:37:33 -0800 Subject: [PATCH 9/9] homekit: fix build breaks --- common/src/settings-mixin.ts | 8 ++++++-- plugins/homekit/src/types/camera.ts | 1 + plugins/homekit/src/types/garage.ts | 4 ++-- plugins/homekit/src/types/mediaplayer.ts | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/common/src/settings-mixin.ts b/common/src/settings-mixin.ts index e8fd043d5..86f233378 100644 --- a/common/src/settings-mixin.ts +++ b/common/src/settings-mixin.ts @@ -37,12 +37,14 @@ export abstract class SettingsMixinDeviceBase extends MixinDeviceBase extends MixinDeviceBase { + getAccessory: async (device: ScryptedDevice & Entry & EntrySensor) => { const accessory = makeAccessory(device); const service = accessory.addService(Service.GarageDoorOpener, device.name); diff --git a/plugins/homekit/src/types/mediaplayer.ts b/plugins/homekit/src/types/mediaplayer.ts index f5257e216..f4550654a 100644 --- a/plugins/homekit/src/types/mediaplayer.ts +++ b/plugins/homekit/src/types/mediaplayer.ts @@ -1,5 +1,5 @@ -import { VideoCamera, MediaPlayer, MediaPlayerState, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk' +import { VideoCamera, MediaPlayer, MediaPlayerState, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, StartStop } from '@scrypted/sdk' import { addSupportedType, bindCharacteristic, DummyDevice } from '../common' import { Categories, Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, NodeCallback, Service } from '../hap'; import { makeAccessory } from './common'; @@ -12,7 +12,7 @@ addSupportedType({ probe(device: DummyDevice) { return device.interfaces.includes(ScryptedInterface.MediaPlayer); }, - getAccessory: async (device: ScryptedDevice & MediaPlayer) => { + getAccessory: async (device: ScryptedDevice & MediaPlayer & StartStop) => { const accessory = makeAccessory(device); accessory.category = Categories.TELEVISION; const service = accessory.addService(Service.Television, "Television", "Television");