@switchbot/homebridge-switchbot
Version:
The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.
254 lines (228 loc) • 11.6 kB
text/typescript
/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
* airpurifier.ts: @switchbot/homebridge-switchbot.
*/
import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'
import type { bodyChange, irdevice } from 'node-switchbot'
import type { SwitchBotPlatform } from '../platform.js'
import type { irDevicesConfig } from '../settings.js'
import { irdeviceBase } from './irdevice.js'
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
export class AirPurifier extends irdeviceBase {
// Services
private AirPurifier: {
Name: CharacteristicValue
Service: Service
Active: CharacteristicValue
RotationSpeed: CharacteristicValue
CurrentAirPurifierState: CharacteristicValue
TargetAirPurifierState: CharacteristicValue
CurrentHeaterCoolerState: CharacteristicValue
}
private TemperatureSensor: {
Name: CharacteristicValue
Service: Service
CurrentTemperature: CharacteristicValue
}
// Characteristic Values
APActive!: CharacteristicValue
CurrentAPTemp!: CharacteristicValue
CurrentAPMode!: CharacteristicValue
CurrentAPFanSpeed!: CharacteristicValue
// Others
Busy: any
Timeout: any = null
static IDLE: number
CurrentMode!: number
static INACTIVE: number
LastTemperature!: number
CurrentFanSpeed!: number
static PURIFYING_AIR: number
constructor(
readonly platform: SwitchBotPlatform,
accessory: PlatformAccessory,
device: irdevice & irDevicesConfig,
) {
super(platform, accessory, device)
// Set category
accessory.category = this.hap.Categories.AIR_PURIFIER
// Initialize AirPurifier Service
accessory.context.AirPurifier = accessory.context.AirPurifier ?? {}
this.AirPurifier = {
Name: accessory.displayName,
Service: accessory.getService(this.hap.Service.AirPurifier) ?? accessory.addService(this.hap.Service.AirPurifier) as Service,
Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE,
RotationSpeed: accessory.context.RotationSpeed ?? 0,
CurrentAirPurifierState: accessory.context.CurrentAirPurifierState ?? this.hap.Characteristic.CurrentAirPurifierState.INACTIVE,
TargetAirPurifierState: accessory.context.TargetAirPurifierState ?? this.hap.Characteristic.TargetAirPurifierState.AUTO,
CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState ?? this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE,
}
accessory.context.AirPurifier = this.AirPurifier as object
this.AirPurifier.Service.setCharacteristic(this.hap.Characteristic.Name, this.AirPurifier.Name).getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
return this.AirPurifier.Active
}).onSet(this.ActiveSet.bind(this))
this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState).onGet(() => {
return this.CurrentAirPurifierStateGet()
})
this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.TargetAirPurifierState).onGet(() => {
return this.AirPurifier.TargetAirPurifierState
}).onSet(this.TargetAirPurifierStateSet.bind(this))
// Initialize TemperatureSensor Service
accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}
this.TemperatureSensor = {
Name: `${accessory.displayName} Temperature Sensor`,
Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? accessory.addService(this.hap.Service.TemperatureSensor) as Service,
CurrentTemperature: accessory.context.CurrentTemperature || 24,
}
accessory.context.TemperatureSensor = this.TemperatureSensor as object
this.TemperatureSensor.Service.setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name).getCharacteristic(this.hap.Characteristic.CurrentTemperature).onGet(() => {
return this.TemperatureSensor.CurrentTemperature
})
}
async ActiveSet(value: CharacteristicValue): Promise<void> {
this.debugLog(`Set Active: ${value}`)
this.AirPurifier.Active = value
if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE) {
this.pushAirPurifierOnChanges()
} else {
this.pushAirPurifierOffChanges()
}
}
async TargetAirPurifierStateSet(value: CharacteristicValue): Promise<void> {
switch (value) {
case this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR:
this.CurrentMode = AirPurifier.PURIFYING_AIR
break
case this.hap.Characteristic.CurrentAirPurifierState.IDLE:
this.CurrentMode = AirPurifier.IDLE
break
case this.hap.Characteristic.CurrentAirPurifierState.INACTIVE:
this.CurrentMode = AirPurifier.INACTIVE
break
default:
break
}
}
async CurrentAirPurifierStateGet(): Promise<number> {
if (this.AirPurifier.Active === 1) {
this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR
} else {
this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.INACTIVE
}
return this.AirPurifier.CurrentAirPurifierState
}
/**
* Pushes the requested changes to the SwitchBot API
* deviceType commandType Command command parameter Description
* AirPurifier: "command" "turnOn" "default" = every home appliance can be turned on by default
* AirPurifier: "command" "turnOff" "default" = every home appliance can be turned off by default
* AirPurifier: "command" "swing" "default" = swing
* AirPurifier: "command" "timer" "default" = timer
* AirPurifier: "command" "lowSpeed" "default" = fan speed to low
* AirPurifier: "command" "middleSpeed" "default" = fan speed to medium
* AirPurifier: "command" "highSpeed" "default" = fan speed to high
*/
async pushAirPurifierOnChanges(): Promise<void> {
this.debugLog(`pushAirPurifierOnChanges Active: ${this.AirPurifier.Active}, disablePushOn: ${this.deviceDisablePushOn}`)
if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE && !this.deviceDisablePushOn) {
const commandType: string = await this.commandType()
const command: string = await this.commandOn()
const bodyChange: bodyChange = {
command,
parameter: 'default',
commandType,
}
await this.pushChanges(bodyChange)
}
}
async pushAirPurifierOffChanges(): Promise<void> {
this.debugLog(`pushAirPurifierOffChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}`)
if (this.AirPurifier.Active === this.hap.Characteristic.Active.INACTIVE && !this.deviceDisablePushOn) {
const commandType: string = await this.commandType()
const command: string = await this.commandOff()
const bodyChange: bodyChange = {
command,
parameter: 'default',
commandType,
}
await this.pushChanges(bodyChange)
}
}
async pushAirPurifierStatusChanges(): Promise<void> {
this.debugLog(`pushAirPurifierStatusChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}, disablePushOn: ${this.deviceDisablePushOn}`)
if (!this.Busy) {
this.Busy = true
this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE
}
clearTimeout(this.Timeout)
// Make a new Timeout set to go off in 1000ms (1 second)
this.Timeout = setTimeout(this.pushAirPurifierDetailsChanges.bind(this), 1500)
}
async pushAirPurifierDetailsChanges(): Promise<void> {
this.debugLog(`pushAirPurifierDetailsChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}, disablePushOn: ${this.deviceDisablePushOn}`)
this.CurrentAPTemp = this.TemperatureSensor!.CurrentTemperature ?? 24
this.CurrentAPMode = this.CurrentMode ?? 1
this.CurrentAPFanSpeed = this.CurrentFanSpeed ?? 1
this.APActive = this.AirPurifier.Active === 1 ? 'on' : 'off'
const parameter = `${this.CurrentAPTemp},${this.CurrentAPMode},${this.CurrentAPFanSpeed},${this.APActive}`
const bodyChange: bodyChange = {
command: 'setAll',
parameter: `${parameter}`,
commandType: 'command',
}
if (this.AirPurifier.Active === 1) {
if ((Number(this.TemperatureSensor!.CurrentTemperature) || 24) < (this.LastTemperature || 30)) {
this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING
} else {
this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.HEATING
}
} else {
this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE
}
await this.pushChanges(bodyChange)
}
async pushChanges(bodyChange: any): Promise<void> {
this.debugLog('pushChanges')
if (this.device.connectionType === 'OpenAPI') {
this.infoLog(`Sending request to SwitchBot API, body: ${JSON.stringify(bodyChange)}`)
try {
const response = await this.pushChangeRequest(bodyChange)
const deviceStatus: any = response.body
await this.pushStatusCodes(deviceStatus)
if (await this.successfulStatusCodes(deviceStatus)) {
await this.successfulPushChange(deviceStatus, bodyChange)
await this.updateHomeKitCharacteristics()
} else {
await this.statusCode(deviceStatus.statusCode)
}
} catch (e: any) {
await this.apiError(e)
await this.pushChangeError(e)
}
} else {
this.warnLog(`Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`)
}
}
async updateHomeKitCharacteristics(): Promise<void> {
this.debugLog('updateHomeKitCharacteristics')
// Active
await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.Active, this.AirPurifier.Active, 'Active')
// CurrentAirPurifierState
await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.CurrentAirPurifierState, this.AirPurifier.CurrentAirPurifierState, 'CurrentAirPurifierState')
// CurrentHeaterCoolerState
await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.CurrentHeaterCoolerState, this.AirPurifier.CurrentHeaterCoolerState, 'CurrentHeaterCoolerState')
// CurrentTemperature
await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature')
}
async apiError(e: any): Promise<void> {
this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.Active, e)
this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.TargetAirPurifierState, e)
this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, e)
this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e)
this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e)
}
}