UNPKG

homebridge-melcloud-control

Version:

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

763 lines (731 loc) • 187 kB
import EventEmitter from 'events'; import MelCloudAtw from './melcloudatw.js'; import RestFul from './restful.js'; import Mqtt from './mqtt.js'; import Functions from './functions.js'; import { TemperatureDisplayUnits, HeatPump, DeviceType } from './constants.js'; let Accessory, Characteristic, Service, Categories, AccessoryUUID; class DeviceAtw extends EventEmitter { constructor(api, account, device, presets, schedules, scenes, buttons, defaultTempsFile, melCloudClass, melCloudAccountData, melCloudDeviceData) { super(); Accessory = api.platformAccessory; Characteristic = api.hap.Characteristic; Service = api.hap.Service; Categories = api.hap.Categories; AccessoryUUID = api.hap.uuid; //account config this.account = account; this.accountType = account.type; this.accountName = account.name; this.accountTypeMelCloud = account.type === 'melcloud'; this.logDeviceInfo = account.log?.deviceInfo || false; this.logInfo = account.log?.info || false; this.logWarn = account.log?.warn || false; this.logDebug = account.log?.debug || false; this.logError = account.log?.error || false; //device config this.device = device; this.deviceId = device.id; this.deviceName = device.name; this.deviceTypeString = DeviceType[device.type]; this.displayType = device.displayType; this.hideZone = device.hideZone; this.temperatureOutdoorSensor = device.temperatureOutdoorSensor || false; this.temperatureRoomSensor = device.temperatureRoomSensor || false; this.temperatureFlowSensor = device.temperatureFlowSensor || false; this.temperatureReturnSensor = device.temperatureReturnSensor || false; this.temperatureRoomZone1Sensor = device.temperatureRoomZone1Sensor || false; this.temperatureFlowZone1Sensor = device.temperatureFlowZone1Sensor || false; this.temperatureReturnZone1Sensor = device.temperatureReturnZone1Sensor || false; this.temperatureWaterTankSensor = device.temperatureWaterTankSensor || false; this.temperatureFlowWaterTankSensor = device.temperatureFlowWaterTankSensor || false; this.temperatureReturnWaterTankSensor = device.temperatureReturnWaterTankSensor || false; this.temperatureRoomZone2Sensor = device.temperatureRoomZone2Sensor || false; this.temperatureFlowZone2Sensor = device.temperatureFlowZone2Sensor || false; this.temperatureReturnZone2Sensor = device.temperatureReturnZone2Sensor || false; this.inStandbySensor = device.inStandbySensor || false; this.connectSensor = device.connectSensor || false; this.errorSensor = device.errorSensor || false; this.frostProtectionSupport = device.frostProtectionSupport || false; this.holidayModeSupport = device.holidayModeSupport || false; this.presets = presets; this.schedules = schedules; this.scenes = scenes; this.buttons = buttons; //files this.defaultTempsFile = defaultTempsFile; //melcloud this.melCloudClass = melCloudClass; this.melCloudDeviceData = melCloudDeviceData; this.melCloudAccountData = melCloudAccountData; //external integrations this.restFul = account.restFul ?? {}; this.restFul.port = device.restFulPort; this.restFulConnected = false; this.mqtt = account.mqtt ?? {}; this.mqttConnected = false; const serviceType = [null, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor, null]; const characteristicType = [null, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState, null]; //presets configured for (const preset of this.presets) { preset.serviceType = serviceType[preset.displayType]; preset.characteristicType = characteristicType[preset.displayType]; preset.state = false; preset.previousSettings = {}; } //schedules configured for (const schedule of this.schedules) { schedule.serviceType = serviceType[schedule.displayType]; schedule.characteristicType = characteristicType[schedule.displayType]; schedule.state = false; } //scenes configured for (const scene of this.scenes) { scene.serviceType = serviceType[scene.displayType]; scene.characteristicType = characteristicType[scene.displayType]; scene.state = false; } //buttons configured for (const button of this.buttons) { button.serviceType = serviceType[button.displayType]; button.characteristicType = characteristicType[button.displayType]; button.state = false; button.previousValue = null; } this.functions = new Functions(this.logWarn, this.logError, this.logDebug) .on('warn', warn => this.emit('warn', warn)) .on('error', error => this.emit('error', error)) .on('debug', debug => this.emit('debug', debug)); //other variables this.displayDeviceInfo = true; this.deviceData = {}; this.accessory = {}; } async setOverExternalIntegration(integration, deviceData, key, value) { try { const accountTypeMelCloud = this.accountTypeMelCloud; let payload = {}; let flag = null; switch (key) { case 'Power': payload.power = value; break; case 'OperationModeZone1': payload.operationModeZone1 = value; flag = HeatPump.EffectiveFlags.OperationModeZone1; break; case 'SetTemperatureZone1': payload.setTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone1; break; case 'SetHeatFlowTemperatureZone1': payload.setHeatFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; break; case 'SetCoolFlowTemperatureZone1': payload.setCoolFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; break; case 'ProhibitZone1': if (!accountTypeMelCloud) return; payload.prohibitZone1 = value; flag = HeatPump.EffectiveFlags.ProhibitHeatingZone1; break; case 'ForcedHotWaterMode': payload.forcedHotWaterMode = value; flag = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 'EcoHotWater': if (!accountTypeMelCloud) return; payload.ecoHotWater = value; flag = HeatPump.EffectiveFlags.EcoHotWater; break; case 'SetTankWaterTemperature': payload.setTankWaterTemperature = value; flag = HeatPump.EffectiveFlags.SetTankWaterTemperature; break; case 'ProhibitHotWater': payload.prohibitHotWater = value; flag = HeatPump.EffectiveFlags.ProhibitHotWater; break; case 'OperationModeZone2': payload.operationModeZone2 = value; flag = HeatPump.EffectiveFlags.OperationModeZone2; break; case 'SetTemperatureZone2': payload.setTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone2; break; case 'SetHeatFlowTemperatureZone2': payload.setHeatFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; break; case 'SetCoolFlowTemperatureZone2': payload.setCoolFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; break; case 'ProhibitZone2': if (!accountTypeMelCloud) return; payload.prohibitZone2 = value; flag = HeatPump.EffectiveFlags.ProhibitHeatingZone2; break; case 'FrostProtection': if (accountTypeMelCloud) return; payload.enabled = value; flag = 'frostprotection'; break; case 'Schedules': if (accountTypeMelCloud) return; payload.enabled = value; flag = 'schedule'; break; case 'HolidayMode': if (accountTypeMelCloud) { payload.holidayMode = value; flag = HeatPump.EffectiveFlags.HolidayMode; } if (!accountTypeMelCloud) { payload.enabled = value; flag = 'holidaymode'; } break; default: this.emit('warn', `${integration}, received unknown key: ${key}, value: ${value}`); return; }; return await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, flag); } catch (error) { throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`); }; } async externalIntegrations() { //RESTFul server const restFulEnabled = this.restFul.enable || false; if (restFulEnabled) { try { await new Promise((resolve) => { const timer = setTimeout(resolve, 5000); this.restFul1 = new RestFul({ port: this.device.restFul.port, logWarn: this.logWarn, logDebug: this.logDebug, }) .once('connected', (success) => { clearTimeout(timer); this.restFulConnected = true; this.emit('success', success); resolve(); }) .on('set', async (key, value) => { try { await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value); } catch (error) { if (this.logWarn) this.emit('warn', `RESTFul set error: ${error}`); }; }) .on('debug', (debug) => this.emit('debug', debug)) .on('warn', (warn) => this.emit('warn', warn)) .on('error', (error) => this.emit('error', error)); }); } catch (error) { this.emit('warn', `RESTFul integration start error: ${error}`); } } const mqttEnabled = this.mqtt.enable || false; if (mqttEnabled) { try { await new Promise((resolve) => { const timer = setTimeout(resolve, 10000); this.mqtt1 = new Mqtt({ host: this.mqtt.host, port: this.mqtt.port || 1883, clientId: this.mqtt.clientId ? `melcloud_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `melcloud_${Math.random().toString(16).slice(3)}`, prefix: this.mqtt.prefix ? `melcloud/${this.mqtt.prefix}/${this.deviceTypeString}/${this.deviceName}` : `melcloud/${this.deviceTypeString}/${this.deviceName}`, user: this.mqtt.auth?.user, passwd: this.mqtt.auth?.passwd, logWarn: this.logWarn, logDebug: this.logDebug }) .once('connected', (success) => { clearTimeout(timer); this.mqttConnected = true; this.emit('success', success); resolve(); }) .on('subscribed', (success) => { this.emit('success', success); }) .on('set', async (key, value) => { try { await this.setOverExternalIntegration('MQTT', this.deviceData, key, value); } catch (error) { if (this.logWarn) this.emit('warn', `MQTT set, error: ${error}`); }; }) .on('debug', (debug) => this.emit('debug', debug)) .on('warn', (warn) => this.emit('warn', warn)) .on('error', (error) => this.emit('error', error)); }); } catch (error) { this.emit('warn', `MQTT integration start error: ${error}`); } }; return true; } //prepare accessory async prepareAccessory() { try { const accountTypeMelCloud = this.accountTypeMelCloud; const deviceData = this.deviceData; const deviceId = this.deviceId; const deviceTypeString = this.deviceTypeString; const deviceName = this.deviceName; const accountName = this.accountName; const presetsOnServer = this.accessory.presets; const schedulesOnServer = this.accessory.schedules; const scenesOnServer = this.accessory.scenes; const zonesCount = this.accessory.zonesCount; const caseHeatPump = this.accessory.caseHeatPump; const caseZone1 = this.accessory.caseZone1; const caseHotWater = this.accessory.caseHotWater; const caseZone2 = this.accessory.caseZone2; const heatCoolModes = this.accessory.heatCoolModes; const zonesSensorsCount = this.accessory.sensorsCount; const caseHeatPumpSensor = this.accessory.caseHeatPumpSensor; const caseZone1Sensor = this.accessory.caseZone1Sensor; const caseHotWaterSensor = this.accessory.caseHotWaterSensor; const caseZone2Sensor = this.accessory.caseZone2Sensor; const supportsOutdoorTemperature = this.accessory.supportsOutdoorTemperature; //accessory if (this.logDebug) this.emit('debug', `Prepare accessory`); const accessoryName = deviceName; const accessoryUUID = AccessoryUUID.generate(accountName + deviceId.toString()); const accessoryCategory = Categories.AIR_HEATER; const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); //information service if (this.logDebug) this.emit('debug', `Prepare information service`); this.informationService = 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 if (zonesCount > 0) { this.melCloudServices = []; this.accessory.zones.forEach((zone, i) => { const zoneName = zone.name const serviceName = `${deviceTypeString} ${accessoryName}: ${zoneName}`; switch (this.displayType) { case 1: //Heater Cooler if (this.logDebug) this.emit('debug', `Prepare heater/cooler ${zoneName} service`); const melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId} ${i}`); if (i === caseHeatPump) melCloudService.setPrimaryService(true); melCloudService.getCharacteristic(Characteristic.Active) .onGet(async () => { const state = zone.state; return state; }) .onSet(async (state) => { if (i !== caseHeatPump) return; // Only heat pump try { const payload = { power: state ? true : false }; if (this.logInfo) this.emit('info', `${zoneName}, Set power: ${state ? 'On' : 'Off'}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload); } catch (error) { if (this.logWarn) this.emit('warn', `Set power error: ${error}`); }; }); melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState) .onGet(async () => { const value = zone.currentOperationMode; return value; }); melCloudService.getCharacteristic(Characteristic.TargetHeaterCoolerState) .setProps({ minValue: zone.operationModesSetPropsMinValue, maxValue: zone.operationModesSetPropsMaxValue, validValues: zone.operationModesSetPropsValidValues }) .onGet(async () => { const value = zone.targetOperationMode; return value; }) .onSet(async (value) => { try { const payload = {}; let operationModeText = ''; let flag = null; switch (i) { case caseHeatPump: //Heat Pump - Operation Mode - IDLE, HOT WATER, HEATING, COOLING, HOT WATER STORAGE, FREEZE STAT, LEGIONELLA, HEATING ECO, MODE 1, MODE 2, MODE 3, HEATING UP // Unit Status - HEAT, COOL switch (value) { case 0: //AUTO Power OFF payload.power = false; break; case 1: //HEAT if (accountTypeMelCloud) deviceData.Device.UnitStatus = 0; payload.power = true; //payload.operationMode = 2; break; case 2: //COOL if (accountTypeMelCloud) deviceData.Device.UnitStatus = 1; payload.power = true; //payload.operationMode = 3; break; }; operationModeText = HeatPump.OperationModeHeatPumpMapEnumToStringInfo[value]; break; case caseZone1: //Zone 1 - HEAT ROOM, HEAT FLOW, HEAT CURVE, COOL ROOM, COOL FLOW, FLOOR DRY UP, IDLE switch (value) { case 0: //AUTO - HEAT CURVE / FLOOR DRY UP payload.operationModeZone1 = [2, 5][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone1; break; case 1: //HEAT - HEAT ROOM / COOL ROOM payload.operationModeZone1 = [0, 3][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone1; break; case 2: //COOL - HEAT FLOW / COOL FLOW payload.operationModeZone1 = [1, 4][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone1; break; }; operationModeText = HeatPump.OperationModeZoneMapEnumToStringInfo[payload.operationModeZone1]; break; case caseHotWater: //Hot Water - AUTO, HEAT NOW switch (value) { case 0: //AUTO //payload.forcedHotWaterMode = false; //flag = HeatPump.EffectiveFlags.ForcedHotWaterMode; return; case 1: //HEAT payload.forcedHotWaterMode = true; flag = HeatPump.EffectiveFlags.ForcedHotWaterMode; break; case 2: //COOL //payload.forcedHotWaterMode = false; //flag = HeatPump.EffectiveFlags.ForcedHotWaterMode; return }; operationModeText = HeatPump.ForceDhwMapEnumToStringInfo[payload.forcedHotWaterMode ? 1 : 0]; break; case caseZone2: //Zone 2 - HEAT ROOM, HEAT FLOW, HEAT CURVE, COOL ROOM, COOL FLOW, FLOOR DRY UP, IDLE switch (value) { case 0: //AUTO - HEAT CURVE / FLOOR DRY UP payload.operationModeZone2 = [2, 5][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone2; break; case 1: //HEAT - HEAT ROOM / COOL ROOM payload.operationModeZone2 = [0, 3][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone2; break; case 2: //COOL - HEAT FLOW / COOL FLOW payload.operationModeZone2 = [1, 4][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone2; break; }; operationModeText = HeatPump.OperationModeZoneMapEnumToStringInfo[payload.operationModeZone2]; break; }; if (this.logInfo) this.emit('info', `${zoneName}, Set operation mode: ${operationModeText}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, flag); } catch (error) { if (this.logWarn) this.emit('warn', `${zoneName}, Set operation mode error: ${error}`); }; }); melCloudService.getCharacteristic(Characteristic.CurrentTemperature) .onGet(async () => { const value = zone.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: zone.temperaturesSetPropsMinValue, maxValue: zone.temperaturesSetPropsMaxValue, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = zone.setTemperature; return value; }) .onSet(async (value) => { try { const payload = {}; let flag = null; switch (this.accountType) { case 'melcloud': //Melcloud switch (i) { case caseHeatPump: //Heat Pump //flag = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; return; case caseZone1: //Zone 1 switch (zone.operationModeRaw) { case 1: //HEAT FLOW payload.setHeatFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; break; case 4: //COOL FLOW payload.setCoolFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; break; default: payload.setTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone1; break }; break; case caseHotWater: //Hot Water payload.setTankWaterTemperature = value; flag = HeatPump.EffectiveFlags.SetTankWaterTemperature; break; case caseZone2: //Zone 2 switch (zone.operationModeRaw) { case 1: //HEAT FLOW payload.setHeatFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; break; case 4: //COOL FLOW payload.setCoolFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; break; default: payload.setTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone2; break }; break; }; break; case 'melcloudhome': switch (i) { case caseHeatPump: //Heat Pump //flag = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; return; case caseZone1: //Zone 1 payload.setTemperatureZone1 = value; break; case caseHotWater: //Hot Water payload.setTankWaterTemperature = value; break; case caseZone2: //Zone 2 payload.setTemperatureZone2 = value; break; }; break; default: if (this.logWarn) this.emit('warn', `Received unknown account type: ${this.accountType}`); return; } if (this.logInfo) this.emit('info', `${zoneName}, Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, flag); } catch (error) { if (this.logWarn) 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: zone.temperaturesSetPropsMinValue, maxValue: zone.temperaturesSetPropsMaxValue, minStep: this.accessory.temperatureIncrement }) .onGet(async () => { const value = zone.setTemperature; return value; }) .onSet(async (value) => { try { const payload = {}; let flag = null; switch (this.accountType) { case 'melcloud': //Melcloud switch (i) { case caseHeatPump: //Heat Pump //flag = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; return; case caseZone1: //Zone 1 switch (zone.operationModeRaw) { case 1: //HEAT FLOW payload.setHeatFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1; break; case 4: //COOL FLOW payload.setCoolFlowTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1; break; default: payload.setTemperatureZone1 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone1; break }; break; case caseHotWater: //Hot Water payload.setTankWaterTemperature = value; flag = HeatPump.EffectiveFlags.SetTankWaterTemperature; break; case caseZone2: //Zone 2 switch (zone.operationModeRaw) { case 1: //HEAT FLOW payload.setHeatFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2; break; case 4: //COOL FLOW payload.setCoolFlowTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2; break; default: payload.setTemperatureZone2 = value; flag = HeatPump.EffectiveFlags.SetTemperatureZone2; break }; break; }; break; case 'melcloudhome': switch (i) { case caseHeatPump: //Heat Pump //flag = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1; return; case caseZone1: //Zone 1 payload.setTemperatureZone1 = value; break; case caseHotWater: //Hot Water payload.setTankWaterTemperature = value; break; case caseZone2: //Zone 2 payload.setTemperatureZone2 = value; break; }; break; default: if (this.logWarn) this.emit('warn', `Received unknown account type: ${this.accountType}`); return; } if (this.logInfo) this.emit('info', `${zoneName}, Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, flag); } catch (error) { if (this.logWarn) this.emit('warn', `${zoneName}, Set heating threshold temperature error: ${error}`); }; }); }; melCloudService.getCharacteristic(Characteristic.LockPhysicalControls) .onGet(async () => { const value = zone.lockPhysicalControl; return value; }) .onSet(async (value) => { try { value = value ? true : false; const payload = {}; let flag = null; switch (i) { case caseHeatPump: //Heat Pump if (accountTypeMelCloud) payload.prohibitZone1 = value; payload.prohibitHotWater = value; if (accountTypeMelCloud) payload.prohibitZone2 = value; flag = HeatPump.EffectiveFlags.ProhibitHeatingZone1 + HeatPump.EffectiveFlags.ProhibitHotWater + HeatPump.EffectiveFlags.ProhibitHeatingZone2; break; case caseZone1: //Zone 1 if (!accountTypeMelCloud) return; payload.prohibitZone1 = value; flag = HeatPump.EffectiveFlags.ProhibitHeatingZone1; break; case caseHotWater: //Hot Water payload.prohibitHotWater = value; flag = HeatPump.EffectiveFlags.ProhibitHotWater; break; case caseZone2: //Zone 2 if (!accountTypeMelCloud) return; payload.prohibitZone2 = value; flag = HeatPump.EffectiveFlags.ProhibitHeatingZone2; break; }; if (this.logInfo) this.emit('info', `${zoneName}, Set lock physical controls: ${value ? 'Lock' : 'Unlock'}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, flag); } catch (error) { if (this.logWarn) 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) => { if (!accountTypeMelCloud) return; try { this.accessory.useFahrenheit = value ? true : false; this.melCloudAccountData.UseFahrenheit = value ? true : false; this.melCloudAccountData.Account.LoginData.UseFahrenheit = value ? true : false; const payload = this.melCloudAccountData; if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`); await this.melCloudAtw.send(this.accountType, this.displayType, deviceData, payload, 'account'); } catch (error) { if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`); }; }); this.melCloudServices.push(melCloudService); accessory.addService(melCloudService); break; case 2: //Thermostat if (this.logDebug) this.emit('debug', `Prepare thermostat ${zoneName} service`); const melCloudServiceT = new Service.Thermostat(serviceName, `Thermostat ${deviceId} ${i}`); if (i === caseHeatPump) melCloudServiceT.setPrimaryService(true); melCloudServiceT.getCharacteristic(Characteristic.CurrentHeatingCoolingState) .onGet(async () => { const value = zone.currentOperationMode; return value; }); melCloudServiceT.getCharacteristic(Characteristic.TargetHeatingCoolingState) .setProps({ minValue: zone.operationModesSetPropsMinValue, maxValue: zone.operationModesSetPropsMaxValue, validValues: zone.operationModesSetPropsValidValues }) .onGet(async () => { const value = zone.targetOperationMode; return value; }) .onSet(async (value) => { try { let operationModeText = ''; const payload = {}; let flag = null; switch (i) { case caseHeatPump: //Heat Pump - Operation Mode - IDLE, HOT WATER, HEATING, COOLING, HOT WATER STORAGE, FREEZE STAT, LEGIONELLA, HEATING ECO, MODE 1, MODE 2, MODE 3, HEATING UP // Unit Status - HEAT, COOL switch (value) { case 0: //OFF payload.power = false; break; case 1: //HEAT if (accountTypeMelCloud) deviceData.Device.UnitStatus = 0; payload.power = true; //payload.operationMode = 2; break; case 2: //COOL if (accountTypeMelCloud) deviceData.Device.UnitStatus = 1; payload.power = true; //payload.operationMode = 3; break; case 3: //AUTO - not used return; }; operationModeText = HeatPump.OperationModeHeatPumpMapEnumToStringInfo[value]; break; case caseZone1: //Zone 1 - HEAT ROOM, HEAT FLOW, HEAT CURVE, COOL ROOM, COOL FLOW, FLOOR DRY UP, IDLE switch (value) { case 0: //OFF //payload.operationModeZone1 = 2; //flag = HeatPump.EffectiveFlags.OperationModeZone1; return; case 1: //HEAT - HEAT ROOM / COOL ROOM payload.operationModeZone1 = [0, 3][zone.operationModeHeatPump]; flag = HeatPump.EffectiveFlags.OperationModeZone1; break; case 2: //COOL - HEAT FLOW / COOL FLOW payload.operationModeZone1 = [1, 4][zone.operationModeHeatPump];