Merge branch 'main' of github.com:koush/scrypted

This commit is contained in:
Koushik Dutta
2023-06-01 20:33:33 -07:00
5 changed files with 227 additions and 1 deletions

View File

@@ -0,0 +1,119 @@
import { ScryptedDevice, ScryptedDeviceType, ScryptedInterface, AirPurifierStatus, AirPurifierMode, AirPurifier, FilterMaintenance } from '@scrypted/sdk';
import { addSupportedType, bindCharacteristic, DummyDevice, } from '../common';
import { Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, Service } from '../hap';
import { makeAccessory } from './common';
import type { HomeKitPlugin } from "../main";
addSupportedType({
type: ScryptedDeviceType.AirPurifier,
probe(device: DummyDevice): boolean {
return device.interfaces.includes(ScryptedInterface.AirPurifier);
},
getAccessory: async (device: ScryptedDevice & AirPurifier & FilterMaintenance, homekitPlugin: HomeKitPlugin) => {
const accessory = makeAccessory(device, homekitPlugin);
const service = accessory.addService(Service.AirPurifier, device.name);
const nightModeService = accessory.addService(Service.Switch, `${device.name} Night Mode`)
/* On/Off AND mode toggle */
bindCharacteristic(device, ScryptedInterface.AirPurifier, service, Characteristic.Active,
() => {
switch(device.airPurifierState.status) {
case AirPurifierStatus.Active:
return Characteristic.Active.ACTIVE;
case AirPurifierStatus.ActiveNightMode:
return Characteristic.Active.ACTIVE;
}
return Characteristic.Active.INACTIVE;
});
service.getCharacteristic(Characteristic.Active)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();
device.setAirPurifierState({
status: (value as boolean) ? AirPurifierStatus.Active : AirPurifierStatus.Inactive,
})
});
/* Current State */
bindCharacteristic(device, ScryptedInterface.AirPurifier, service, Characteristic.CurrentAirPurifierState,
() => {
switch (device.airPurifierState.status) {
case AirPurifierStatus.Inactive:
return Characteristic.CurrentAirPurifierState.INACTIVE;
case AirPurifierStatus.Idle:
return Characteristic.CurrentAirPurifierState.IDLE;
}
return Characteristic.CurrentAirPurifierState.PURIFYING_AIR;
});
/* Fan Speed */
bindCharacteristic(device, ScryptedInterface.AirPurifier, service, Characteristic.RotationSpeed,
() => device.airPurifierState.speed);
service.getCharacteristic(Characteristic.RotationSpeed)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();
device.setAirPurifierState({
speed: value,
})
})
/* i.e. Mode: Manual/Auto slider */
bindCharacteristic(device, ScryptedInterface.AirPurifier, service, Characteristic.TargetAirPurifierState,
() => {
if (device.airPurifierState.mode == AirPurifierMode.Automatic)
return Characteristic.TargetAirPurifierState.AUTO;
return Characteristic.TargetAirPurifierState.MANUAL;
});
service.getCharacteristic(Characteristic.TargetAirPurifierState)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();
device.setAirPurifierState({
mode: value === Characteristic.TargetAirPurifierState.AUTO ? AirPurifierMode.Automatic : AirPurifierMode.Manual,
})
});
/* LockPhysicalControls i.e. "Child Lock: Unlocked/Locked" */
bindCharacteristic(device, ScryptedInterface.AirPurifier, service, Characteristic.LockPhysicalControls,
() => !!device.airPurifierState.lockPhysicalControls);
service.getCharacteristic(Characteristic.LockPhysicalControls)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();
device.setAirPurifierState({
lockPhysicalControls: (value as boolean),
})
})
/* Night mode switch */
bindCharacteristic(device, ScryptedInterface.AirPurifier, nightModeService, Characteristic.On,
() => !!(device.airPurifierState.status === AirPurifierStatus.ActiveNightMode));
nightModeService.getCharacteristic(Characteristic.On)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();
device.setAirPurifierState({
status: value ? AirPurifierStatus.ActiveNightMode : AirPurifierStatus.Active,
})
})
/* Optional: Filter Maintenance Service */
if (device.interfaces.includes(ScryptedInterface.FilterMaintenance)) {
const filterMaintenanceService = accessory.addService(Service.FilterMaintenance, device.name);
bindCharacteristic(device, ScryptedInterface.FilterMaintenance, filterMaintenanceService, Characteristic.FilterLifeLevel,
() => device.filterLifeLevel)
bindCharacteristic(device, ScryptedInterface.FilterMaintenance, filterMaintenanceService, Characteristic.FilterChangeIndication,
() => {
if (device.filterChangeIndication)
return Characteristic.FilterChangeIndication.CHANGE_FILTER;
return Characteristic.FilterChangeIndication.FILTER_OK;
})
}
return accessory;
}
});

View File

@@ -15,3 +15,4 @@ import './vacuum';
import './outlet';
import './notifier';
import './windowcovering'
import './airpurifier'

View File

@@ -1418,7 +1418,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
const cloud = msos?.find(mso => mso.source === 'cloud');
if (cloud) {
this.storage.setItem('warnedCloud', 'true');
log.a(`${this.name} is a cloud camera. Prebuffering maintains a persistent stream and will not enabled by default. You must enable the Prebuffer stream manually.`)
log.a(`${this.name} is a cloud camera. Prebuffering maintains a persistent stream and will not be enabled by default. You must enable the Prebuffer stream manually.`)
}
}

View File

@@ -9,6 +9,16 @@ from typing import Union, Any, Callable
from .other import *
class AirPurifierMode(Enum):
Automatic = "Automatic"
Manual = "Manual"
class AirPurifierStatus(Enum):
Active = "Active"
ActiveNightMode = "ActiveNightMode"
Idle = "Idle"
Inactive = "Inactive"
class AirQuality(Enum):
Excellent = "Excellent"
Fair = "Fair"
@@ -49,6 +59,7 @@ class PanTiltZoomMovement(Enum):
class ScryptedDeviceType(Enum):
API = "API"
AirPurifier = "AirPurifier"
Automation = "Automation"
Builtin = "Builtin"
Camera = "Camera"
@@ -83,6 +94,7 @@ class ScryptedDeviceType(Enum):
WindowCovering = "WindowCovering"
class ScryptedInterface(Enum):
AirPurifier = "AirPurifier"
AirQualitySensor = "AirQualitySensor"
AmbientLightSensor = "AmbientLightSensor"
AudioSensor = "AudioSensor"
@@ -106,6 +118,7 @@ class ScryptedInterface(Enum):
EntrySensor = "EntrySensor"
EventRecorder = "EventRecorder"
Fan = "Fan"
FilterMaintenance = "FilterMaintenance"
FloodSensor = "FloodSensor"
HttpRequestHandler = "HttpRequestHandler"
HumiditySensor = "HumiditySensor"
@@ -319,6 +332,13 @@ class AdoptDevice(TypedDict):
settings: DeviceCreatorSettings
pass
class AirPurifierState(TypedDict):
lockPhysicalControls: bool
mode: AirPurifierMode
speed: float
status: AirPurifierStatus
pass
class ColorHsv(TypedDict):
h: float
s: float
@@ -733,6 +753,12 @@ class VideoFrameGeneratorOptions(TypedDict):
class TamperState(TypedDict):
pass
class AirPurifier:
airPurifierState: AirPurifierState
async def setAirPurifierState(self, state: AirPurifierState) -> None:
pass
pass
class AirQualitySensor:
airQuality: AirQuality
pass
@@ -864,6 +890,11 @@ class Fan:
pass
pass
class FilterMaintenance:
filterChangeIndication: bool
filterLifeLevel: float
pass
class FloodSensor:
flooded: bool
pass
@@ -1400,6 +1431,9 @@ class ScryptedInterfaceProperty(Enum):
noxDensity = "noxDensity"
co2ppm = "co2ppm"
airQuality = "airQuality"
airPurifierState = "airPurifierState"
filterChangeIndication = "filterChangeIndication"
filterLifeLevel = "filterLifeLevel"
humiditySetting = "humiditySetting"
fan = "fan"
applicationInfo = "applicationInfo"
@@ -1481,6 +1515,7 @@ class ScryptedInterfaceMethods(Enum):
putSetting = "putSetting"
armSecuritySystem = "armSecuritySystem"
disarmSecuritySystem = "disarmSecuritySystem"
setAirPurifierState = "setAirPurifierState"
getReadmeMarkdown = "getReadmeMarkdown"
getOauthUrl = "getOauthUrl"
onOauthCallback = "onOauthCallback"
@@ -1912,6 +1947,27 @@ class DeviceState:
def airQuality(self, value: AirQuality):
self.setScryptedProperty("airQuality", value)
@property
def airPurifierState(self) -> AirPurifierState:
return self.getScryptedProperty("airPurifierState")
@airPurifierState.setter
def airPurifierState(self, value: AirPurifierState):
self.setScryptedProperty("airPurifierState", value)
@property
def filterChangeIndication(self) -> bool:
return self.getScryptedProperty("filterChangeIndication")
@filterChangeIndication.setter
def filterChangeIndication(self, value: bool):
self.setScryptedProperty("filterChangeIndication", value)
@property
def filterLifeLevel(self) -> float:
return self.getScryptedProperty("filterLifeLevel")
@filterLifeLevel.setter
def filterLifeLevel(self, value: float):
self.setScryptedProperty("filterLifeLevel", value)
@property
def humiditySetting(self) -> HumiditySettingStatus:
return self.getScryptedProperty("humiditySetting")
@@ -2431,6 +2487,23 @@ ScryptedInterfaceDescriptors = {
"airQuality"
]
},
"AirPurifier": {
"name": "AirPurifier",
"methods": [
"setAirPurifierState"
],
"properties": [
"airPurifierState"
]
},
"FilterMaintenance": {
"name": "FilterMaintenance",
"methods": [],
"properties": [
"filterChangeIndication",
"filterLifeLevel"
]
},
"Readme": {
"name": "Readme",
"methods": [

View File

@@ -131,6 +131,7 @@ export enum ScryptedDeviceType {
SecuritySystem = "SecuritySystem",
WindowCovering = "WindowCovering",
Siren = "Siren",
AirPurifier = "AirPurifier",
Unknown = "Unknown",
}
/**
@@ -1181,6 +1182,36 @@ export interface Position {
export interface PositionSensor {
position?: Position;
}
export enum AirPurifierStatus {
Inactive = "Inactive",
Idle = "Idle",
Active = "Active",
ActiveNightMode = "ActiveNightMode",
}
export enum AirPurifierMode {
Manual = "Manual",
Automatic = "Automatic",
}
export interface AirPurifierState {
speed?: number;
status?: AirPurifierStatus,
mode?: AirPurifierMode,
lockPhysicalControls?: boolean,
}
export interface AirPurifier {
airPurifierState?: AirPurifierState;
setAirPurifierState(state: AirPurifierState): Promise<void>;
}
export interface FilterMaintenance {
filterLifeLevel?: number,
filterChangeIndication?: boolean,
}
export interface PM10Sensor {
pm10Density?: number;
}
@@ -1950,6 +1981,8 @@ export enum ScryptedInterface {
NOXSensor = "NOXSensor",
CO2Sensor = "CO2Sensor",
AirQualitySensor = "AirQualitySensor",
AirPurifier = "AirPurifier",
FilterMaintenance = "FilterMaintenance",
Readme = "Readme",
OauthClient = "OauthClient",
MixinProvider = "MixinProvider",