UNPKG

homebridge-daikin-local-platform

Version:

Homebridge platform plugin providing HomeKit support for Daikin air conditioners with local control

519 lines (391 loc) 20 kB
import { Service, PlatformAccessory, CharacteristicValue, CharacteristicSetCallback, CharacteristicGetCallback, CharacteristicEventTypes } from 'homebridge'; import DaikinPlatform from '../platform'; import { DEVICE_STATUS_REFRESH_INTERVAL } from '../const'; import { DaikinAccessoryContext} from '../types'; import { CLIMATE_MODE_AUTO, CLIMATE_MODE_COOLING, CLIMATE_MODE_DEHUMIDIFY, CLIMATE_MODE_HUMIDIFY, CLIMATE_MODE_FAN, CLIMATE_MODE_HEATING, CLIMATE_FAN_SPEED_AUTO, CLIMATE_FAN_SPEED_SLIENT, CLIMATE_FAN_SPEED_1, CLIMATE_FAN_SPEED_2, CLIMATE_FAN_SPEED_3, CLIMATE_FAN_SPEED_4, CLIMATE_FAN_SPEED_5 } from '../daikin-local'; import {DaikinLocalAPI, DaikinDevice} from '../daikin-local'; /** * An instance of this class is created for each accessory the platform registers. * Each accessory may expose multiple services of different service types. */ export default class ClimateAccessory { private services: Service[] = []; private _refreshInterval: NodeJS.Timer | undefined; private _lastFanSpeed = 1; // slient constructor( private readonly platform: DaikinPlatform, private readonly accessory: PlatformAccessory<DaikinAccessoryContext>, ) { accessory.context.device.setCallback(this.updateDeviceStatus.bind(this)); // Accessory Information // https://developers.homebridge.io/#/service/AccessoryInformation this.accessory.getService(this.platform.Service.AccessoryInformation) ?.setCharacteristic( this.platform.Characteristic.Manufacturer, 'Daikin ' + accessory.context.device?.getDeviceReg(), ) .setCharacteristic( this.platform.Characteristic.Model, accessory.context.device?.getDeviceType() || 'Unknown', ) .setCharacteristic( this.platform.Characteristic.SerialNumber, accessory.context.device?.getMacAddress() || 'Unknown', ) .setCharacteristic( this.platform.Characteristic.FirmwareRevision, accessory.context.device?.getFirmwareVersion() || 'Unknown', ); this.services['Climate'] = this.accessory.getService(this.platform.Service.HeaterCooler) || this.accessory.addService(this.platform.Service.HeaterCooler); // This is what is displayed as the default name on the Home app this.services['Climate'].setCharacteristic( this.platform.Characteristic.Name, accessory.context.device?.getDeviceName() || '空調', ); this.services['Climate'] .getCharacteristic(this.platform.Characteristic.Active) .onSet(this.setClimateActive.bind(this)) .onGet(this.getClimateActive.bind(this)); this.services['Climate'] .getCharacteristic(this.platform.Characteristic.CurrentTemperature) .setProps({ minValue: -100, maxValue: 100, minStep: 0.01, }); this.services['Climate'] .getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .onGet(this.getCurrentHeaterCoolerState.bind(this)); this.services['Climate'] .getCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState) .onSet(this.setTargetHeaterCoolerState.bind(this)); // Cooling Threshold Temperature (optional) this.services['Climate'] .getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature) .setProps({ minValue: accessory.context.device?.getCoolingThresholdTemperatureRange()[0] || '10', maxValue: accessory.context.device?.getCoolingThresholdTemperatureRange()[1] || '30', minStep: 0.5, }) .onSet(this.setCoolingThresholdTemperature.bind(this)) .onGet(this.getCoolingThresholdTemperature.bind(this)); // Heating Threshold Temperature (optional) this.services['Climate'] .getCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature) .setProps({ minValue: accessory.context.device?.getHeatingThresholdTemperatureRange()[0] || '10', maxValue: accessory.context.device?.getHeatingThresholdTemperatureRange()[1] || '30', minStep: 0.5, }) .onSet(this.setHeatingThresholdTemperature.bind(this)) .onGet(this.getHeatingThresholdTemperature.bind(this)); // Fan control service this.services['Fan'] = this.accessory.getService(this.platform.Service.Fan) || this.accessory.addService(this.platform.Service.Fan); this.services['Fan'].getCharacteristic(this.platform.Characteristic.On) .onGet(this.getFanStatus.bind(this)) .onSet(this.setFanStatus.bind(this)); this.services['Fan'].getCharacteristic(this.platform.Characteristic.RotationSpeed) .setProps({ unit: null, format: this.platform.Characteristic.Formats.UINT8, minValue: 0, maxValue: 6, validValues: [0, 1, 2, 3, 4, 5, 6] }) .onGet(this.getRotationSpeed.bind(this)) .onSet(this.setRotationSpeed.bind(this)); /* // // Motion sensor switch // const buttonName = 'Motion Sensor'; this.services[buttonName] = this.accessory.getServiceById(this.platform.Service.Switch, buttonName) || this.accessory.addService(this.platform.Service.Switch, 'buttonBuzzerName', buttonName); this.services[buttonName].setCharacteristic(this.platform.Characteristic.Name, buttonName); this.services[buttonName].getCharacteristic(this.platform.Characteristic.On) .onGet(this.getMotionDetection.bind(this)) .onSet(this.setMotionDetection.bind(this)); this.services[buttonName].addOptionalCharacteristic(this.platform.Characteristic.ConfiguredName); this.services[buttonName].setCharacteristic(this.platform.Characteristic.ConfiguredName, buttonName); */ //// this.services['HumiditySensor'] = this.accessory.getService(this.platform.Service.HumiditySensor) || this.accessory.addService(this.platform.Service.HumiditySensor); this.services['HumiditySensor'].getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) .onGet(this.getCurrentRelativeHumidity.bind(this)); ////////// // Update characteristic values asynchronously instead of using onGet handlers this.refreshDeviceStatus(); } async refreshDeviceStatus() { this.platform.log.debug(`Accessory: Refresh status for device '${this.accessory.displayName}'`); await this.accessory.context.device.fetchDeviceStatus(); /// // Schedule continuous device updates on the first run if (!this._refreshInterval) { this._refreshInterval = setInterval( this.refreshDeviceStatus.bind(this), DEVICE_STATUS_REFRESH_INTERVAL, ); } } async setClimateActive(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setClimateActive() for device '${this.accessory.displayName}'`); this.accessory.context.device.setPowerStatus(value === this.platform.Characteristic.Active.ACTIVE ? true : false); } async getClimateActive():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getClimateActive() for device '${this.accessory.displayName}'`); const active = this.accessory.context.device.getPowerStatus() ? this.platform.Characteristic.Active.ACTIVE : this.platform.Characteristic.Active.INACTIVE; this.services['Climate'].updateCharacteristic(this.platform.Characteristic.Active, active); return active; } async getFanStatus():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getFanStatus() for device '${this.accessory.displayName}'`); const value = this.accessory.context.device.getFanSpeedNumber() !== 0; this.services['Fan'].updateCharacteristic(this.platform.Characteristic.On, value); return value; } async setFanStatus(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setFanStatus() for device '${this.accessory.displayName}'`); let speed = this._lastFanSpeed; // restore to previous non-zero speed if (value === false) { // turn off fan means turn on auto-speed mode speed = 0; } this.setRotationSpeed(speed); } async getRotationSpeed():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getRotationSpeed() for device '${this.accessory.displayName}'`); let value = this.accessory.context.device.getFanSpeedNumber(); if (value === 0) { value = this._lastFanSpeed; } this.services['Fan'].updateCharacteristic(this.platform.Characteristic.RotationSpeed, value); return value; } async setRotationSpeed(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setRotationSpeed() for device '${this.accessory.displayName}'`); let speed = CLIMATE_FAN_SPEED_AUTO; switch (value) { case 0: const lastFanSpeed = this.accessory.context.device.getFanSpeedNumber(); if (lastFanSpeed !== 0) { this._lastFanSpeed = lastFanSpeed; } speed = CLIMATE_FAN_SPEED_AUTO; break; case 1: speed = CLIMATE_FAN_SPEED_SLIENT; break; case 2: speed = CLIMATE_FAN_SPEED_1; break; case 3: speed = CLIMATE_FAN_SPEED_2; break; case 4: speed = CLIMATE_FAN_SPEED_3; break; case 5: speed = CLIMATE_FAN_SPEED_4; break; case 6: speed = CLIMATE_FAN_SPEED_5; break; default: this.platform.log.error(`Unknown RotationSpeed: ${value}` ); return; } this.accessory.context.device.setFanSpeed(speed); } async getCurrentRelativeHumidity():Promise<CharacteristicValue> { try{ this.platform.log.debug(`Accessory: getCurrentRelativeHumidity() for device '${this.accessory.displayName}'`); }catch(e){ this.platform.log.error(e); } return this.accessory.context.device.getIndoorHumidity(); } async getCurrentHeaterCoolerState():Promise<CharacteristicValue> { if(this.accessory.context.device.getPowerStatus()){ const currentTemperature = await this.accessory.context.device.getIndoorTemperature() || 0; const targetTemperature = await this.accessory.context.device.getTargetTemperature() || 0; const currentMode = await this.accessory.context.device.getOperationMode() || CLIMATE_MODE_AUTO; switch (currentMode) { // Auto case CLIMATE_MODE_AUTO: // Set target state and current state (based on current temperature) this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.AUTO, ); if (currentTemperature < targetTemperature) { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.HEATING); } else if (currentTemperature > targetTemperature) { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.COOLING); } else { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); } break; // Heat case CLIMATE_MODE_HEATING: this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.HEAT, ); if (currentTemperature < targetTemperature) { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.HEATING); } else { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); } break; // Cool case CLIMATE_MODE_COOLING: this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.COOL, ); if (currentTemperature > targetTemperature) { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.COOLING); } else { this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); } break; // Dry (Dehumidifier) case CLIMATE_MODE_DEHUMIDIFY: this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.AUTO, ); break; // Humidifier case CLIMATE_MODE_HUMIDIFY: this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.AUTO, ); break; // Humidifier case CLIMATE_MODE_HUMIDIFY: this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.AUTO, ); break; // Fan case CLIMATE_MODE_FAN: this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState) .updateValue(this.platform.Characteristic.CurrentHeaterCoolerState.IDLE); this.services['Climate'].updateCharacteristic( this.platform.Characteristic.TargetHeaterCoolerState, this.platform.Characteristic.TargetHeaterCoolerState.AUTO, ); break; default: this.platform.log.error( `Unknown TargetHeaterCoolerState state: '${this.accessory.displayName}' '${currentMode}'`); break; } return this.services['Climate'].getCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState).value ; } return this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE; } async getCoolingThresholdTemperature():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getCoolingThresholdTemperature() for device '${this.accessory.displayName}'`); const value = this.accessory.context.device.getTargetTemperatureWithMode(CLIMATE_MODE_COOLING); this.services['Climate'].updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, value); return value; } async getHeatingThresholdTemperature():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getHeatingThresholdTemperature() for device '${this.accessory.displayName}'`); const value = this.accessory.context.device.getTargetTemperatureWithMode(CLIMATE_MODE_HEATING); this.services['Climate'].updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, value); return value; } async setCoolingThresholdTemperature(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setCoolingThresholdTemperature() for device '${this.accessory.displayName}'`); const threshold:number = +value; this.accessory.context.device.setOperationMode(CLIMATE_MODE_COOLING); this.accessory.context.device.setTargetTemperature(threshold); } async setHeatingThresholdTemperature(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setHeatingThresholdTemperature() for device '${this.accessory.displayName}'`); const threshold:number = +value; this.accessory.context.device.setOperationMode(CLIMATE_MODE_HEATING); this.accessory.context.device.setTargetTemperature(threshold); } async setTargetHeaterCoolerState(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setTargetHeaterCoolerState() for device '${this.accessory.displayName}'`); let mode = CLIMATE_MODE_AUTO switch (value) { case this.platform.Characteristic.TargetHeaterCoolerState.AUTO: mode = CLIMATE_MODE_AUTO; break; case this.platform.Characteristic.TargetHeaterCoolerState.COOL: mode = CLIMATE_MODE_COOLING; break; case this.platform.Characteristic.TargetHeaterCoolerState.HEAT: mode = CLIMATE_MODE_HEATING; break; default: this.platform.log.error(`Unknown TargetHeaterCoolerState ${value}`); return; } this.accessory.context.device.setOperationMode(mode); } async getMotionDetection():Promise<CharacteristicValue> { this.platform.log.debug(`Accessory: getMotionDetection() for device '${this.accessory.displayName}'`); return this.accessory.context.device.getMotionDetection(); } async setMotionDetection(value: CharacteristicValue) { this.platform.log.debug(`Accessory: setMotionDetection() for device '${this.accessory.displayName}'`); this.accessory.context.device.setMotionDetection(value ? true : false); this.services['MotionSensor'].updateCharacteristic(this.platform.Characteristic.On, value); } async updateDeviceStatus(device: DaikinDevice) { try{ this.platform.log.debug(`Accessory: updateDeviceStatus() for device '${this.accessory.displayName}'`); const active = this.accessory.context.device.getPowerStatus() ? this.platform.Characteristic.Active.ACTIVE : this.platform.Characteristic.Active.INACTIVE; this.services['Climate'].updateCharacteristic(this.platform.Characteristic.Active, active); this.services['Climate'].updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.accessory.context.device.getIndoorTemperature()); this.getCurrentHeaterCoolerState(); this.services['Climate'].updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, this.accessory.context.device.getTargetTemperatureWithMode(CLIMATE_MODE_HEATING)); this.services['Climate'].updateCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, this.accessory.context.device.getTargetTemperatureWithMode(CLIMATE_MODE_COOLING)); this.services['HumiditySensor'].updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.accessory.context.device.getIndoorHumidity()); //this.services['MotionSensor'].updateCharacteristic(this.platform.Characteristic.On, this.accessory.context.device.getMotionDetection()); }catch(e){ this.platform.log.error(e); } } }