Skip to content

Commit

Permalink
add types to support Air Purifier (#833)
Browse files Browse the repository at this point in the history
* add types to support Air Purifier

* fix homekit type for airpurifier
  • Loading branch information
bzoel authored Jun 1, 2023
1 parent b7cda86 commit 8d4124a
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
119 changes: 119 additions & 0 deletions plugins/homekit/src/types/airpurifier.ts
Original file line number Diff line number Diff line change
@@ -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;
}
});
1 change: 1 addition & 0 deletions plugins/homekit/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ import './vacuum';
import './outlet';
import './notifier';
import './windowcovering'
import './airpurifier'
73 changes: 73 additions & 0 deletions sdk/types/scrypted_python/scrypted_sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
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"
Expand Down Expand Up @@ -49,6 +59,7 @@ class PanTiltZoomMovement(Enum):

class ScryptedDeviceType(Enum):
API = "API"
AirPurifier = "AirPurifier"
Automation = "Automation"
Builtin = "Builtin"
Camera = "Camera"
Expand Down Expand Up @@ -83,6 +94,7 @@ class ScryptedDeviceType(Enum):
WindowCovering = "WindowCovering"

class ScryptedInterface(Enum):
AirPurifier = "AirPurifier"
AirQualitySensor = "AirQualitySensor"
AmbientLightSensor = "AmbientLightSensor"
AudioSensor = "AudioSensor"
Expand All @@ -106,6 +118,7 @@ class ScryptedInterface(Enum):
EntrySensor = "EntrySensor"
EventRecorder = "EventRecorder"
Fan = "Fan"
FilterMaintenance = "FilterMaintenance"
FloodSensor = "FloodSensor"
HttpRequestHandler = "HttpRequestHandler"
HumiditySensor = "HumiditySensor"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -864,6 +890,11 @@ async def setFan(self, fan: FanState) -> None:
pass
pass

class FilterMaintenance:
filterChangeIndication: bool
filterLifeLevel: float
pass

class FloodSensor:
flooded: bool
pass
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -1481,6 +1515,7 @@ class ScryptedInterfaceMethods(Enum):
putSetting = "putSetting"
armSecuritySystem = "armSecuritySystem"
disarmSecuritySystem = "disarmSecuritySystem"
setAirPurifierState = "setAirPurifierState"
getReadmeMarkdown = "getReadmeMarkdown"
getOauthUrl = "getOauthUrl"
onOauthCallback = "onOauthCallback"
Expand Down Expand Up @@ -1912,6 +1947,27 @@ def airQuality(self) -> AirQuality:
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")
Expand Down Expand Up @@ -2431,6 +2487,23 @@ def applicationInfo(self, value: LauncherApplicationInfo):
"airQuality"
]
},
"AirPurifier": {
"name": "AirPurifier",
"methods": [
"setAirPurifierState"
],
"properties": [
"airPurifierState"
]
},
"FilterMaintenance": {
"name": "FilterMaintenance",
"methods": [],
"properties": [
"filterChangeIndication",
"filterLifeLevel"
]
},
"Readme": {
"name": "Readme",
"methods": [
Expand Down
33 changes: 33 additions & 0 deletions sdk/types/src/types.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export enum ScryptedDeviceType {
SecuritySystem = "SecuritySystem",
WindowCovering = "WindowCovering",
Siren = "Siren",
AirPurifier = "AirPurifier",
Unknown = "Unknown",
}
/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -1950,6 +1981,8 @@ export enum ScryptedInterface {
NOXSensor = "NOXSensor",
CO2Sensor = "CO2Sensor",
AirQualitySensor = "AirQualitySensor",
AirPurifier = "AirPurifier",
FilterMaintenance = "FilterMaintenance",
Readme = "Readme",
OauthClient = "OauthClient",
MixinProvider = "MixinProvider",
Expand Down

0 comments on commit 8d4124a

Please sign in to comment.