Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add types to support Air Purifier #833

Merged
merged 2 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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