UNPKG

homebridge-melcloud-control

Version:

Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.

787 lines (751 loc) • 96.7 kB
import EventEmitter from 'events'; import MelCloudAta from './melcloudata.js'; import RestFul from './restful.js'; import Mqtt from './mqtt.js'; import { TemperatureDisplayUnits, AirConditioner } from './constants.js'; let Accessory, Characteristic, Service, Categories, AccessoryUUID; class DeviceAta extends EventEmitter { constructor(api, account, device, melCloud, accountInfo, contextKey, accountName, deviceId, deviceName, deviceTypeText, devicesFile, refreshInterval, useFahrenheit, restFul, mqtt) { super(); Accessory = api.platformAccessory; Characteristic = api.hap.Characteristic; Service = api.hap.Service; Categories = api.hap.Categories; AccessoryUUID = api.hap.uuid; //account config this.displayMode = device.displayMode; this.temperatureSensor = device.temperatureSensor || false; this.temperatureSensorOutdoor = device.temperatureSensorOutdoor || false; this.heatDryFanMode = device.heatDryFanMode || 1; //NONE, HEAT, DRY, FAN this.coolDryFanMode = device.coolDryFanMode || 1; //NONE, COOL, DRY, FAN this.autoDryFanMode = device.autoDryFanMode || 1; //NONE, AUTO, DRY, FAN this.presets = device.presets || []; this.buttons = device.buttonsSensors || []; this.disableLogInfo = account.disableLogInfo || false; this.disableLogDeviceInfo = account.disableLogDeviceInfo || false; this.enableDebugMode = account.enableDebugMode || false; this.accountInfo = accountInfo; this.contextKey = contextKey; this.accountName = accountName; this.deviceId = deviceId; this.deviceName = deviceName; this.deviceTypeText = deviceTypeText; this.devicesFile = devicesFile; this.refreshInterval = refreshInterval; this.startPrepareAccessory = true; this.displayDeviceInfo = true; //external integrations this.restFul = restFul; this.restFulConnected = false; this.mqtt = mqtt; this.mqttConnected = false; //function this.melCloud = melCloud; //function //presets configured this.presetsConfigured = []; for (const preset of this.presets) { const presetName = preset.name ?? false; const presetDisplayType = preset.displayType ?? 0; const presetNamePrefix = preset.namePrefix ?? false; if (presetName && presetDisplayType > 0) { const presetyServiceType = ['', Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][presetDisplayType]; const presetCharacteristicType = ['', Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][presetDisplayType]; preset.namePrefix = presetNamePrefix; preset.serviceType = presetyServiceType; preset.characteristicType = presetCharacteristicType; preset.state = false; preset.previousSettings = {}; this.presetsConfigured.push(preset); } else { const log = presetDisplayType === 0 ? false : this.emit('warn', `Preset Name: ${preset ? preset : 'Missing'}`); }; } this.presetsConfiguredCount = this.presetsConfigured.length || 0; //buttons configured this.buttonsConfigured = []; for (const button of this.buttons) { const buttonName = button.name ?? false; const buttonMode = button.mode ?? -1; const buttonDisplayType = button.displayType ?? 0; const buttonNamePrefix = button.namePrefix ?? false; if (buttonName && buttonMode >= 0 && buttonDisplayType > 0) { const buttonServiceType = ['', Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][buttonDisplayType]; const buttonCharacteristicType = ['', Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][buttonDisplayType]; button.namePrefix = buttonNamePrefix; button.serviceType = buttonServiceType; button.characteristicType = buttonCharacteristicType; button.state = false; button.previousValue = null; this.buttonsConfigured.push(button); } else { const log = buttonDisplayType === 0 ? false : this.emit('warn', `Button Name: ${buttonName ? buttonName : 'Missing'}, Mode: ${buttonMode ? buttonMode : 'Missing'}`); }; } this.buttonsConfiguredCount = this.buttonsConfigured.length || 0; //device data this.deviceData = {}; //accessory this.accessory = {}; this.accessory.useFahrenheit = useFahrenheit ? 1 : 0; this.accessory.temperatureUnit = TemperatureDisplayUnits[this.accessory.useFahrenheit]; }; async externalIntegrations() { try { //RESTFul server const restFulEnabled = this.restFul.enable || false; if (restFulEnabled) { if (!this.restFulConnected) { this.restFul1 = new RestFul({ port: this.deviceId.toString().slice(-4).replace(/^0/, '9'), debug: this.restFul.debug || false }); this.restFul1.on('connected', (message) => { this.restFulConnected = true; this.emit('success', message); }) .on('set', async (key, value) => { try { await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value); } catch (error) { this.emit('warn', error); }; }) .on('debug', (debug) => { this.emit('debug', debug); }) .on('error', (error) => { this.emit('warn', error); }); } } //MQTT client const mqttEnabled = this.mqtt.enable || false; if (mqttEnabled) { if (!this.mqttConnected) { this.mqtt1 = new Mqtt({ host: this.mqtt.host, port: this.mqtt.port || 1883, clientId: `${this.mqtt.clientId}_${this.deviceId}` || `${this.deviceTypeText}_${this.deviceName}_${this.deviceId}`, prefix: `${this.mqtt.prefix}/${this.deviceTypeText}/${this.deviceName}`, user: this.mqtt.user, passwd: this.mqtt.pass, debug: this.mqtt.debug || false }); this.mqtt1.on('connected', (message) => { this.mqttConnected = true; this.emit('success', message); }) .on('subscribed', (message) => { this.emit('success', message); }) .on('set', async (key, value) => { try { await this.setOverExternalIntegration('MQTT', this.deviceData, key, value); } catch (error) { this.emit('warn', error); }; }) .on('debug', (debug) => { this.emit('debug', debug); }) .on('error', (error) => { this.emit('warn', error); }); } } } catch (error) { this.emit('warn', `External integration start error: ${error}`); }; } async setOverExternalIntegration(integration, deviceData, key, value) { try { let set = false switch (key) { case 'Power': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'OperationMode': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.OperationModeSetTemperature; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'SetTemperature': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'DefaultCoolingSetTemperature': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'DefaultHeatingSetTemperature': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'FanSpeed': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetFanSpeed; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'VaneHorizontalDirection': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneHorizontal; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'VaneVerticalDirection': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneVertical; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'HideVaneControls': deviceData[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'HideDryModeControl': deviceData[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; await this.melCloudAta.send(deviceData, this.displayMode); break; case 'ProhibitSetTemperature': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; set = await this.melCloudAta.send(deviceData, this.displayMode); break; case 'ProhibitOperationMode': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; await this.melCloudAta.send(deviceData, this.displayMode); break; case 'ProhibitPower': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; set = await this.melCloudAta.send(deviceData, this.displayMode); break; default: this.emit('warn', `${integration}, received key: ${key}, value: ${value}`); break; }; return set; } catch (error) { throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`); }; } async startImpulseGenerator() { try { //start impule generator await this.melCloudAta.impulseGenerator.start([{ name: 'checkState', sampling: this.refreshInterval }]); return true; } catch (error) { throw new Error(`Impulse generator start error: ${error}`); }; } //prepare accessory async prepareAccessory(accountInfo, deviceData, deviceId, deviceTypeText, deviceName, accountName) { try { const presetsOnServer = this.accessory.presets; const modelSupportsHeat = this.accessory.modelSupportsHeat; const modelSupportsDry = this.accessory.modelSupportsDry; const modelSupportsCool = this.accessory.modelSupportsCool; const modelSupportsAuto = this.accessory.modelSupportsAuto; const modelSupportsFanSpeed = this.accessory.modelSupportsFanSpeed; const hasAutomaticFanSpeed = this.accessory.hasAutomaticFanSpeed; const hasOutdoorTemperature = this.accessory.hasOutdoorTemperature; const numberOfFanSpeeds = this.accessory.numberOfFanSpeeds; const swingFunction = this.accessory.swingFunction; const autoDryFanMode = [this.accessory.operationMode, 8, modelSupportsDry ? 2 : 8, 7][this.autoDryFanMode]; //NONE, AUTO - 8, DRY - 2, FAN - 7 const heatDryFanMode = [this.accessory.operationMode, 1, modelSupportsDry ? 2 : 1, 7][this.heatDryFanMode]; //NONE, HEAT - 1, DRY - 2, FAN - 7 const coolDryFanMode = [this.accessory.operationMode, 3, modelSupportsDry ? 2 : 3, 7][this.coolDryFanMode]; //NONE, COOL - 3, DRY - 2, FAN - 7 //accessory const debug = this.enableDebugMode ? this.emit('debug', `Prepare accessory`) : false; const accessoryName = deviceName; const accessoryUUID = AccessoryUUID.generate(accountName + deviceId.toString()); const accessoryCategory = Categories.AIR_CONDITIONER; const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); //information service const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare information service`) : false; accessory.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) .setCharacteristic(Characteristic.Model, this.model) .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision) .setCharacteristic(Characteristic.ConfiguredName, accessoryName); //melcloud services const serviceName = `${deviceTypeText} ${accessoryName}`; switch (this.displayMode) { case 1: //Heater Cooler const debug = this.enableDebugMode ? this.emit('debug', `Prepare heater/cooler service`) : false; this.melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId}`); this.melCloudService.getCharacteristic(Characteristic.Active) .onGet(async () => { const state = this.accessory.power; return state; }) .onSet(async (state) => { try { deviceData.Device.Power = [false, true][state]; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set power: ${state ? 'ON' : 'OFF'}`); } catch (error) { this.emit('warn', `Set power error: ${error}`); }; }); this.melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState) .onGet(async () => { const value = this.accessory.currentOperationMode; return value; }); this.melCloudService.getCharacteristic(Characteristic.TargetHeaterCoolerState) .setProps({ minValue: this.accessory.operationModeSetPropsMinValue, maxValue: this.accessory.operationModeSetPropsMaxValue, validValues: this.accessory.operationModeSetPropsValidValues }) .onGet(async () => { const value = this.accessory.targetOperationMode; //1 = HEAT, 2 = DRY 3 = COOL, 7 = FAN, 8 = AUTO return value; }) .onSet(async (value) => { try { switch (value) { case 0: //AUTO - AUTO deviceData.Device.OperationMode = autoDryFanMode; break; case 1: //HEAT - HEAT deviceData.Device.OperationMode = heatDryFanMode; break; case 2: //COOL - COOL deviceData.Device.OperationMode = coolDryFanMode; break; }; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.OperationModeSetTemperature; await this.melCloudAta.send(deviceData, this.displayMode); const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode]; const info = this.disableLogInfo ? false : this.emit('message', `Set operation mode: ${operationModeText}`); } catch (error) { this.emit('warn', `Set operation mode error: ${error}`); }; }); this.melCloudService.getCharacteristic(Characteristic.CurrentTemperature) .onGet(async () => { const value = this.accessory.roomTemperature; return value; }); if (modelSupportsFanSpeed) { this.melCloudService.getCharacteristic(Characteristic.RotationSpeed) .setProps({ minValue: 0, maxValue: this.accessory.fanSpeedSetPropsMaxValue, minStep: 1 }) .onGet(async () => { const value = this.accessory.fanSpeed; //AUTO, 1, 2, 3, 4, 5, 6, OFF return value; }) .onSet(async (value) => { try { let fanSpeedModeText = ''; switch (numberOfFanSpeeds) { case 2: //Fan speed mode 2 fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 0][value] : [7, 1, 2][value]; deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 0][value] : [1, 1, 2][value]; break; case 3: //Fan speed mode 3 fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 0][value] : [7, 1, 2, 3][value]; deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 0][value] : [1, 1, 2, 3][value]; break; case 4: //Fan speed mode 4 fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 0][value] : [7, 1, 2, 3, 4][value]; deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 0][value] : [1, 1, 2, 3, 4][value]; break; case 5: //Fan speed mode 5 5; fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 0][value] : [7, 1, 2, 3, 4, 5][value]; deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 5, 0][value] : [1, 1, 2, 3, 4, 5][value]; break; case 6: //Fan speed mode 6 fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 6, 0][value] : [7, 1, 2, 3, 4, 5, 6][value]; deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 5, 6, 0][value] : [1, 1, 2, 3, 4, 5, 6][value]; break; }; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetFanSpeed; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set fan speed mode: ${AirConditioner.FanSpeed[fanSpeedModeText]}`); } catch (error) { this.emit('warn', `Set fan speed mode error: ${error}`); }; }); }; if (swingFunction) { this.melCloudService.getCharacteristic(Characteristic.SwingMode) .onGet(async () => { //Vane Horizontal: Auto, 1, 2, 3, 4, 5, 6, 12 = Swing //Vertical: Auto, 1, 2, 3, 4, 5, 7 = Swing const value = this.accessory.swingMode; return value; }) .onSet(async (value) => { try { deviceData.Device.VaneHorizontal = value ? 12 : 0; deviceData.Device.VaneVertical = value ? 7 : 0; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneVerticalVaneHorizontal; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set air direction mode: ${AirConditioner.AirDirection[value]}`); } catch (error) { this.emit('warn', `Set vane swing mode error: ${error}`); }; }); }; this.melCloudService.getCharacteristic(Characteristic.CoolingThresholdTemperature) .setProps({ minValue: this.accessory.minTempCoolDry, maxValue: this.accessory.maxTempCoolDry, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = this.accessory.operationMode === 8 ? this.accessory.defaultCoolingSetTemperature : this.accessory.setTemperature; return value; }) .onSet(async (value) => { try { deviceData.Device.DefaultCoolingSetTemperature = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`); } catch (error) { this.emit('warn', `Set cooling threshold temperature error: ${error}`); }; }); if (modelSupportsHeat) { this.melCloudService.getCharacteristic(Characteristic.HeatingThresholdTemperature) .setProps({ minValue: this.accessory.minTempHeat, maxValue: this.accessory.maxTempHeat, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = this.accessory.operationMode === 8 ? this.accessory.defaultHeatingSetTemperature : this.accessory.setTemperature; return value; }) .onSet(async (value) => { try { deviceData.Device.DefaultHeatingSetTemperature = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`); } catch (error) { this.emit('warn', `Set heating threshold temperature error: ${error}`); }; }); }; this.melCloudService.getCharacteristic(Characteristic.LockPhysicalControls) .onGet(async () => { const value = this.accessory.lockPhysicalControl; return value; }) .onSet(async (value) => { try { value = value ? true : false; deviceData.Device.ProhibitSetTemperature = value; deviceData.Device.ProhibitOperationMode = value; deviceData.Device.ProhibitPower = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set local physical controls: ${value ? 'LOCK' : 'UNLOCK'}`); } catch (error) { this.emit('warn', `Set lock physical controls error: ${error}`); }; }); this.melCloudService.getCharacteristic(Characteristic.TemperatureDisplayUnits) .onGet(async () => { const value = this.accessory.useFahrenheit; return value; }) .onSet(async (value) => { try { accountInfo.UseFahrenheit = [false, true][value]; await this.melCloud.send(accountInfo); this.accessory.useFahrenheit = value; const info = this.disableLogInfo ? false : this.emit('message', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`); } catch (error) { this.emit('warn', `Set temperature display unit error: ${error}`); }; }); accessory.addService(this.melCloudService); break; case 2: //Thermostat const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare thermostat service`) : false; this.melCloudService = new Service.Thermostat(serviceName, `Thermostat ${deviceId}`); this.melCloudService.getCharacteristic(Characteristic.CurrentHeatingCoolingState) .onGet(async () => { const value = this.accessory.currentOperationMode; return value; }); this.melCloudService.getCharacteristic(Characteristic.TargetHeatingCoolingState) .setProps({ minValue: this.accessory.operationModeSetPropsMinValue, maxValue: this.accessory.operationModeSetPropsMaxValue, validValues: this.accessory.operationModeSetPropsValidValues }) .onGet(async () => { const value = this.accessory.targetOperationMode; //1 = HEAT, 2 = DRY 3 = COOL, 7 = FAN, 8 = AUTO return value; }) .onSet(async (value) => { try { switch (value) { case 0: //OFF - POWER OFF deviceData.Device.Power = false; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power; break; case 1: //HEAT - HEAT deviceData.Device.Power = true; deviceData.Device.OperationMode = heatDryFanMode; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 2: //COOL - COOL deviceData.Device.Power = true; deviceData.Device.OperationMode = coolDryFanMode; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature break; case 3: //AUTO - AUTO deviceData.Device.Power = true; deviceData.Device.OperationMode = autoDryFanMode; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; }; await this.melCloudAta.send(deviceData, this.displayMode); const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode]; const info = this.disableLogInfo ? false : this.emit('message', `Set operation mode: ${operationModeText}`); } catch (error) { this.emit('warn', `Set operation mode error: ${error}`); }; }); this.melCloudService.getCharacteristic(Characteristic.CurrentTemperature) .onGet(async () => { const value = this.accessory.roomTemperature; return value; }); this.melCloudService.getCharacteristic(Characteristic.TargetTemperature) .setProps({ minValue: this.accessory.minTempHeat, maxValue: this.accessory.maxTempHeat, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = this.accessory.setTemperature; return value; }) .onSet(async (value) => { try { deviceData.Device.SetTemperature = value; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `Set temperature: ${value}${this.accessory.temperatureUnit}`); } catch (error) { this.emit('warn', `Set temperature error: ${error}`); }; }); this.melCloudService.getCharacteristic(Characteristic.TemperatureDisplayUnits) .onGet(async () => { const value = this.accessory.useFahrenheit; return value; }) .onSet(async (value) => { try { accountInfo.UseFahrenheit = [false, true][value]; await this.melCloud.send(accountInfo); this.accessory.useFahrenheit = value; const info = this.disableLogInfo ? false : this.emit('message', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`); } catch (error) { this.emit('warn', `Set temperature display unit error: ${error}`); }; }); accessory.addService(this.melCloudService); break; }; //temperature sensor services if (this.temperatureSensor && this.accessory.roomTemperature !== null) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare room temperature sensor service`) : false; this.roomTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Room`, `Room Temperature Sensor ${deviceId}`); this.roomTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName); this.roomTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Room`); this.roomTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature) .setProps({ minValue: -35, maxValue: 150, minStep: 0.5 }) .onGet(async () => { const state = this.accessory.roomTemperature; return state; }) accessory.addService(this.roomTemperatureSensorService); }; if (this.temperatureSensorOutdoor && hasOutdoorTemperature && this.accessory.outdoorTemperature !== null) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare outdoor temperature sensor service`) : false; this.outdoorTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Outdoor`, `Outdoor Temperature Sensor ${deviceId}`); this.outdoorTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName); this.outdoorTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Outdoor`); this.outdoorTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature) .setProps({ minValue: -35, maxValue: 150, minStep: 0.5 }) .onGet(async () => { const state = this.accessory.outdoorTemperature; return state; }) accessory.addService(this.outdoorTemperatureSensorService); }; //presets services if (this.presetsConfiguredCount > 0) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare presets services`) : false; this.presetsServices = []; for (let i = 0; i < this.presetsConfiguredCount; i++) { const preset = this.presetsConfigured[i]; const presetData = presetsOnServer.find(p => p.ID === preset.Id); //get preset name const presetName = preset.name; //get preset name prefix const presetNamePrefix = preset.namePrefix; const serviceName = presetNamePrefix ? `${accessoryName} ${presetName}` : presetName; const serviceType = preset.serviceType; const characteristicType = preset.characteristicType; const presetService = new serviceType(serviceName, `Preset ${deviceId} ${i}`); presetService.addOptionalCharacteristic(Characteristic.ConfiguredName); presetService.setCharacteristic(Characteristic.ConfiguredName, serviceName); presetService.getCharacteristic(characteristicType) .onGet(async () => { const state = preset.state; return state; }) .onSet(async (state) => { try { switch (state) { case true: preset.previousSettings = deviceData.Device; deviceData.Device.Power = presetData.Power; deviceData.Device.OperationMode = presetData.OperationMode; deviceData.Device.SetTemperature = presetData.SetTemperature; deviceData.Device.VaneHorizontalDirection = presetData.VaneHorizontal; deviceData.Device.VaneVerticalDirection = presetData.VaneVertical; deviceData.Device.FanSpeed = presetData.FanSpeed; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Presets; break; case false: deviceData.Device.Power = preset.previousSettings.Power; deviceData.Device.OperationMode = preset.previousSettings.OperationMode; deviceData.Device.SetTemperature = preset.previousSettings.SetTemperature; deviceData.Device.VaneHorizontalDirection = preset.previousSettings.VaneHorizontalDirection; deviceData.Device.VaneVerticalDirection = preset.previousSettings.VaneVerticalDirection; deviceData.Device.FanSpeed = preset.previousSettings.FanSpeed; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Presets; break; }; await this.melCloudAta.send(deviceData, this.displayMode); const info = this.disableLogInfo ? false : this.emit('message', `${state ? 'Set:' : 'Unset:'} ${presetName}`); } catch (error) { this.emit('warn', `Set preset error: ${error}`); }; }); this.presetsServices.push(presetService); accessory.addService(presetService); }; }; //buttons services if (this.buttonsConfiguredCount > 0) { const debug = this.enableDebugMode ? this.emit('debug', `Prepare buttons/sensors services`) : false; this.buttonsServices = []; for (let i = 0; i < this.buttonsConfiguredCount; i++) { const button = this.buttonsConfigured[i]; //get button mode const mode = button.mode; //get button name const buttonName = button.name; //get button name prefix const buttonNamePrefix = button.namePrefix; const serviceName = buttonNamePrefix ? `${accessoryName} ${buttonName}` : buttonName; const serviceType = button.serviceType; const characteristicType = button.characteristicType; const buttonService = new serviceType(serviceName, `Button ${deviceId} ${i}`); buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); buttonService.getCharacteristic(characteristicType) .onGet(async () => { const state = button.state; return state; }) .onSet(async (state) => { try { switch (mode) { case 0: //POWER ON,OFF deviceData.Device.Power = state; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power; break; case 1: //OPERATING MODE HEAT button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 1 : button.previousValue === 9 ? 1 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 2: //OPERATING MODE DRY button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 2 : button.previousValue === 10 ? 2 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break case 3: //OPERATING MODE COOL button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 3 : button.previousValue === 11 ? 3 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 4: //OPERATING MODE FAN button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 7 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 5: //OPERATING MODE AUTO button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 8 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 6: //OPERATING MODE PURIFY button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode; deviceData.Device.Power = true; deviceData.Device.OperationMode = state ? 12 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature; break; case 7: //OPERATING MODE DRY CONTROL HIDE deviceData.HideDryModeControl = state; break; case 10: //VANE H SWING MODE AUTO button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection; deviceData.Device.Power = true; deviceData.Device.VaneHorizontalDirection = state ? 0 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal; break; case 11: //VANE H SWING MODE 1 button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection; deviceData.Device.Power = true; deviceData.Device.VaneHorizontalDirection = state ? 1 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal; break; case 12: //VANE H SWING MODE 2 button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection; deviceData.Device.Power = true; deviceData.Device.VaneHorizontalDirection = state ? 2 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal; break; case 13: //VANE H SWING MODE 3 button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection; deviceData.Device.Power = true; deviceData.Device.VaneHorizontalDirection = state ? 3 : button.previousValue; deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal;