From fc26f05df4b3813895259e2dc4bece0ca479d19a Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Sun, 25 Aug 2024 20:40:57 +0200 Subject: [PATCH] release v2.3.0 --- CHANGELOG.md | 11 ++ README.md | 17 ++- config.schema.json | 166 +++++++++++++++++++++-------- index.js | 20 ++-- package.json | 2 +- sample-config.json | 8 +- src/deviceata.js | 255 ++++++++++++++++++++++++++++++--------------- src/deviceatw.js | 5 +- src/deviceerv.js | 5 +- src/melcloud.js | 8 +- src/melcloudata.js | 20 ++-- src/melcloudatw.js | 8 +- src/melclouderv.js | 6 +- 13 files changed, 356 insertions(+), 175 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a56595..1bcaa7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - After update to v2 from v1 only RESTFull and MQTT config settings need to be updated in config. +## [2.3.0] - (25.08.2024) + +## Changes + +- removed from config.json `ataDisableAutoMode`, `ataDisableHeatMode`, `ataAutoHeatMode` +- added to config.json `ataHeatDryFanMode`, `ataCoolDryFanMode`, `ataAutoDryFanMode` +- added individual operating mode assingn for `Heat/Cool/Auto`, [#132](https://github.com/grzegorz914/homebridge-melcloud-control/issues/132) +- unfortunatelly disabled operating mode cannot be hiden in HomeKit app due to HomeKit app limitations +- cleanup +- config schema updated + ## [2.2.0] - (18.08.2024) ## Changes diff --git a/README.md b/README.md index 5c6a34c..6c25ee1 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation * Support temperature display units `Celsius/Fahrenheit`. * Support hide device by `DeviceId`. * Support control device `Presets`. -* Support direct device control creating extra `Buttons`, applied for all devices of same type in account. -* Support identify all states of device creating `Sensors`, applied for all devices of same type in account. +* Support assing inividual operating mode for `Heat/Cool/Auto`. +* Support direct device control using extra `Buttons`, applied for all devices of same type in account. +* Support detect all device states using extra `Sensors`, applied for all devices of same type in account. * Support automations, shortcuts and Siri. * Support external integrations, [RESTFul](https://github.com/grzegorz914/homebridge-melcloud-control?tab=readme-ov-file#restful-integration), [MQTT](https://github.com/grzegorz914/homebridge-melcloud-control?tab=readme-ov-file#mqtt-integration). @@ -48,13 +49,12 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation * Swing mode `AUTO/SWING`. * Physical lock controls `LOCK/UNLOCK`. * Temperature display unit `°F/°C`. - * If `AUTO/HEAT` or both modes are not supported by device will use `DRY/FAN` or `FAN/DRY` modes instead. * Thermostat: * Power `ON/OFF`. * Operating mode `POWER OFF/HEAT/COOL/AUTO`. * Temperature `HEATING/COOLING`. * Temperature display unit `°F/°C`. - * If `AUTO/HEAT` or both modes are not supported by device will use `DRY/FAN` or `FAN/DRY` modes instead. + * Assign operating mode for `HEAT/AUTO` * Buttons: * For direct device control. * Power `ON/OFF`. @@ -164,7 +164,6 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation * Run this plugin as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) (Highly Recommended), this prevent crash Homebridge if plugin crashes. * Install and use [Homebridge Config UI X](https://github.com/homebridge/homebridge-config-ui-x/wiki) to configure this plugin (Highly Recommended). * The `sample-config.json` can be edited and used as an alternative. -* Be sure to always make a backup copy of your config.json file before making any changes to it.

@@ -176,12 +175,12 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation | `user` | Here set the MELCloud username. | | `passwd` | Here set the MELCloud password. | | `language` | Here select the MELCloud language. | -| `ataDisplayMode` | Here select main control mode `Heater/Cooler`, `Thermostat`. | +| `ataDisplayMode` | Here select device control mode `Heater/Cooler`, `Thermostat`. | +| `ataHeatDryFanMode` | Here select the operatiing mode for `Heat`, if this mode is not supported, it will be disabled. | +| `ataCoolDryFanMode` | Here select the operatiing mode for `Cool`, if this mode is not supported, it will be disabled. | +| `ataAutoDryFanMode` | Here select the operatiing mode for `Auto`, if this mode is not supported, it will be disabled.. | | `ataTemperatureSensor` | This enable extra `Room` temperature sensors to use with automations in HomeKit app. | | `ataTemperatureSensorOutdoor` | This enable extra `Outdoor` temperature sensors to use with automations in HomeKit app. | -| `ataDisableAutoMode` | This will disable `Auto` mode even this mode is supported by device. | -| `ataDisableHeatMode` | This will disable `Heat` mode even this mode is supported by device. | -| `ataAutoHeatMode` | Here select operation mode for `Auto/Heat`, if `Auto`, `Heat` or both modes are not supported by device will be used selected modes instead. | | `ataPresets` | This enable extra buttons for configured presets and display it in HomeKit app. | | `ataButtons.name` | Here set `Button Name` which You want expose to the `Homebridge/HomeKit`. | | `ataButtons.mode` | Here select button mode, VH - Vane Horizontal, VV - Vane Horizontal. | diff --git a/config.schema.json b/config.schema.json index cebbeaf..920b6fd 100644 --- a/config.schema.json +++ b/config.schema.json @@ -205,7 +205,7 @@ "required": true }, "ataDisplayMode": { - "title": "Control Mode", + "title": "Control", "type": "integer", "minimum": 0, "maximum": 1, @@ -224,64 +224,135 @@ ] } ], - "description": "Select main control mode.", + "description": "Select device control mode displayed in HomeKit app.", "required": true }, - "ataTemperatureSensor": { - "title": "Temperature Sensor Room", - "type": "boolean", - "default": false, - "description": "This enable extra room temperature sensor to use with automations in HomeKit app.", - "required": false - }, - "ataTemperatureSensorOutdoor": { - "title": "Temperature Sensor Outdoor", - "type": "boolean", - "default": false, - "description": "This enable extra outdoor temperature sensor to use with automations in HomeKit app.", - "required": false - }, - "ataPresets": { - "title": "Presets", - "type": "boolean", - "default": false, - "description": "This enable extra buttons for configured presets and display it in HomeKit app.", - "required": false - }, - "ataDisableAutoMode": { - "title": "Disable Auto Mode", - "type": "boolean", - "default": false, - "description": "This will disable Auto mode even if this mode is supported by device.", - "required": false + "ataHeatDryFanMode": { + "title": "Heat", + "type": "integer", + "minimum": 0, + "maximum": 3, + "default": 1, + "oneOf": [ + { + "title": "None/Disabled", + "enum": [ + 0 + ] + }, + { + "title": "Heat", + "enum": [ + 1 + ] + }, + { + "title": "Dry", + "enum": [ + 2 + ] + }, + { + "title": "Fan", + "enum": [ + 3 + ] + } + ], + "description": "Select the operatiing mode for Heat, if this mode is not supported, it will be disabled.", + "required": true }, - "ataDisableHeatMode": { - "title": "Disable Heat Mode", - "type": "boolean", - "default": false, - "description": "This will disable Heat mode even if this mode is supported by device.", - "required": false + "ataCoolDryFanMode": { + "title": "Cool", + "type": "integer", + "minimum": 0, + "maximum": 3, + "default": 1, + "oneOf": [ + { + "title": "None/Disabled", + "enum": [ + 0 + ] + }, + { + "title": "Cool", + "enum": [ + 1 + ] + }, + { + "title": "Dry", + "enum": [ + 2 + ] + }, + { + "title": "Fan", + "enum": [ + 3 + ] + } + ], + "description": "Select the operatiing mode for Cool, if this mode is not supported, it will be disabled.", + "required": true }, - "ataAutoHeatMode": { - "title": "Auto/Heat Mode", + "ataAutoDryFanMode": { + "title": "Auto", "type": "integer", + "minimum": 0, + "maximum": 3, + "default": 1, "oneOf": [ { - "title": "Auto - Dry / Heat - Fan", + "title": "None/Disabled", "enum": [ 0 ] }, { - "title": "Auto - Fan / Heat - Dry", + "title": "Auto", "enum": [ 1 ] + }, + { + "title": "Dry", + "enum": [ + 2 + ] + }, + { + "title": "Fan", + "enum": [ + 3 + ] } ], - "description": "Operation mode for Auto/Heat option, if Auto, Heat or both modes are not supported by the device this will be used for selected modes instead.", + "description": "Select the operatiing mode for Auto, if this mode is not supported, it will be disabled.", "required": true }, + "ataPresets": { + "title": "Presets", + "type": "boolean", + "default": false, + "description": "This enable extra buttons for configured presets and display it in HomeKit app.", + "required": false + }, + "ataTemperatureSensor": { + "title": "Room", + "type": "boolean", + "default": false, + "description": "This enable extra room temperature sensor to use with automations in HomeKit app.", + "required": false + }, + "ataTemperatureSensorOutdoor": { + "title": "Outdoor", + "type": "boolean", + "default": false, + "description": "This enable extra outdoor temperature sensor to use with automations in HomeKit app.", + "required": false + }, "ataButtons": { "title": "Button / Sensor", "type": "array", @@ -1359,9 +1430,18 @@ "items": [ "accounts[].ataDisplayMode", "accounts[].ataPresets", - "accounts[].ataDisableAutoMode", - "accounts[].ataDisableHeatMode", - "accounts[].ataAutoHeatMode", + { + "key": "accounts[]", + "type": "section", + "title": "Operation Modes", + "expandable": true, + "expanded": false, + "items": [ + "accounts[].ataHeatDryFanMode", + "accounts[].ataCoolDryFanMode", + "accounts[].ataAutoDryFanMode" + ] + }, { "key": "accounts[]", "type": "section", diff --git a/index.js b/index.js index 5231439..c0c73f3 100644 --- a/index.js +++ b/index.js @@ -17,12 +17,6 @@ class MelCloudPlatform { this.accessories = []; const accountsName = []; - //check if the directory exists, if not then create it - const prefDir = path.join(api.user.storagePath(), 'melcloud'); - if (!fs.existsSync(prefDir)) { - fs.mkdirSync(prefDir); - }; - api.on('didFinishLaunching', () => { //loop through accounts for (const account of config.accounts) { @@ -64,9 +58,21 @@ class MelCloudPlatform { }; const debug1 = enableDebugMode ? log.info(`Account: ${accountName}, Config: ${JSON.stringify(debugData, null, 2)}`) : false; - //set refresh interval + //define directory and file paths + const prefDir = path.join(api.user.storagePath(), 'melcloud'); const accountInfoFile = `${prefDir}/${accountName}_Account`; const buildingsFile = `${prefDir}/${accountName}_Buildings`; + + //create directory if it doesn't exist + try { + //create directory if it doesn't exist + fs.mkdirSync(prefDir, { recursive: true }); + } catch (error) { + log.error(`Account: ${accountName}, prepare directory error: ${error.message ?? error}`); + return; + } + + //set refresh interval const refreshInterval = account.refreshInterval * 1000 || 120000; const deviceRefreshInterval = account.deviceRefreshInterval * 1000 || 5000; diff --git a/package.json b/package.json index 0f2803e..8b07f6e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "MELCloud Control", "name": "homebridge-melcloud-control", - "version": "2.2.2", + "version": "2.3.0", "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.", "license": "MIT", "author": "grzegorz914", diff --git a/sample-config.json b/sample-config.json index a778fc2..e174339 100644 --- a/sample-config.json +++ b/sample-config.json @@ -24,11 +24,11 @@ "passwd": "password", "language": "0", "ataDisplayMode": 0, + "ataHeatDryFanMode": 1, + "ataCoolDryFanMode": 1, + "ataAutoDryFanMode": 1, "ataTemperatureSensor": false, "ataTemperatureSensorOutdoor": false, - "ataDisableAutoMode": false, - "ataDisableHeatMode": false, - "ataAutoHeatMode": 0, "ataPresets": false, "ataButtons": [ { @@ -108,4 +108,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/src/deviceata.js b/src/deviceata.js index 975022c..6483c31 100644 --- a/src/deviceata.js +++ b/src/deviceata.js @@ -21,9 +21,9 @@ class DeviceAta extends EventEmitter { this.temperatureSensor = account.ataTemperatureSensor || false; this.temperatureSensorOutdoor = account.ataTemperatureSensorOutdoor || false; this.presetsEnabled = account.ataPresets || false; - this.disableAutoMode = account.ataDisableAutoMode || false; - this.disableHeatMode = account.ataDisableHeatMode || false; - this.autoHeatMode = account.ataAutoHeatMode || 0; //DRY, FAN + this.heatDryFanMode = account.ataHeatDryFanMode || 1; //NONE, HEAT, DRY, FAN + this.coolDryFanMode = account.ataCoolDryFanMode || 1; //NONE, COOL, DRY, FAN + this.autoDryFanMode = account.ataAutoDryFanMode || 1; //NONE, AUTO, DRY, FAN this.buttons = account.ataButtons || []; this.disableLogInfo = account.disableLogInfo || false; this.disableLogDeviceInfo = account.disableLogDeviceInfo || false; @@ -39,9 +39,8 @@ class DeviceAta extends EventEmitter { const mqttEnabled = mqtt.enable || false; this.mqttConnected = false; - //variables + //function this.melCloud = melCloud; //function - this.startPrepareAccessory = true; //buttons configured this.buttonsConfigured = []; @@ -64,6 +63,22 @@ class DeviceAta extends EventEmitter { } this.buttonsConfiguredCount = this.buttonsConfigured.length || 0; + //variables + this.startPrepareAccessory = true; + this.power = false; + this.offline = false; + this.currentOperationMode = 0; + this.targetOperationMode = 0; + this.fanSpeed = 0; + this.swingMode = 0; + this.lockPhysicalControls = 0; + this.useFahrenheit = 0; + + this.operationModeSetPropsMinValue = 0; + this.operationModeSetPropsMaxValue = 3; + this.operationModeSetPropsValidValues = [0]; + this.fanSpeedSetPropsMaxValue = 2; + //melcloud device this.melCloudAta = new MelCloudAta({ contextKey: contextKey, @@ -169,10 +184,11 @@ class DeviceAta extends EventEmitter { const swingFunction = deviceData.Device.SwingFunction ?? false; const numberOfFanSpeeds = deviceData.Device.NumberOfFanSpeeds ?? 0; const modelSupportsFanSpeed = deviceData.Device.ModelSupportsFanSpeed ?? false; - const modelSupportsAuto1 = deviceData.Device.ModelSupportsAuto ?? false; - const modelSupportsAuto = !this.disableAutoMode && modelSupportsAuto1; const modelSupportsHeat1 = deviceData.Device.ModelSupportsHeat ?? false; - const modelSupportsHeat = !this.disableHeatMode && modelSupportsHeat1; + const modelSupportsHeat = this.heatDryFanMode >= 1 && modelSupportsHeat1; + const modelSupportsCool = this.coolDryFanMode >= 1; + const modelSupportsAuto1 = deviceData.Device.ModelSupportsAuto ?? false; + const modelSupportsAuto = this.autoDryFanMode >= 1 && modelSupportsAuto1; const modelSupportsDry = deviceData.Device.ModelSupportsDry ?? false; const temperatureIncrement = deviceData.Device.TemperatureIncrement ?? 1; const outdoorTemperature = deviceData.Device.OutdoorTemperature; @@ -183,8 +199,6 @@ class DeviceAta extends EventEmitter { this.swingFunction = swingFunction; this.numberOfFanSpeeds = numberOfFanSpeeds; this.modelSupportsFanSpeed = modelSupportsFanSpeed; - this.modelSupportsAuto = modelSupportsAuto; - this.modelSupportsHeat = modelSupportsHeat; this.modelSupportsDry = modelSupportsDry; this.temperatureIncrement = temperatureIncrement; this.temperatureUnit = CONSTANTS.TemperatureDisplayUnits[useFahrenheit]; @@ -212,109 +226,188 @@ class DeviceAta extends EventEmitter { this.presetsCount = this.presetsEnabled ? presets.length : 0; //operating mode - let autoHeatDryFanMode = 0; - let currentOperationMode = 0; - let targetOperationMode = 0; - let fanSpeed = 0; - let swingMode = 0; - let lockPhysicalControls = 0; - - let operationModeSetPropsMinValue = 0; - let operationModeSetPropsMaxValue = 3; - let operationModeSetPropsValidValues = [0, 1, 2, 3]; - let fanSpeedSetPropsMaxValue = 2; - switch (displayMode) { case 0: //Heater Cooler //operating mode 0, HEAT, DRY, COOL, 4, 5, 6, FAN, AUTO, ISEE HEAT, ISEE DRY, ISEE COOL - autoHeatDryFanMode = !modelSupportsAuto && !modelSupportsHeat ? [operationMode === 2 ? 0 : 1, operationMode === 6 ? 0 : 1][this.autoHeatMode] : !modelSupportsAuto && modelSupportsHeat ? 0 : modelSupportsAuto && !modelSupportsHeat ? 1 : 1; - currentOperationMode = !power ? 0 : inStandbyMode ? 1 : [0, 2, 3, 3, 1, 1, 1, 1, (setTemperature < roomTemperature) ? 3 : 2, 2, 1, 3][operationMode]; //INACTIVE, IDLE, HEATING, COOLING - targetOperationMode = [0, 1, autoHeatDryFanMode, 2, 2, 2, 2, autoHeatDryFanMode, 0, 1, 1, 2][operationMode]; //AUTO, HEAT, COOL - operationModeSetPropsMinValue = 0; - operationModeSetPropsMaxValue = 2; - operationModeSetPropsValidValues = [0, 1, 2]; + switch (power) { + case true: + switch (!inStandbyMode) { + case true: + switch (operationMode) { + case 1: //HEAT + this.currentOperationMode = 2; //INACTIVE, IDLE, HEATING, COOLING + this.targetOperationMode = 1; //AUTO, HEAT, COOL + break; + case 2: //DRY + this.currentOperationMode = 3; + this.targetOperationMode = 2; + break; + case 3: //COOL + this.currentOperationMode = 3; + this.targetOperationMode = 2; + break; + case 7: //FAN + this.currentOperationMode = 3; + this.targetOperationMode = 2; + break; + case 8: //AUTO + this.currentOperationMode = setTemperature < roomTemperature ? 3 : 2; + this.targetOperationMode = 0; + break; + case 9: //ISEE HEAT + this.currentOperationMode = 2 + this.targetOperationMode = 1; + break; + case 10: //ISEE DRY + this.currentOperationMode = 3; + this.targetOperationMode = 2; + break; + case 11: //ISEE COOL; + this.currentOperationMode = 3; + this.targetOperationMode = 2; + break; + default: + this.emit('warn', `Unknown operating mode: ${operationMode}`); + break; + }; + break; + case false: + this.currentOperationMode = 1; + break; + }; + break; + case false: + this.currentOperationMode = 0; + break; + }; + this.operationModeSetPropsMinValue = modelSupportsAuto && modelSupportsHeat ? 0 : !modelSupportsAuto && modelSupportsHeat ? 1 : modelSupportsAuto && !modelSupportsHeat ? 0 : 2; + this.operationModeSetPropsMaxValue = 2 + this.operationModeSetPropsValidValues = modelSupportsAuto && modelSupportsHeat ? [0, 1, 2] : !modelSupportsAuto && modelSupportsHeat ? [1, 2] : modelSupportsAuto && !modelSupportsHeat ? [0, 2] : [2]; //fan speed mode if (modelSupportsFanSpeed) { switch (numberOfFanSpeeds) { case 2: //Fan speed mode 2 - fanSpeed = hasAutomaticFanSpeed ? [3, 1, 2][setFanSpeed] : [0, 1, 2][setFanSpeed]; - fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 3 : 2; + this.fanSpeed = hasAutomaticFanSpeed ? [3, 1, 2][setFanSpeed] : [0, 1, 2][setFanSpeed]; + this.fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 3 : 2; break; case 3: //Fan speed mode 3 - fanSpeed = hasAutomaticFanSpeed ? [4, 1, 2, 3][setFanSpeed] : [0, 1, 2, 3][setFanSpeed]; - fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 4 : 3; + this.fanSpeed = hasAutomaticFanSpeed ? [4, 1, 2, 3][setFanSpeed] : [0, 1, 2, 3][setFanSpeed]; + this.fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 4 : 3; break; case 4: //Fan speed mode 4 - fanSpeed = hasAutomaticFanSpeed ? [5, 1, 2, 3, 4][setFanSpeed] : [0, 1, 2, 3, 4][setFanSpeed]; - fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 5 : 4; + this.fanSpeed = hasAutomaticFanSpeed ? [5, 1, 2, 3, 4][setFanSpeed] : [0, 1, 2, 3, 4][setFanSpeed]; + this.fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 5 : 4; break; case 5: //Fan speed mode 5 - fanSpeed = hasAutomaticFanSpeed ? [6, 1, 2, 3, 4, 5][setFanSpeed] : [0, 1, 2, 3, 4, 5][setFanSpeed]; - fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 6 : 5; + this.fanSpeed = hasAutomaticFanSpeed ? [6, 1, 2, 3, 4, 5][setFanSpeed] : [0, 1, 2, 3, 4, 5][setFanSpeed]; + this.fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 6 : 5; break; case 6: //Fan speed mode 6 - fanSpeed = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 6][setFanSpeed] : [0, 1, 2, 3, 4, 5, 6][setFanSpeed]; - fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 7 : 6; + this.fanSpeed = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 6][setFanSpeed] : [0, 1, 2, 3, 4, 5, 6][setFanSpeed]; + this.fanSpeedSetPropsMaxValue = hasAutomaticFanSpeed ? 7 : 6; break; }; }; //swing and vane mode - swingMode = swingFunction && vaneHorizontal === 12 && vaneVertical === 7 ? 1 : 0; + this.swingMode = swingFunction && vaneHorizontal === 12 && vaneVertical === 7 ? 1 : 0; //lock physical controls - lockPhysicalControls = prohibitSetTemperature && prohibitOperationMode && prohibitPower ? 1 : 0; + this.lockPhysicalControls = prohibitSetTemperature && prohibitOperationMode && prohibitPower ? 1 : 0; //update characteristics if (this.melCloudService) { this.melCloudService .updateCharacteristic(Characteristic.Active, power) - .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, currentOperationMode) - .updateCharacteristic(Characteristic.TargetHeaterCoolerState, targetOperationMode) + .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, this.currentOperationMode) + .updateCharacteristic(Characteristic.TargetHeaterCoolerState, this.targetOperationMode) .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature) .updateCharacteristic(Characteristic.HeatingThresholdTemperature, setTemperature) .updateCharacteristic(Characteristic.CoolingThresholdTemperature, setTemperature) - .updateCharacteristic(Characteristic.LockPhysicalControls, lockPhysicalControls) - .updateCharacteristic(Characteristic.TemperatureDisplayUnits, useFahrenheit); - const updateRS = modelSupportsFanSpeed ? this.melCloudService.updateCharacteristic(Characteristic.RotationSpeed, fanSpeed) : false; - const updateSM = swingFunction ? this.melCloudService.updateCharacteristic(Characteristic.SwingMode, swingMode) : false; + .updateCharacteristic(Characteristic.LockPhysicalControls, this.lockPhysicalControls) + .updateCharacteristic(Characteristic.TemperatureDisplayUnits, this.useFahrenheit); + const updateRS = modelSupportsFanSpeed ? this.melCloudService.updateCharacteristic(Characteristic.RotationSpeed, this.fanSpeed) : false; + const updateSM = swingFunction ? this.melCloudService.updateCharacteristic(Characteristic.SwingMode, this.swingMode) : false; }; break; case 1: //Thermostat - //operating mode 0, HEAT, DRY, COOL, 4, 5, 6, FAN, AUTO, ISEE HEAT, ISEE DRY, ISEE COOL - autoHeatDryFanMode = !modelSupportsAuto && !modelSupportsHeat ? [operationMode === 2 ? 3 : 1, operationMode === 6 ? 3 : 1][this.autoHeatMode] : !modelSupportsAuto && modelSupportsHeat ? 3 : modelSupportsAuto && !modelSupportsHeat ? 1 : 1; - currentOperationMode = !power || inStandbyMode ? 0 : [0, 1, 2, 2, 2, 2, 2, 2, (setTemperature < roomTemperature) ? 2 : 1, 1, 1, 2][operationMode]; //OFF, HEAT, COOL - targetOperationMode = !power || inStandbyMode ? 0 : [0, 1, autoHeatDryFanMode, 2, 2, 2, 2, autoHeatDryFanMode, 3, 1, 1, 2][operationMode]; //OFF, HEAT, COOL, AUTO - operationModeSetPropsMinValue = 0; - operationModeSetPropsMaxValue = 3; - operationModeSetPropsValidValues = [0, 1, 2, 3]; + switch (power) { + case true: + switch (!inStandbyMode) { + case true: + switch (operationMode) { + case 1: //HEAT + this.currentOperationMode = 1; //OFF, HEAT, COOL + this.targetOperationMode = 1; //OFF, HEAT, COOL, AUTO + break; + case 2: //DRY + this.currentOperationMode = 2; + this.targetOperationMode = 2; + break; + case 3: //COOL + this.currentOperationMode = 2; + this.targetOperationMode = 2; + break; + case 7: //FAN + this.currentOperationMode = 2; + this.targetOperationMode = 2; + break; + case 8: //AUTO + this.currentOperationMode = setTemperature < roomTemperature ? 2 : 1; + this.targetOperationMode = 3; + break; + case 9: //ISEE HEAT + this.currentOperationMode = 1 + this.targetOperationMode = 1; + break; + case 10: //ISEE DRY + this.currentOperationMode = 2; + this.targetOperationMode = 2; + break; + case 11: //ISEE COOL; + this.currentOperationMode = 2; + this.targetOperationMode = 2; + break; + default: + this.emit('warn', `Unknown operating mode: ${operationMode}`); + break; + }; + break; + case false: + this.currentOperationMode = 0; + this.targetOperationMode = 0; + break; + }; + case false: + this.currentOperationMode = 0; + this.targetOperationMode = 0; + break; + }; + this.operationModeSetPropsMinValue = 0 + this.operationModeSetPropsMaxValue = modelSupportsAuto && modelSupportsHeat ? 3 : !modelSupportsAuto && modelSupportsHeat ? 2 : modelSupportsAuto && !modelSupportsHeat ? 3 : 2; + this.operationModeSetPropsValidValues = modelSupportsAuto && modelSupportsHeat ? [0, 1, 2, 3] : !modelSupportsAuto && modelSupportsHeat ? [0, 1, 2] : modelSupportsAuto && !modelSupportsHeat ? [0, 2, 3] : [0, 2]; //update characteristics if (this.melCloudService) { this.melCloudService - .updateCharacteristic(Characteristic.CurrentHeatingCoolingState, currentOperationMode) - .updateCharacteristic(Characteristic.TargetHeatingCoolingState, targetOperationMode) + .updateCharacteristic(Characteristic.CurrentHeatingCoolingState, this.currentOperationMode) + .updateCharacteristic(Characteristic.TargetHeatingCoolingState, this.targetOperationMode) .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature) .updateCharacteristic(Characteristic.TargetTemperature, setTemperature) - .updateCharacteristic(Characteristic.TemperatureDisplayUnits, useFahrenheit); + .updateCharacteristic(Characteristic.TemperatureDisplayUnits, this.useFahrenheit); }; break; }; this.power = power; this.offline = offline; - this.currentOperationMode = currentOperationMode; - this.targetOperationMode = targetOperationMode; this.roomTemperature = roomTemperature; this.outdoorTemperature = outdoorTemperature; this.setTemperature = setTemperature; - this.fanSpeed = fanSpeed; this.setFanSpeed = setFanSpeed; - this.swingMode = swingMode; this.vaneHorizontal = vaneHorizontal; this.vaneVertical = vaneVertical; - this.lockPhysicalControls = lockPhysicalControls; if (this.roomTemperatureSensorService) { this.roomTemperatureSensorService @@ -426,7 +519,7 @@ class DeviceAta extends EventEmitter { button.state = power ? (setFanSpeed === 6) : false; break; case 37: //PHYSICAL LOCK CONTROLS ALL - button.state = (lockPhysicalControls === 1); + button.state = (this.lockPhysicalControls === 1); break; case 38: //PHYSICAL LOCK CONTROLS POWER button.state = (prohibitPower === true); @@ -483,20 +576,14 @@ class DeviceAta extends EventEmitter { const info1 = displayMode === 0 ? this.emit('message', `Heating threshold temperature: ${setTemperature}${this.temperatureUnit}`) : false; const info2 = displayMode === 0 ? this.emit('message', `Cooling threshold temperature: ${setTemperature}${this.temperatureUnit}`) : false; const info3 = modelSupportsFanSpeed ? this.emit('message', `Fan speed mode: ${CONSTANTS.AirConditioner.FanSpeed[setFanSpeed]}`) : false; - const info4 = swingFunction ? this.emit('message', `Vane swing mode: ${CONSTANTS.AirConditioner.AirDirection[swingMode ? 6 : 0]}`) : false; + const info4 = swingFunction ? this.emit('message', `Vane swing mode: ${CONSTANTS.AirConditioner.AirDirection[this.swingMode ? 6 : 0]}`) : false; this.emit('message', `Temperature display unit: ${this.temperatureUnit}`); - this.emit('message', `Lock physical controls: ${lockPhysicalControls ? 'LOCKED' : 'UNLOCKED'}`); + this.emit('message', `Lock physical controls: ${this.lockPhysicalControls ? 'LOCKED' : 'UNLOCKED'}`); }; //start prepare accessory if (this.startPrepareAccessory) { try { - this.operationModeSetPropsMinValue = operationModeSetPropsMinValue; - this.operationModeSetPropsMaxValue = operationModeSetPropsMaxValue; - this.operationModeSetPropsValidValues = operationModeSetPropsValidValues; - this.fanSpeedSetPropsMaxValue = fanSpeedSetPropsMaxValue; - - await new Promise(resolve => setTimeout(resolve, 150)); const accessory = await this.prepareAccessory(accountInfo, deviceState, deviceId, deviceTypeText, deviceName, accountName); this.emit('publishAccessory', accessory); this.startPrepareAccessory = false; @@ -594,7 +681,7 @@ class DeviceAta extends EventEmitter { }; return set; } catch (error) { - throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`); + throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`); }; } @@ -626,20 +713,19 @@ class DeviceAta extends EventEmitter { const presetsCount = this.presetsCount; const hasAutomaticFanSpeed = this.hasAutomaticFanSpeed; const modelSupportsFanSpeed = this.modelSupportsFanSpeed; - const modelSupportsAuto = this.modelSupportsAuto; - const modelSupportsHeat = this.modelSupportsHeat; const modelSupportsDry = this.modelSupportsDry; const numberOfFanSpeeds = this.numberOfFanSpeeds; const swingFunction = this.swingFunction; const hasOutdoorTemperature = this.hasOutdoorTemperature; - const autoDryFan = [modelSupportsDry ? 2 : 7, 7][this.autoHeatMode]; - const heatFanDry = [7, modelSupportsDry ? 2 : 7][this.autoHeatMode]; + const autoDryFanMode = [deviceState.OperationMode, 8, modelSupportsDry ? 2 : 8, 7][this.autoDryFanMode]; //NONE, AUTO - 8, DRY - 2, FAN - 7 + const heatDryFanMode = [deviceState.OperationMode, 1, modelSupportsDry ? 2 : 1, 7][this.heatDryFanMode]; //NONE, HEAT - 1, DRY - 2, FAN - 7 + const coolDryFanMode = [deviceState.OperationMode, 3, modelSupportsDry ? 2 : 3, 7][this.coolDryFanMode]; //NONE, COOL - 3, DRY - 2, FAN - 7 const serviceName = `${deviceTypeText} ${accessoryName}`; switch (displayMode) { case 0: //Heater Cooler - const debug = this.enableDebugMode ? this.emit('debug', `Prepare heather/cooler service`) : false; - this.melCloudService = accessory.addService(Service.HeaterCooler, serviceName, `HeaterCooler ${deviceId}`); + 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.power; @@ -676,17 +762,17 @@ class DeviceAta extends EventEmitter { switch (value) { case 0: //AUTO - AUTO deviceState.Power = true; - deviceState.OperationMode = modelSupportsAuto ? 8 : autoDryFan; + deviceState.OperationMode = autoDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; case 1: //HEAT - HEAT deviceState.Power = true; - deviceState.OperationMode = modelSupportsHeat ? 1 : heatFanDry; + deviceState.OperationMode = heatDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; case 2: //COOL - COOL deviceState.Power = true; - deviceState.OperationMode = 3; + deviceState.OperationMode = coolDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; }; @@ -847,6 +933,7 @@ class DeviceAta extends EventEmitter { this.emit('warn', `Set temperature display unit error: ${error}`); }; }); + accessory.addService(this.melCloudService); break; case 1: //Thermostat const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare thermostat service`) : false; @@ -875,17 +962,17 @@ class DeviceAta extends EventEmitter { break; case 1: //HEAT - HEAT deviceState.Power = true; - deviceState.OperationMode = modelSupportsHeat ? 1 : heatFanDry; + deviceState.OperationMode = heatDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; case 2: //COOL - COOL deviceState.Power = true; - deviceState.OperationMode = 3; + deviceState.OperationMode = coolDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; case 3: //AUTO - AUTO deviceState.Power = true; - deviceState.OperationMode = modelSupportsAuto ? 8 : autoDryFan; + deviceState.OperationMode = autoDryFanMode; deviceState.EffectiveFlags = CONSTANTS.AirConditioner.EffectiveFlags.Power + CONSTANTS.AirConditioner.EffectiveFlags.OperationMode; break; }; @@ -1254,7 +1341,7 @@ class DeviceAta extends EventEmitter { return accessory; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/deviceatw.js b/src/deviceatw.js index 32771d3..9d424f4 100644 --- a/src/deviceatw.js +++ b/src/deviceatw.js @@ -676,7 +676,6 @@ class DeviceAtw extends EventEmitter { //start prepare accessory if (this.startPrepareAccessory) { try { - await new Promise(resolve => setTimeout(resolve, 150)); const accessory = await this.prepareAccessory(accountInfo, deviceState, deviceId, deviceTypeText, deviceName, accountName); this.emit('publishAccessory', accessory); this.startPrepareAccessory = false; @@ -794,7 +793,7 @@ class DeviceAtw extends EventEmitter { }; return set; } catch (error) { - throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`); + throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`); }; } //prepare accessory @@ -1729,7 +1728,7 @@ class DeviceAtw extends EventEmitter { return accessory; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/deviceerv.js b/src/deviceerv.js index 777858d..2957372 100644 --- a/src/deviceerv.js +++ b/src/deviceerv.js @@ -472,7 +472,6 @@ class DeviceErv extends EventEmitter { this.operationModeSetPropsValidValues = operationModeSetPropsValidValues; this.fanSpeedSetPropsMaxValue = fanSpeedSetPropsMaxValue; - await new Promise(resolve => setTimeout(resolve, 150)); const accessory = await this.prepareAccessory(accountInfo, deviceState, deviceId, deviceTypeText, deviceName, accountName); this.emit('publishAccessory', accessory); this.startPrepareAccessory = false; @@ -560,7 +559,7 @@ class DeviceErv extends EventEmitter { }; return set; } catch (error) { - throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error}`); + throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`); }; } @@ -1170,7 +1169,7 @@ class DeviceErv extends EventEmitter { return accessory; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/melcloud.js b/src/melcloud.js index 8def860..9f3c2fe 100644 --- a/src/melcloud.js +++ b/src/melcloud.js @@ -134,7 +134,7 @@ class MelCloud extends EventEmitter { this.impulseGenerator.start(timers); return true; } catch (error) { - this.emit('error', `Connect to MELCloud error: ${error}.`); + this.emit('error', `Connect to MELCloud error: ${error.message ?? error}.`); }; } @@ -195,7 +195,7 @@ class MelCloud extends EventEmitter { } return true; } catch (error) { - throw new Error(`Scanning for devices error: ${error}.`); + throw new Error(`Scanning for devices error: ${error.message ?? error}.`); }; } @@ -205,7 +205,7 @@ class MelCloud extends EventEmitter { const debug3 = this.enableDebugMode ? this.emit('debug', `Data saved to path: ${path}.`) : false; return true; } catch (error) { - throw new Error(`Save data to path: ${path}, error: ${error}`); + throw new Error(`Save data to path: ${path}, error: ${error.message ?? error}`); } } @@ -219,7 +219,7 @@ class MelCloud extends EventEmitter { await this.saveData(this.accountInfoFile, accountInfo); return true; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/melcloudata.js b/src/melcloudata.js index e2926fc..8909c00 100644 --- a/src/melcloudata.js +++ b/src/melcloudata.js @@ -14,7 +14,7 @@ class MelCloudAta extends EventEmitter { const accountInfoFile = config.accountInfoFile; const deviceInfoFile = config.deviceInfoFile; const debugLog = config.debugLog; - const refreshInterval= config.refreshInterval; + const refreshInterval = config.refreshInterval; //set default values this.deviceData = {}; @@ -127,13 +127,13 @@ class MelCloudAta extends EventEmitter { const legacyDevice = device.LegacyDevice; const unitSupportsStandbyMode = device.UnitSupportsStandbyMode; const isSplitSystem = device.IsSplitSystem; - const hasHalfDegreeIncrements = device.HasHalfDegreeIncrements ?? false; - const hasOutdoorTemperature = device.HasOutdoorTemperature ?? false + const hasHalfDegreeIncrements = device.HasHalfDegreeIncrements; + const hasOutdoorTemperature = device.HasOutdoorTemperature; const modelIsAirCurtain = device.ModelIsAirCurtain; - const modelSupportsFanSpeed = device.ModelSupportsFanSpeed ?? false; - const modelSupportsAuto = device.ModelSupportsAuto ?? false; - const modelSupportsHeat = device.ModelSupportsHeat ?? false; - const modelSupportsDry = device.ModelSupportsDry ?? false; + const modelSupportsFanSpeed = device.ModelSupportsFanSpeed; + const modelSupportsAuto = device.ModelSupportsAuto; + const modelSupportsHeat = device.ModelSupportsHeat; + const modelSupportsDry = device.ModelSupportsDry; const modelSupportsVaneVertical = device.ModelSupportsVaneVertical; const modelSupportsVaneHorizontal = device.ModelSupportsVaneHorizontal; const modelSupportsWideVane = device.ModelSupportsWideVane; @@ -357,7 +357,7 @@ class MelCloudAta extends EventEmitter { const debug2 = debugLog ? this.emit('debug', `Device State: ${JSON.stringify(deviceState, null, 2)}`) : false; this.emit('deviceState', deviceData, deviceState, useFahrenheit); } catch (error) { - this.emit('error', `Check device error: ${error}.`); + this.emit('error', `Check device error: ${error.message ?? error}.`); }; }).on('state', () => { }); @@ -370,7 +370,7 @@ class MelCloudAta extends EventEmitter { const data = savedData.length > 0 ? JSON.parse(savedData) : false; return data; } catch (error) { - throw new Error(`Read data from path: ${path}, error: ${error}`); + throw new Error(`Read data from path: ${path}, error: ${error.message ?? error}`); } } @@ -415,7 +415,7 @@ class MelCloudAta extends EventEmitter { this.emit('deviceState', this.deviceData, deviceState, this.useFahrenheit); return true;; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/melcloudatw.js b/src/melcloudatw.js index 6b756da..c7c4414 100644 --- a/src/melcloudatw.js +++ b/src/melcloudatw.js @@ -14,7 +14,7 @@ class MelCloudAtw extends EventEmitter { const accountInfoFile = config.accountInfoFile; const deviceInfoFile = config.deviceInfoFile; const debugLog = config.debugLog; - const refreshInterval= config.refreshInterval; + const refreshInterval = config.refreshInterval; //set default values this.deviceData = {}; @@ -428,7 +428,7 @@ class MelCloudAtw extends EventEmitter { const debug2 = debugLog ? this.emit('debug', `Device State: ${JSON.stringify(deviceState, null, 2)}`) : false; this.emit('deviceState', deviceData, deviceState, useFahrenheit); } catch (error) { - this.emit('error', `Check device error: ${error}.`); + this.emit('error', `Check device error: ${error.message ?? error}.`); }; }).on('state', () => { }); @@ -441,7 +441,7 @@ class MelCloudAtw extends EventEmitter { const data = savedData.length > 0 ? JSON.parse(savedData) : false; return data; } catch (error) { - throw new Error(`Read data from path: ${path}, error: ${error}`); + throw new Error(`Read data from path: ${path}, error: ${error.message ?? error}`); } } @@ -468,7 +468,7 @@ class MelCloudAtw extends EventEmitter { this.emit('deviceState', this.deviceData, deviceState, this.useFahrenheit); return true; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; }; diff --git a/src/melclouderv.js b/src/melclouderv.js index ee413d3..710e386 100644 --- a/src/melclouderv.js +++ b/src/melclouderv.js @@ -345,7 +345,7 @@ class MelCloudErv extends EventEmitter { const debug2 = debugLog ? this.emit('debug', `Device State: ${JSON.stringify(deviceState, null, 2)}`) : false; this.emit('deviceState', deviceData, deviceState, useFahrenheit); } catch (error) { - this.emit('error', `Check device error: ${error}.`); + this.emit('error', `Check device error: ${error.message ?? error}.`); }; }).on('state', () => { }); @@ -358,7 +358,7 @@ class MelCloudErv extends EventEmitter { const data = savedData.length > 0 ? JSON.parse(savedData) : false; return data;; } catch (error) { - throw new Error(`Read data from path: ${path}, error: ${error}`); + throw new Error(`Read data from path: ${path}, error: ${error.message ?? error}`); } } @@ -379,7 +379,7 @@ class MelCloudErv extends EventEmitter { this.emit('deviceState', this.deviceData, deviceState, this.useFahrenheit); return true; } catch (error) { - throw new Error(error); + throw new Error(error.message ?? error); }; }; };