UNPKG

homebridge-melcloud-control

Version:

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

745 lines (720 loc) • 140 kB
import EventEmitter from 'events'; import MelCloudAtw from './melcloudatw.js'; import RestFul from './restful.js'; import Mqtt from './mqtt.js'; import { TemperatureDisplayUnits, HeatPump } from './constants.js'; let Accessory, Characteristic, Service, Categories, AccessoryUUID; class DeviceAtw 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.temperatureSensorFlow = device.temperatureSensorFlow || false; this.temperatureSensorReturn = device.temperatureSensorReturn || false; this.temperatureSensorFlowZone1 = device.temperatureSensorFlowZone1 || false; this.temperatureSensorReturnZone1 = device.temperatureSensorFlowWaterTank || false; this.temperatureSensorFlowWaterTank = device.temperatureSensorFlowZone1 || false; this.temperatureSensorReturnWaterTank = device.temperatureSensorReturnWaterTank || false; this.temperatureSensorFlowZone2 = device.temperatureSensorFlowZone2 || false; this.temperatureSensorReturnZone2 = device.temperatureSensorReturnZone2 || false; 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 = { zones: [{}, {}, {}, {}] }; 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 = HeatPump.EffectiveFlags.Power; set = await this.melCloudAtw.send(deviceData); break; case 'OperationMode': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationMode; set = await this.melCloudAtw.send(deviceData); break; case 'OperationModeZone1': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; set = await this.melCloudAtw.send(deviceData); break; case 'OperationModeZone2': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; set = await this.melCloudAtw.send(deviceData); break; case 'SetTemperatureZone1': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2; set = await this.melCloudAtw.send(deviceData); break; case 'SetTemperatureZone2': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2; set = await this.melCloudAtw.send(deviceData); break; case 'SetHeatFlowTemperatureZone1': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; set = await this.melCloudAtw.send(deviceData); break; case 'SetHeatFlowTemperatureZone2': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; set = await this.melCloudAtw.send(deviceData); break; case 'SetCoolFlowTemperatureZone1': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; set = await this.melCloudAtw.send(deviceData); break; case 'SetCoolFlowTemperatureZone2': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; set = await this.melCloudAtw.send(deviceData); break; case 'SetTankWaterTemperature': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature; set = await this.melCloudAtw.send(deviceData); break; case 'ForcedHotWaterMode': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; set = await this.melCloudAtw.send(deviceData); break; case 'EcoHotWater': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.EcoHotWater; set = await this.melCloudAtw.send(deviceData); break; case 'HolidayMode': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.HolidayMode; set = await this.melCloudAtw.send(deviceData); break; case 'ProhibitZone1': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitZone1; set = await this.melCloudAtw.send(deviceData); break; case 'ProhibitZone2': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitZone2; set = await this.melCloudAtw.send(deviceData); break; case 'ProhibitHotWater': deviceData.Device[key] = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitHotWater; set = await this.melCloudAtw.send(deviceData); 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.melCloudAtw.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 zonesCount = this.accessory.zonesCount; const caseHotWater = this.accessory.caseHotWater; const caseZone2 = this.accessory.caseZone2; const heatCoolModes = this.accessory.heatCoolModes; const temperatureSensor = this.temperatureSensor; const temperatureSensorFlow = this.temperatureSensorFlow; const temperatureSensorReturn = this.temperatureSensorReturn; const temperatureSensorFlowZone1 = this.temperatureSensorFlowZone1; const temperatureSensorReturnZone1 = this.temperatureSensorReturnZone1; const temperatureSensorFlowWaterTank = this.temperatureSensorFlowWaterTank; const temperatureSensorReturnWaterTank = this.temperatureSensorReturnWaterTank; const temperatureSensorFlowZone2 = this.temperatureSensorFlowZone2; const temperatureSensorReturnZone2 = this.temperatureSensorReturnZone2; //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_HEATER; 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); //services this.melCloudServices = []; for (let i = 0; i < zonesCount; i++) { const zoneName = this.accessory.zones[i].name const serviceName = `${deviceTypeText} ${accessoryName}: ${zoneName}`; switch (this.displayMode) { case 1: //Heater Cooler const debug = this.enableDebugMode ? this.emit('debug', `Prepare heather/cooler ${zoneName} service`) : false; const melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId} ${i}`); melCloudService.getCharacteristic(Characteristic.Active) .onGet(async () => { const state = this.accessory.power; return state; }) .onSet(async (state) => { try { switch (i) { case 0: //Heat Pump deviceData.Device.Power = [false, true][state]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power; await this.melCloudAtw.send(deviceData); const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set power: ${state ? 'ON' : 'OFF'}`); break; }; } catch (error) { this.emit('warn', `Set power error: ${error}`); }; }); melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState) .onGet(async () => { const value = this.accessory.zones[i].currentOperationMode; return value; }); melCloudService.getCharacteristic(Characteristic.TargetHeaterCoolerState) .setProps({ minValue: this.accessory.zones[i].operationModesSetPropsMinValue, maxValue: this.accessory.zones[i].operationModesSetPropsMaxValue, validValues: this.accessory.zones[i].operationModesSetPropsValidValues }) .onGet(async () => { const value = this.accessory.zones[i].targetOperationMode; return value; }) .onSet(async (value) => { try { let operationModeText = ''; switch (i) { case 0: //Heat Pump - ON, HEAT, COOL switch (value) { case 0: //AUTO deviceData.Device.Power = true; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power; break; case 1: //HEAT deviceData.Device.Power = true; deviceData.Device.UnitStatus = 0; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode; break; case 2: //COOL deviceData.Device.Power = true; deviceData.Device.UnitStatus = 1; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode; break; }; operationModeText = [HeatPump.System[0], HeatPump.System[deviceData.Device.UnitStatus]][this.accessory.power]; break; case 1: //Zone 1 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP switch (value) { case 0: //AUTO - HEAT CURVE deviceData.Device.OperationModeZone1 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT deviceData.Device.OperationModeZone1 = [0, 3][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; case 2: //COOL - HEAT FLOW / COOL FLOW deviceData.Device.OperationModeZone1 = [1, 4][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; }; operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone1]; break; case caseHotWater: //Hot Water - AUTO, HEAT NOW switch (value) { case 0: //AUTO deviceData.Device.ForcedHotWaterMode = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 1: //HEAT deviceData.Device.ForcedHotWaterMode = true; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 2: //COOL deviceData.Device.ForcedHotWaterMode = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break }; operationModeText = deviceData.Device.OperationMode === 1 ? HeatPump.ForceDhw[1] : HeatPump.ForceDhw[deviceData.Device.ForcedHotWaterMode ? 1 : 0]; break; case caseZone2: //Zone 2 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP switch (value) { case 0: //AUTO deviceData.Device.OperationModeZone2 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT deviceData.Device.OperationModeZone2 = [0, 3][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; case 2: //COOL - HEAT FLOW / COOL FLOW deviceData.Device.OperationModeZone2 = [1, 4][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; }; operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone2]; break; }; await this.melCloudAtw.send(deviceData); const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set operation mode: ${operationModeText}`); } catch (error) { this.emit('warn', `${zoneName}, Set operation mode error: ${error}`); }; }); melCloudService.getCharacteristic(Characteristic.CurrentTemperature) .setProps({ minValue: -35, maxValue: 150, minStep: 0.5 }) .onGet(async () => { const value = this.accessory.zones[i].roomTemperature; return value; }); //only for heat/cool, only cool and not for hot water tank if ((heatCoolModes === 0 || heatCoolModes === 2) && i !== caseHotWater) { melCloudService.getCharacteristic(Characteristic.CoolingThresholdTemperature) .setProps({ minValue: this.accessory.zones[i].temperaturesSetPropsMinValue, maxValue: this.accessory.zones[i].temperaturesSetPropsMaxValue, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = this.accessory.zones[i].setTemperature; return value; }) .onSet(async (value) => { try { switch (i) { case 0: //Heat Pump //deviceData.Device.SetTemperatureZone1 = value; //deviceData.Device.EffectiveFlags = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; break; case 1: //Zone 1 switch (this.accessory.zones[i].operationMode) { case 1: //HEAT FLOW deviceData.Device.SetHeatFlowTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; break; case 4: //COOL FLOW deviceData.Device.SetCoolFlowTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; break; default: deviceData.Device.SetTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone1; break }; break; case caseHotWater: //Hot Water deviceData.Device.SetTankWaterTemperature = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature; break; case caseZone2: //Zone 2 switch (this.accessory.zones[i].operationMode) { case 1: //HEAT FLOW deviceData.Device.SetHeatFlowTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; break; case 4: //COOL FLOW deviceData.Device.SetCoolFlowTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; break; default: deviceData.Device.SetTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2; break }; break; }; const set = i > 0 ? await this.melCloudAtw.send(deviceData) : false; const info = this.disableLogInfo || i === 0 ? false : this.emit('message', `${zoneName}, Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`); } catch (error) { this.emit('warn', `${zoneName}, Set cooling threshold temperature error: ${error}`); }; }); }; //device can heat/cool or only heat if (heatCoolModes === 0 || heatCoolModes === 1) { melCloudService.getCharacteristic(Characteristic.HeatingThresholdTemperature) .setProps({ minValue: this.accessory.zones[i].temperaturesSetPropsMinValue, maxValue: this.accessory.zones[i].temperaturesSetPropsMaxValue, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = this.accessory.zones[i].setTemperature; return value; }) .onSet(async (value) => { try { switch (i) { case 0: //Heat Pump //deviceData.Device.SetTemperatureZone1 = value; //deviceData.Device.EffectiveFlags = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; break; case 1: //Zone 1 switch (this.accessory.zones[i].operationMode) { case 1: //HEAT FLOW deviceData.Device.SetHeatFlowTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; break; case 4: //COOL FLOW deviceData.Device.SetCoolFlowTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; break; default: deviceData.Device.SetTemperatureZone1 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone1; break }; break; case caseHotWater: //Hot Water deviceData.Device.SetTankWaterTemperature = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature; break; case caseZone2: //Zone 2 switch (this.accessory.zones[i].operationMode) { case 1: //HEAT FLOW deviceData.Device.SetHeatFlowTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; break; case 4: //COOL FLOW deviceData.Device.SetCoolFlowTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; break; default: deviceData.Device.SetTemperatureZone2 = value; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2; break }; break; }; const set = i > 0 ? await this.melCloudAtw.send(deviceData) : false; const info = this.disableLogInfo || i === 0 ? false : this.emit('message', `${zoneName}, Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`); } catch (error) { this.emit('warn', `${zoneName}, Set heating threshold temperature error: ${error}`); }; }); }; melCloudService.getCharacteristic(Characteristic.LockPhysicalControls) .onGet(async () => { const value = this.accessory.zones[i].lockPhysicalControl; return value; }) .onSet(async (value) => { try { value = value ? true : false; switch (i) { case 0: //Heat Pump deviceData.Device.ProhibitZone1 = value; deviceData.Device.ProhibitHotWater = value; deviceData.Device.ProhibitZone2 = value; HeatPump.EffectiveFlags.ProhibitHeatingZone1 + HeatPump.EffectiveFlags.ProhibitHotWater + HeatPump.EffectiveFlags.ProhibitHeatingZone2; break; case 1: //Zone 1 deviceData.Device.ProhibitZone1 = value; HeatPump.EffectiveFlags.ProhibitHeatingZone1; break; case caseHotWater: //Hot Water deviceData.Device.ProhibitHotWater = value; HeatPump.EffectiveFlags.ProhibitHotWater; break; case caseZone2: //Zone 2 deviceData.Device.ProhibitZone2 = value; HeatPump.EffectiveFlags.ProhibitHeatingZone2; break; }; await this.melCloudAtw.send(deviceData); const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set lock physical controls: ${value ? 'LOCK' : 'UNLOCK'}`); } catch (error) { this.emit('warn', `${zoneName}, Set lock physical controls error: ${error}`); }; }); 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}`); }; }); this.melCloudServices.push(melCloudService); accessory.addService(melCloudService); break; case 2: //Thermostat const debug3 = this.enableDebugMode ? this.emit('debug', `Prepare thermostat ${zoneName} service`) : false; const melCloudServiceT = new Service.Thermostat(serviceName, `Thermostat ${deviceId} ${i}`); melCloudServiceT.getCharacteristic(Characteristic.CurrentHeatingCoolingState) .onGet(async () => { const value = this.accessory.zones[i].currentOperationMode; return value; }); melCloudServiceT.getCharacteristic(Characteristic.TargetHeatingCoolingState) .setProps({ minValue: this.accessory.zones[i].operationModesSetPropsMinValue, maxValue: this.accessory.zones[i].operationModesSetPropsMaxValue, validValues: this.accessory.zones[i].operationModesSetPropsValidValues }) .onGet(async () => { const value = this.accessory.zones[i].targetOperationMode; return value; }) .onSet(async (value) => { try { let operationModeText = ''; switch (i) { case 0: //Heat Pump - HEAT, COOL switch (value) { case 0: //OFF deviceData.Device.Power = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power; break; case 1: //HEAT deviceData.Device.Power = true; deviceData.Device.UnitStatus = 0; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode; break; case 2: //COOL deviceData.Device.Power = true; deviceData.Device.UnitStatus = 1; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode; break; case 3: //AUTO deviceData.Device.Power = true; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power; break; }; operationModeText = [HeatPump.System[0], HeatPump.System[deviceData.Device.UnitStatus]][this.accessory.power]; break; case 1: //Zone 1 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP switch (value) { case 0: //OFF - HEAT CURVE deviceData.Device.OperationModeZone1 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT deviceData.Device.OperationModeZone1 = [0, 3][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; case 2: //COOL - HEAT FLOW / COOL FLOW deviceData.Device.OperationModeZone1 = [1, 4][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; case 3: //AUTO - HEAT CURVE deviceData.Device.OperationModeZone1 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1; break; }; operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone1]; break; case caseHotWater: //Hot Water - AUTO, HEAT NOW switch (value) { case 0: //OFF deviceData.Device.ForcedHotWaterMode = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 1: //HEAT deviceData.Device.ForcedHotWaterMode = true; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 2: //COOL deviceData.Device.ForcedHotWaterMode = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 3: //AUTO deviceData.Device.ForcedHotWaterMode = false; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; }; operationModeText = deviceData.Device.OperationMode === 1 ? HeatPump.ForceDhw[1] : HeatPump.ForceDhw[deviceData.Device.ForcedHotWaterMode ? 1 : 0]; break; case caseZone2: //Zone 2 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP switch (value) { case 0: //OFF - HEAT CURVE deviceData.Device.OperationModeZone2 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT deviceData.Device.OperationModeZone2 = [0, 3][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; case 2: //COOL - HEAT FLOW / COOL FLOW deviceData.Device.OperationModeZone2 = [1, 4][this.unitStatus]; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; case 3: //AUTO - HEAT CURVE deviceData.Device.OperationModeZone2 = 2; deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2; break; }; operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone2];