homebridge-melcloud-control 3.9.4 → 3.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
16
  - do not configure it manually, always using Config UI X
17
17
  - required Homebridge v2.0.0 and above
18
18
 
19
+ ## [3.9.5] - (02.09.2025)
20
+
21
+ ## Changes
22
+
23
+ - fix ERV start error
24
+
25
+ ## [3.9.4] - (02.09.2025)
26
+
27
+ ## Changes
28
+
29
+ - fix [#213](https://github.com/grzegorz914/homebridge-melcloud-control/issues/213)
30
+
19
31
  ## [3.9.0] - (18.08.2025)
20
32
 
21
33
  ## Changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "3.9.4",
4
+ "version": "3.9.6",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/deviceata.js CHANGED
@@ -46,8 +46,8 @@ class DeviceAta extends EventEmitter {
46
46
  //presets configured
47
47
  this.presetsConfigured = [];
48
48
  for (const preset of this.presets) {
49
- const displayType = preset.displayType ?? 0;
50
- if (displayType === 0) {
49
+ const displayType = preset.displayType;
50
+ if (!displayType) {
51
51
  continue;
52
52
  };
53
53
 
@@ -65,8 +65,8 @@ class DeviceAta extends EventEmitter {
65
65
  //buttons configured
66
66
  this.buttonsConfigured = [];
67
67
  for (const button of this.buttons) {
68
- const displayType = button.displayType ?? 0;
69
- if (displayType === 0) {
68
+ const displayType = button.displayType;
69
+ if (!displayType) {
70
70
  continue;
71
71
  };
72
72
 
@@ -1128,19 +1128,17 @@ class DeviceAta extends EventEmitter {
1128
1128
  };
1129
1129
 
1130
1130
  //update characteristics
1131
- if (this.melCloudService) {
1132
- this.melCloudService
1133
- .updateCharacteristic(Characteristic.Active, power ? 1 : 0)
1134
- .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, obj.currentOperationMode)
1135
- .updateCharacteristic(Characteristic.TargetHeaterCoolerState, obj.targetOperationMode)
1136
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1137
- .updateCharacteristic(Characteristic.LockPhysicalControls, obj.lockPhysicalControl)
1138
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit)
1139
- .updateCharacteristic(Characteristic.CoolingThresholdTemperature, defaultCoolingSetTemperature);
1140
- const updateDefHeat = modelSupportsHeat ? this.melCloudService.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature) : false;
1141
- const updateRS = modelSupportsFanSpeed ? this.melCloudService.updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed) : false;
1142
- const updateSM = swingFunction ? this.melCloudService.updateCharacteristic(Characteristic.SwingMode, obj.swingMode) : false;
1143
- };
1131
+ this.melCloudService
1132
+ ?.updateCharacteristic(Characteristic.Active, power ? 1 : 0)
1133
+ .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, obj.currentOperationMode)
1134
+ .updateCharacteristic(Characteristic.TargetHeaterCoolerState, obj.targetOperationMode)
1135
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1136
+ .updateCharacteristic(Characteristic.LockPhysicalControls, obj.lockPhysicalControl)
1137
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit)
1138
+ .updateCharacteristic(Characteristic.CoolingThresholdTemperature, defaultCoolingSetTemperature);
1139
+ const updateDefHeat = modelSupportsHeat ? this.melCloudService?.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature) : false;
1140
+ const updateRS = modelSupportsFanSpeed ? this.melCloudService?.updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed) : false;
1141
+ const updateSM = swingFunction ? this.melCloudService?.updateCharacteristic(Characteristic.SwingMode, obj.swingMode) : false;
1144
1142
  break;
1145
1143
  case 2: //Thermostat
1146
1144
  switch (operationMode) {
@@ -1188,27 +1186,18 @@ class DeviceAta extends EventEmitter {
1188
1186
  obj.operationModeSetPropsValidValues = modelSupportsAuto && modelSupportsHeat ? [0, 1, 2, 3] : !modelSupportsAuto && modelSupportsHeat ? [0, 1, 2] : modelSupportsAuto && !modelSupportsHeat ? [0, 2, 3] : [0, 2];
1189
1187
 
1190
1188
  //update characteristics
1191
- if (this.melCloudService) {
1192
- this.melCloudService
1193
- .updateCharacteristic(Characteristic.CurrentHeatingCoolingState, obj.currentOperationMode)
1194
- .updateCharacteristic(Characteristic.TargetHeatingCoolingState, obj.targetOperationMode)
1195
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1196
- .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1197
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1198
- };
1189
+ this.melCloudService
1190
+ ?.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, obj.currentOperationMode)
1191
+ .updateCharacteristic(Characteristic.TargetHeatingCoolingState, obj.targetOperationMode)
1192
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1193
+ .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1194
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1199
1195
  break;
1200
1196
  };
1201
1197
  this.accessory = obj;
1202
1198
 
1203
- if (this.roomTemperatureSensorService) {
1204
- this.roomTemperatureSensorService
1205
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1206
- };
1207
-
1208
- if (this.outdoorTemperatureSensorService) {
1209
- this.outdoorTemperatureSensorService
1210
- .updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature)
1211
- };
1199
+ this.roomTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature);
1200
+ this.outdoorTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature);
1212
1201
 
1213
1202
  //update presets state
1214
1203
  if (this.presetsConfigured.length > 0) {
@@ -1222,11 +1211,8 @@ class DeviceAta extends EventEmitter {
1222
1211
  && presetData.VaneVertical === vaneVerticalDirection
1223
1212
  && presetData.FanSpeed === fanSpeed) : false;
1224
1213
 
1225
- if (this.presetsServices) {
1226
- const characteristicType = preset.characteristicType;
1227
- this.presetsServices[i]
1228
- .updateCharacteristic(characteristicType, preset.state)
1229
- };
1214
+ const characteristicType = preset.characteristicType;
1215
+ this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1230
1216
  });
1231
1217
  };
1232
1218
 
@@ -1346,11 +1332,8 @@ class DeviceAta extends EventEmitter {
1346
1332
  };
1347
1333
 
1348
1334
  //update services
1349
- if (this.buttonsServices) {
1350
- const characteristicType = button.characteristicType;
1351
- this.buttonsServices[i]
1352
- .updateCharacteristic(characteristicType, button.state)
1353
- };
1335
+ const characteristicType = button.characteristicType;
1336
+ this.buttonsServices?.[i]?.updateCharacteristic(characteristicType, button.state);
1354
1337
  });
1355
1338
  };
1356
1339
 
package/src/deviceatw.js CHANGED
@@ -51,8 +51,8 @@ class DeviceAtw extends EventEmitter {
51
51
  //presets configured
52
52
  this.presetsConfigured = [];
53
53
  for (const preset of this.presets) {
54
- const displayType = preset.displayType ?? 0;
55
- if (displayType === 0) {
54
+ const displayType = preset.displayType;
55
+ if (!displayType) {
56
56
  continue;
57
57
  };
58
58
 
@@ -70,8 +70,8 @@ class DeviceAtw extends EventEmitter {
70
70
  //buttons configured
71
71
  this.buttonsConfigured = [];
72
72
  for (const button of this.buttons) {
73
- const displayType = button.displayType ?? 0;
74
- if (displayType === 0) {
73
+ const displayType = button.displayType;
74
+ if (!displayType) {
75
75
  continue;
76
76
  };
77
77
 
@@ -271,7 +271,6 @@ class DeviceAtw extends EventEmitter {
271
271
  };
272
272
  }
273
273
 
274
-
275
274
  async startImpulseGenerator() {
276
275
  try {
277
276
  //start impule generator
@@ -1564,17 +1563,14 @@ class DeviceAtw extends EventEmitter {
1564
1563
  };
1565
1564
 
1566
1565
  //update characteristics
1567
- if (this.melCloudServices) {
1568
- this.melCloudServices[i]
1569
- .updateCharacteristic(Characteristic.Active, power)
1570
- .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, currentOperationMode)
1571
- .updateCharacteristic(Characteristic.TargetHeaterCoolerState, targetOperationMode)
1572
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1573
- .updateCharacteristic(Characteristic.LockPhysicalControls, lockPhysicalControl)
1574
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1575
- const updateDefCool = heatCoolModes === 0 || heatCoolModes === 2 ? this.melCloudServices[i].updateCharacteristic(Characteristic.CoolingThresholdTemperature, setTemperature) : false;
1576
- const updateDefHeat = heatCoolModes === 0 || heatCoolModes === 1 ? this.melCloudServices[i].updateCharacteristic(Characteristic.HeatingThresholdTemperature, setTemperature) : false;
1577
- }
1566
+ this.melCloudServices?.[i]
1567
+ ?.updateCharacteristic(Characteristic.Active, power)
1568
+ .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, currentOperationMode)
1569
+ .updateCharacteristic(Characteristic.TargetHeaterCoolerState, targetOperationMode)
1570
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1571
+ .updateCharacteristic(Characteristic.LockPhysicalControls, lockPhysicalControl)
1572
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1573
+ const updateDefCool = heatCoolModes === 0 || heatCoolModes === 2 ? this.melCloudServices?.[i]?.updateCharacteristic(Characteristic.CoolingThresholdTemperature, setTemperature) : false;
1578
1574
  break;
1579
1575
  case 2: //Thermostat
1580
1576
  switch (i) {
@@ -1671,14 +1667,12 @@ class DeviceAtw extends EventEmitter {
1671
1667
  };
1672
1668
 
1673
1669
  //update characteristics
1674
- if (this.melCloudServices) {
1675
- this.melCloudServices[i]
1676
- .updateCharacteristic(Characteristic.CurrentHeatingCoolingState, currentOperationMode)
1677
- .updateCharacteristic(Characteristic.TargetHeatingCoolingState, targetOperationMode)
1678
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1679
- .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1680
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1681
- }
1670
+ this.melCloudServices?.[i]
1671
+ ?.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, currentOperationMode)
1672
+ .updateCharacteristic(Characteristic.TargetHeatingCoolingState, targetOperationMode)
1673
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1674
+ .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1675
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1682
1676
  break;
1683
1677
  };
1684
1678
 
@@ -1748,20 +1742,9 @@ class DeviceAtw extends EventEmitter {
1748
1742
  returnTemperature = returnTemperatureHeatPump;
1749
1743
 
1750
1744
  //updte characteristics
1751
- if (this.roomTemperatureSensorService) {
1752
- this.roomTemperatureSensorService
1753
- .updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature)
1754
- };
1755
-
1756
- if (this.flowTemperatureSensorService) {
1757
- this.flowTemperatureSensorService
1758
- .updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureHeatPump)
1759
- };
1760
-
1761
- if (this.returnTemperatureSensorService) {
1762
- this.returnTemperatureSensorService
1763
- .updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureHeatPump)
1764
- };
1745
+ this.roomTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature);
1746
+ this.flowTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureHeatPump);
1747
+ this.returnTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureHeatPump);
1765
1748
  break;
1766
1749
  case caseZone1Sensor: //Zone 1
1767
1750
  name = zone1Name;
@@ -1770,20 +1753,9 @@ class DeviceAtw extends EventEmitter {
1770
1753
  returnTemperature = returnTemperatureZone1;
1771
1754
 
1772
1755
  //updte characteristics
1773
- if (this.roomTemperatureZone1SensorService) {
1774
- this.roomTemperatureZone1SensorService
1775
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperatureZone1)
1776
- };
1777
-
1778
- if (this.flowTemperatureZone1SensorService) {
1779
- this.flowTemperatureZone1SensorService
1780
- .updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureZone1)
1781
- };
1782
-
1783
- if (this.returnTemperatureZone1SensorService) {
1784
- this.returnTemperatureZone1SensorService
1785
- .updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureZone1)
1786
- };
1756
+ this.roomTemperatureZone1SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, roomTemperatureZone1);
1757
+ this.flowTemperatureZone1SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureZone1);
1758
+ this.returnTemperatureZone1SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureZone1);
1787
1759
  break;
1788
1760
  case caseHotWaterSensor: //Hot Water
1789
1761
  name = hotWaterName;
@@ -1792,20 +1764,9 @@ class DeviceAtw extends EventEmitter {
1792
1764
  returnTemperature = returnTemperatureWaterTank;
1793
1765
 
1794
1766
  //updte characteristics
1795
- if (this.roomTemperatureWaterTankSensorService) {
1796
- this.roomTemperatureWaterTankSensorService
1797
- .updateCharacteristic(Characteristic.CurrentTemperature, tankWaterTemperature)
1798
- };
1799
-
1800
- if (this.flowTemperatureWaterTankSensorService) {
1801
- this.flowTemperatureWaterTankSensorService
1802
- .updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureWaterTank)
1803
- };
1804
-
1805
- if (this.returnTemperatureWaterTankSensorService) {
1806
- this.returnTemperatureWaterTankSensorService
1807
- .updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureWaterTank)
1808
- };
1767
+ this.roomTemperatureWaterTankSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, tankWaterTemperature);
1768
+ this.flowTemperatureWaterTankSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureWaterTank);
1769
+ this.returnTemperatureWaterTankSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureWaterTank);
1809
1770
  break;
1810
1771
  case caseZone2Sensor: //Zone 2
1811
1772
  name = zone2Name;
@@ -1814,20 +1775,9 @@ class DeviceAtw extends EventEmitter {
1814
1775
  returnTemperature = returnTemperatureZone2;
1815
1776
 
1816
1777
  //updte characteristics
1817
- if (this.roomTemperatureZone2SensorService) {
1818
- this.roomTemperatureZone2SensorService
1819
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperatureZone2)
1820
- };
1821
-
1822
- if (this.flowTemperatureZone2SensorService) {
1823
- this.flowTemperatureZone2SensorService
1824
- .updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureZone2)
1825
- };
1826
-
1827
- if (this.returnTemperatureZone2SensorService) {
1828
- this.returnTemperatureZone2SensorService
1829
- .updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureZone2)
1830
- };
1778
+ this.roomTemperatureZone2SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, roomTemperatureZone2);
1779
+ this.flowTemperatureZone2SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, flowTemperatureZone2);
1780
+ this.returnTemperatureZone2SensorService?.updateCharacteristic(Characteristic.CurrentTemperature, returnTemperatureZone2);
1831
1781
  break;
1832
1782
  };
1833
1783
 
@@ -1886,18 +1836,14 @@ class DeviceAtw extends EventEmitter {
1886
1836
  && presetData.SetCoolFlowTemperatureZone1 === setCoolFlowTemperatureZone1
1887
1837
  && presetData.SetCoolFlowTemperatureZone2 === setCoolFlowTemperatureZone2) : false;
1888
1838
 
1889
- if (this.presetsServices) {
1890
- const characteristicType = preset.characteristicType;
1891
- this.presetsServices[i]
1892
- .updateCharacteristic(characteristicType, preset.state)
1893
- };
1839
+ const characteristicType = preset.characteristicType;
1840
+ this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1894
1841
  });
1895
1842
  };
1896
1843
 
1897
1844
  //update buttons state
1898
1845
  if (this.buttonsConfiguredCount > 0) {
1899
- for (let i = 0; i < this.buttonsConfiguredCount; i++) {
1900
- const button = this.buttonsConfigured[i];
1846
+ this.buttonsConfigured.forEach((button, i) => {
1901
1847
  const mode = button.mode;
1902
1848
  switch (mode) {
1903
1849
  case 0: //POWER ON,OFF
@@ -1973,7 +1919,11 @@ class DeviceAtw extends EventEmitter {
1973
1919
  this.emit('warn', `Unknown button mode: ${mode} detected`);
1974
1920
  break;
1975
1921
  };
1976
- };
1922
+
1923
+ //update services
1924
+ const characteristicType = button.characteristicType;
1925
+ this.buttonsServices?.[i]?.updateCharacteristic(characteristicType, button.state);
1926
+ });
1977
1927
  };
1978
1928
  })
1979
1929
  .on('success', (success) => this.emit('success', success))
package/src/deviceerv.js CHANGED
@@ -44,8 +44,8 @@ class DeviceErv extends EventEmitter {
44
44
  //presets configured
45
45
  this.presetsConfigured = [];
46
46
  for (const preset of this.presets) {
47
- const displayType = preset.displayType ?? 0;
48
- if (displayType === 0) {
47
+ const displayType = preset.displayType;
48
+ if (!displayType) {
49
49
  continue;
50
50
  };
51
51
 
@@ -63,8 +63,8 @@ class DeviceErv extends EventEmitter {
63
63
  //buttons configured
64
64
  this.buttonsConfigured = [];
65
65
  for (const button of this.buttons) {
66
- const displayType = button.displayType ?? 0;
67
- if (displayType === 0) {
66
+ const displayType = button.displayType;
67
+ if (!displayType) {
68
68
  continue;
69
69
  };
70
70
 
@@ -1038,18 +1038,16 @@ class DeviceErv extends EventEmitter {
1038
1038
  };
1039
1039
 
1040
1040
  //update characteristics
1041
- if (this.melCloudService) {
1042
- this.melCloudService
1043
- .updateCharacteristic(Characteristic.Active, power)
1044
- .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, obj.currentOperationMode)
1045
- .updateCharacteristic(Characteristic.TargetHeaterCoolerState, obj.targetOperationMode)
1046
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1047
- .updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed)
1048
- .updateCharacteristic(Characteristic.LockPhysicalControls, obj.lockPhysicalControl)
1049
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1050
- const updateDefCool = hasCoolOperationMode ? this.melCloudService.updateCharacteristic(Characteristic.CoolingThresholdTemperature, defaultCoolingSetTemperature) : false;
1051
- const updateDefHeat = hasHeatOperationMode ? this.melCloudService.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature) : false;
1052
- };
1041
+ this.melCloudService
1042
+ ?.updateCharacteristic(Characteristic.Active, power)
1043
+ .updateCharacteristic(Characteristic.CurrentHeaterCoolerState, obj.currentOperationMode)
1044
+ .updateCharacteristic(Characteristic.TargetHeaterCoolerState, obj.targetOperationMode)
1045
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1046
+ .updateCharacteristic(Characteristic.RotationSpeed, obj.fanSpeed)
1047
+ .updateCharacteristic(Characteristic.LockPhysicalControls, obj.lockPhysicalControl)
1048
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1049
+ const updateDefCool = hasCoolOperationMode ? this.melCloudService?.updateCharacteristic(Characteristic.CoolingThresholdTemperature, defaultCoolingSetTemperature) : false;
1050
+ const updateDefHeat = hasHeatOperationMode ? this.melCloudService?.updateCharacteristic(Characteristic.HeatingThresholdTemperature, defaultHeatingSetTemperature) : false;
1053
1051
  break;
1054
1052
  case 2: //Thermostat
1055
1053
  //operation mode - 0, HEAT, 2, COOL, 4, 5, 6, FAN, AUTO
@@ -1088,59 +1086,36 @@ class DeviceErv extends EventEmitter {
1088
1086
  obj.operationModeSetPropsValidValues = hasAutoVentilationMode ? (hasBypassVentilationMode ? [0, 1, 2, 3] : [0, 2, 3]) : (hasBypassVentilationMode ? [0, 1, 2] : [0, 2]);
1089
1087
 
1090
1088
  //update characteristics
1091
- if (this.melCloudService) {
1092
- this.melCloudService
1093
- .updateCharacteristic(Characteristic.CurrentHeatingCoolingState, obj.currentOperationMode)
1094
- .updateCharacteristic(Characteristic.TargetHeatingCoolingState, obj.targetOperationMode)
1095
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1096
- .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1097
- .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1098
- };
1089
+ this.melCloudService
1090
+ ?.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, obj.currentOperationMode)
1091
+ .updateCharacteristic(Characteristic.TargetHeatingCoolingState, obj.targetOperationMode)
1092
+ .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1093
+ .updateCharacteristic(Characteristic.TargetTemperature, setTemperature)
1094
+ .updateCharacteristic(Characteristic.TemperatureDisplayUnits, obj.useFahrenheit);
1099
1095
  break;
1100
1096
  };
1101
1097
  this.accessory = obj;
1102
1098
 
1103
1099
  //update temperature sensors
1104
- if (this.roomTemperatureSensorService) {
1105
- this.roomTemperatureSensorService
1106
- .updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature)
1107
- };
1108
-
1109
- if (this.outdoorTemperatureSensorService) {
1110
- this.outdoorTemperatureSensorService
1111
- .updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature)
1112
- };
1113
-
1114
- if (this.supplyTemperatureSensorService) {
1115
- this.supplyTemperatureSensorService
1116
- .updateCharacteristic(Characteristic.CurrentTemperature, supplyTemperature)
1117
- };
1100
+ this.roomTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature);
1101
+ this.outdoorTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, outdoorTemperature);
1102
+ this.supplyTemperatureSensorService?.updateCharacteristic(Characteristic.CurrentTemperature, supplyTemperature);
1118
1103
 
1119
1104
  //update core maintenance
1120
- if (this.coreMaintenanceService) {
1121
- this.coreMaintenanceService
1122
- .updateCharacteristic(Characteristic.FilterChangeIndication, coreMaintenanceRequired)
1123
- }
1105
+ this.coreMaintenanceService?.updateCharacteristic(Characteristic.FilterChangeIndication, coreMaintenanceRequired);
1124
1106
 
1125
1107
  //update filter maintenance
1126
- if (this.filterMaintenanceService) {
1127
- this.filterMaintenanceService
1128
- .updateCharacteristic(Characteristic.FilterChangeIndication, filterMaintenanceRequired)
1129
- }
1108
+ this.filterMaintenanceService?.updateCharacteristic(Characteristic.FilterChangeIndication, filterMaintenanceRequired);
1130
1109
 
1131
1110
  //update CO2 sensor
1132
- if (this.carbonDioxideSensorService) {
1133
- this.carbonDioxideSensorService
1134
- .updateCharacteristic(Characteristic.CarbonDioxideDetected, roomCO2Detected)
1135
- .updateCharacteristic(Characteristic.CarbonDioxideLevel, roomCO2Level)
1136
- }
1111
+ this.carbonDioxideSensorService
1112
+ ?.updateCharacteristic(Characteristic.CarbonDioxideDetected, roomCO2Detected)
1113
+ .updateCharacteristic(Characteristic.CarbonDioxideLevel, roomCO2Level);
1137
1114
 
1138
1115
  //update PM2.5 sensor
1139
- if (this.airQualitySensorService) {
1140
- this.airQualitySensorService
1141
- .updateCharacteristic(Characteristic.AirQuality, pM25AirQuality)
1142
- .updateCharacteristic(Characteristic.PM2_5Density, pM25Level)
1143
- }
1116
+ this.airQualitySensorService
1117
+ ?.updateCharacteristic(Characteristic.AirQuality, pM25AirQuality)
1118
+ .updateCharacteristic(Characteristic.PM2_5Density, pM25Level);
1144
1119
 
1145
1120
  //update presets state
1146
1121
  if (this.presetsConfigured.length > 0) {
@@ -1153,11 +1128,8 @@ class DeviceErv extends EventEmitter {
1153
1128
  && presetData.VentilationMode === ventilationMode
1154
1129
  && presetData.FanSpeed === setFanSpeed) : false;
1155
1130
 
1156
- if (this.presetsServices) {
1157
- const characteristicType = preset.characteristicType;
1158
- this.presetsServices[i]
1159
- .updateCharacteristic(characteristicType, preset.state)
1160
- };
1131
+ const characteristicType = preset.characteristicType;
1132
+ this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1161
1133
  });
1162
1134
  };
1163
1135
 
@@ -1214,11 +1186,8 @@ class DeviceErv extends EventEmitter {
1214
1186
  };
1215
1187
 
1216
1188
  //update services
1217
- if (this.buttonsServices) {
1218
- const characteristicType = button.characteristicType;
1219
- this.buttonsServices[i]
1220
- .updateCharacteristic(characteristicType, button.state)
1221
- };
1189
+ const characteristicType = button.characteristicType;
1190
+ this.buttonsServices?.[i]?.updateCharacteristic(characteristicType, button.state);
1222
1191
  });
1223
1192
  };
1224
1193
 
@@ -1260,7 +1229,7 @@ class DeviceErv extends EventEmitter {
1260
1229
  await this.melCloudErv.checkState();
1261
1230
 
1262
1231
  //prepare accessory
1263
- const accessory = await this.prepareAccessory(deviceData);
1232
+ const accessory = await this.prepareAccessory();
1264
1233
  return accessory;
1265
1234
  } catch (error) {
1266
1235
  throw new Error(`Start error: ${error}`);
@@ -0,0 +1,31 @@
1
+ import { promises as fsPromises } from 'fs';
2
+
3
+ class Functions {
4
+ constructor(config) {
5
+ }
6
+
7
+ async saveData(path, data) {
8
+ try {
9
+ data = JSON.stringify(data, null, 2);
10
+ await fsPromises.writeFile(path, data);
11
+ return true;
12
+ } catch (error) {
13
+ throw new Error(`Save data error: ${error}`);
14
+ }
15
+ }
16
+
17
+ async readData(path) {
18
+ try {
19
+ const data = await fsPromises.readFile(path);
20
+ return data;
21
+ } catch (error) {
22
+ throw new Error(`Read data error: ${error}`);
23
+ }
24
+ }
25
+
26
+ async scaleValue(value, inMin, inMax, outMin, outMax) {
27
+ const scaledValue = parseFloat((((Math.max(inMin, Math.min(inMax, value)) - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin).toFixed(0));
28
+ return scaledValue;
29
+ }
30
+ }
31
+ export default Functions
package/src/melcloud.js CHANGED
@@ -1,8 +1,8 @@
1
- import { promises as fsPromises } from 'fs';
2
1
  import { Agent } from 'https';
3
2
  import axios from 'axios';
4
3
  import EventEmitter from 'events';
5
4
  import ImpulseGenerator from './impulsegenerator.js';
5
+ import Functions from './functions.js';
6
6
  import { ApiUrls } from './constants.js';
7
7
 
8
8
  class MelCloud extends EventEmitter {
@@ -15,6 +15,7 @@ class MelCloud extends EventEmitter {
15
15
  this.requestConfig = requestConfig;
16
16
  this.devicesId = [];
17
17
  this.contextKey = '';
18
+ this.functions = new Functions();
18
19
 
19
20
  this.options = {
20
21
  data: {
@@ -42,8 +43,73 @@ class MelCloud extends EventEmitter {
42
43
  };
43
44
  };
44
45
 
46
+ async chackDevicesList(contextKey) {
47
+ try {
48
+ //create axios instance get
49
+ const axiosInstanceGet = axios.create({
50
+ method: 'GET',
51
+ baseURL: ApiUrls.BaseURL,
52
+ timeout: 10000,
53
+ headers: {
54
+ 'X-MitsContextKey': contextKey
55
+ },
56
+ maxContentLength: 100000000,
57
+ maxBodyLength: 1000000000,
58
+ withCredentials: true,
59
+ httpsAgent: new Agent({
60
+ keepAlive: false,
61
+ rejectUnauthorized: false
62
+ })
63
+ });
64
+
65
+ if (this.enableDebugMode) this.emit('debug', `Scanning for devices`);
66
+ const listDevicesData = await axiosInstanceGet(ApiUrls.ListDevices);
67
+ const buildingsList = listDevicesData.data;
68
+ if (this.enableDebugMode) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
69
+
70
+ if (!buildingsList) {
71
+ this.emit('warn', `No building found`);
72
+ return null;
73
+ }
74
+
75
+ //save buildings to the file
76
+ await this.functions.saveData(this.buildingsFile, buildingsList);
77
+ if (this.enableDebugMode) this.emit('debug', `Buildings list saved`);
78
+
79
+ //read buildings structure and get the devices
80
+ const devices = [];
81
+ for (const building of buildingsList) {
82
+ const buildingStructure = building.Structure;
83
+
84
+ //get all devices from the building structure
85
+ const allDevices = [
86
+ ...buildingStructure.Floors.flatMap(floor => [...floor.Areas.flatMap(area => area.Devices), ...floor.Devices]),
87
+ ...buildingStructure.Areas.flatMap(area => area.Devices),
88
+ ...buildingStructure.Devices
89
+ ];
90
+
91
+ //add all devices to the devices array
92
+ devices.push(...allDevices);
93
+ }
94
+
95
+ const devicesCount = devices.length;
96
+ if (devicesCount === 0) {
97
+ this.emit('warn', `No devices found`);
98
+ return null;
99
+ }
100
+
101
+ //save buildings to the file
102
+ await this.functions.saveData(this.devicesFile, devices);
103
+ if (this.enableDebugMode) this.emit('debug', `${devicesCount} devices saved`);
104
+
105
+ return devices;
106
+ } catch (error) {
107
+ throw new Error(`Check devices list error: ${error}`);
108
+ };
109
+ }
110
+
45
111
  async connect() {
46
- if (this.enableDebugMode) this.emit('debug', `Connecting to MELCloud`);
112
+ if (this.enableDebugMode) this.emit('debug', `Connecting to MELCloud`);
47
113
 
48
114
  try {
49
115
  const axiosInstanceLogin = axios.create({
@@ -75,7 +141,7 @@ class MelCloud extends EventEmitter {
75
141
  MapLongitude: 'removed',
76
142
  MapLatitude: 'removed'
77
143
  };
78
- if (this.enableDebugMode) this.emit('debug', `MELCloud Info: ${JSON.stringify(debugData, null, 2)}`);
144
+ if (this.enableDebugMode) this.emit('debug', `MELCloud Info: ${JSON.stringify(debugData, null, 2)}`);
79
145
 
80
146
  if (contextKey === undefined || contextKey === null) {
81
147
  this.emit('warn', `Context key: ${contextKey}, missing`)
@@ -101,7 +167,7 @@ class MelCloud extends EventEmitter {
101
167
  });
102
168
 
103
169
  //save melcloud info to the file
104
- await this.saveData(this.accountFile, accountInfo);
170
+ await this.functions.saveData(this.accountFile, accountInfo);
105
171
 
106
172
  //emit connect success
107
173
  this.emit('success', `Connect to MELCloud Success`)
@@ -118,81 +184,6 @@ class MelCloud extends EventEmitter {
118
184
  };
119
185
  }
120
186
 
121
- async chackDevicesList(contextKey) {
122
- try {
123
- //create axios instance get
124
- const axiosInstanceGet = axios.create({
125
- method: 'GET',
126
- baseURL: ApiUrls.BaseURL,
127
- timeout: 10000,
128
- headers: {
129
- 'X-MitsContextKey': contextKey
130
- },
131
- maxContentLength: 100000000,
132
- maxBodyLength: 1000000000,
133
- withCredentials: true,
134
- httpsAgent: new Agent({
135
- keepAlive: false,
136
- rejectUnauthorized: false
137
- })
138
- });
139
-
140
- if (this.enableDebugMode) this.emit('debug', `Scanning for devices`);
141
- const listDevicesData = await axiosInstanceGet(ApiUrls.ListDevices);
142
- const buildingsList = listDevicesData.data;
143
- if (this.enableDebugMode) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
144
-
145
- if (!buildingsList) {
146
- this.emit('warn', `No building found`);
147
- return null;
148
- }
149
-
150
- //save buildings to the file
151
- await this.saveData(this.buildingsFile, buildingsList);
152
- if (this.enableDebugMode) this.emit('debug', `Buildings list saved`);
153
-
154
- //read buildings structure and get the devices
155
- const devices = [];
156
- for (const building of buildingsList) {
157
- const buildingStructure = building.Structure;
158
-
159
- //get all devices from the building structure
160
- const allDevices = [
161
- ...buildingStructure.Floors.flatMap(floor => [...floor.Areas.flatMap(area => area.Devices), ...floor.Devices]),
162
- ...buildingStructure.Areas.flatMap(area => area.Devices),
163
- ...buildingStructure.Devices
164
- ];
165
-
166
- //add all devices to the devices array
167
- devices.push(...allDevices);
168
- }
169
-
170
- const devicesCount = devices.length;
171
- if (devicesCount === 0) {
172
- this.emit('warn', `No devices found`);
173
- return null;
174
- }
175
-
176
- //save buildings to the file
177
- await this.saveData(this.devicesFile, devices);
178
- if (this.enableDebugMode) this.emit('debug', `${devicesCount} devices saved`);
179
-
180
- return devices;
181
- } catch (error) {
182
- throw new Error(`Check devices list error: ${error}`);
183
- };
184
- }
185
-
186
- async saveData(path, data) {
187
- try {
188
- await fsPromises.writeFile(path, JSON.stringify(data, null, 2));
189
- if (this.enableDebugMode) this.emit('debug', `Data saved to: ${path}`);
190
- return true;
191
- } catch (error) {
192
- throw new Error(`Save data error: ${error}`);
193
- }
194
- }
195
-
196
187
  async send(accountInfo) {
197
188
  try {
198
189
  const options = {
@@ -200,7 +191,7 @@ class MelCloud extends EventEmitter {
200
191
  };
201
192
 
202
193
  await this.axiosInstancePost(ApiUrls.UpdateApplicationOptions, options);
203
- await this.saveData(this.accountFile, accountInfo);
194
+ await this.functions.saveData(this.accountFile, accountInfo);
204
195
  return true;
205
196
  } catch (error) {
206
197
  throw new Error(`Send data error: ${error}`);
@@ -1,8 +1,8 @@
1
- import { promises as fsPromises } from 'fs';
2
1
  import { Agent } from 'https';
3
2
  import axios from 'axios';
4
3
  import EventEmitter from 'events';
5
4
  import ImpulseGenerator from './impulsegenerator.js';
5
+ import Functions from './functions.js';
6
6
  import { ApiUrls } from './constants.js';
7
7
 
8
8
  class MelCloudAta extends EventEmitter {
@@ -11,6 +11,7 @@ class MelCloudAta extends EventEmitter {
11
11
  this.devicesFile = config.devicesFile;
12
12
  this.deviceId = config.deviceId;
13
13
  this.enableDebugMode = config.enableDebugMode;
14
+ this.functions = new Functions();
14
15
 
15
16
  //set default values
16
17
  this.deviceState = {};
@@ -30,22 +31,36 @@ class MelCloudAta extends EventEmitter {
30
31
  })
31
32
  });
32
33
 
33
- this.impulseGenerator = new ImpulseGenerator();
34
- this.impulseGenerator.on('checkState', async () => {
35
- try {
34
+ //lock flags
35
+ this.locks = {
36
+ checkState: false,
37
+ };
38
+ this.impulseGenerator = new ImpulseGenerator()
39
+ .on('checkState', () => this.handleWithLock('checkState', async () => {
36
40
  await this.checkState();
37
- } catch (error) {
38
- this.emit('error', `Impulse generator error: ${error}`);
39
- };
40
- }).on('state', (state) => {
41
- this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}`);
42
- });
43
- };
41
+ }))
42
+ .on('state', (state) => {
43
+ this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}.`);
44
+ });
45
+ }
46
+
47
+ async handleWithLock(lockKey, fn) {
48
+ if (this.locks[lockKey]) return;
49
+
50
+ this.locks[lockKey] = true;
51
+ try {
52
+ await fn();
53
+ } catch (error) {
54
+ this.emit('error', `Inpulse generator error: ${error}`);
55
+ } finally {
56
+ this.locks[lockKey] = false;
57
+ }
58
+ }
44
59
 
45
60
  async checkState() {
46
61
  try {
47
62
  //read device info from file
48
- const devicesData = await this.readData(this.devicesFile);
63
+ const devicesData = await this.functions.readData(this.devicesFile);
49
64
 
50
65
  if (!Array.isArray(devicesData)) {
51
66
  this.emit('warn', `Device data not found`);
@@ -361,25 +376,6 @@ class MelCloudAta extends EventEmitter {
361
376
  };
362
377
  };
363
378
 
364
-
365
- async readData(path) {
366
- try {
367
- const savedData = await fsPromises.readFile(path)
368
- if (savedData.toString().trim().length === 0) {
369
- return null;
370
- }
371
-
372
- try {
373
- const data = JSON.parse(savedData);
374
- return data;
375
- } catch (error) {
376
- throw new Error(`Parse JSON error: ${error}`);
377
- }
378
- } catch (error) {
379
- throw new Error(`Read data error: ${error}`);
380
- }
381
- }
382
-
383
379
  async send(deviceData, displayMode) {
384
380
  try {
385
381
  //set target temp based on display mode and operation mode
@@ -1,8 +1,8 @@
1
- import { promises as fsPromises } from 'fs';
2
1
  import { Agent } from 'https';
3
2
  import axios from 'axios';
4
3
  import EventEmitter from 'events';
5
4
  import ImpulseGenerator from './impulsegenerator.js';
5
+ import Functions from './functions.js';
6
6
  import { ApiUrls } from './constants.js';
7
7
 
8
8
  class MelCloudAtw extends EventEmitter {
@@ -11,6 +11,7 @@ class MelCloudAtw extends EventEmitter {
11
11
  this.devicesFile = config.devicesFile;
12
12
  this.deviceId = config.deviceId;
13
13
  this.enableDebugMode = config.enableDebugMode;
14
+ this.functions = new Functions();
14
15
 
15
16
  //set default values
16
17
  this.deviceState = {};
@@ -30,22 +31,36 @@ class MelCloudAtw extends EventEmitter {
30
31
  })
31
32
  });
32
33
 
33
- this.impulseGenerator = new ImpulseGenerator();
34
- this.impulseGenerator.on('checkState', async () => {
35
- try {
34
+ //lock flags
35
+ this.locks = {
36
+ checkState: false,
37
+ };
38
+ this.impulseGenerator = new ImpulseGenerator()
39
+ .on('checkState', () => this.handleWithLock('checkState', async () => {
36
40
  await this.checkState();
37
- } catch (error) {
38
- this.emit('error', `Impulse generator error: ${error}`);
39
- };
40
- }).on('state', (state) => {
41
- this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}`);
42
- });
43
- };
41
+ }))
42
+ .on('state', (state) => {
43
+ this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}.`);
44
+ });
45
+ }
46
+
47
+ async handleWithLock(lockKey, fn) {
48
+ if (this.locks[lockKey]) return;
49
+
50
+ this.locks[lockKey] = true;
51
+ try {
52
+ await fn();
53
+ } catch (error) {
54
+ this.emit('error', `Inpulse generator error: ${error}`);
55
+ } finally {
56
+ this.locks[lockKey] = false;
57
+ }
58
+ }
44
59
 
45
60
  async checkState() {
46
61
  try {
47
62
  //read device info from file
48
- const devicesData = await this.readData(this.devicesFile);
63
+ const devicesData = await this.functions.readData(this.devicesFile);
49
64
 
50
65
  if (!Array.isArray(devicesData)) {
51
66
  this.emit('warn', `Device data not found`);
@@ -425,24 +440,6 @@ class MelCloudAtw extends EventEmitter {
425
440
  };
426
441
  };
427
442
 
428
- async readData(path) {
429
- try {
430
- const savedData = await fsPromises.readFile(path)
431
- if (savedData.toString().trim().length === 0) {
432
- return null;
433
- }
434
-
435
- try {
436
- const data = JSON.parse(savedData);
437
- return data;
438
- } catch (error) {
439
- throw new Error(`Parse JSON error: ${error}`);
440
- }
441
- } catch (error) {
442
- throw new Error(`Read data error: ${error}`);
443
- }
444
- }
445
-
446
443
  async send(deviceData) {
447
444
  try {
448
445
  //prevent to set out of range temp
@@ -1,8 +1,8 @@
1
- import { promises as fsPromises } from 'fs';
2
1
  import { Agent } from 'https';
3
2
  import axios from 'axios';
4
3
  import EventEmitter from 'events';
5
4
  import ImpulseGenerator from './impulsegenerator.js';
5
+ import Functions from './functions.js';
6
6
  import { ApiUrls } from './constants.js';
7
7
 
8
8
  class MelCloudErv extends EventEmitter {
@@ -11,6 +11,7 @@ class MelCloudErv extends EventEmitter {
11
11
  this.devicesFile = config.devicesFile;
12
12
  this.deviceId = config.deviceId;
13
13
  this.enableDebugMode = config.enableDebugMode;
14
+ this.functions = new Functions();
14
15
 
15
16
  //set default values
16
17
  this.deviceState = {};
@@ -30,22 +31,36 @@ class MelCloudErv extends EventEmitter {
30
31
  })
31
32
  });
32
33
 
33
- this.impulseGenerator = new ImpulseGenerator();
34
- this.impulseGenerator.on('checkState', async () => {
35
- try {
34
+ //lock flags
35
+ this.locks = {
36
+ checkState: false,
37
+ };
38
+ this.impulseGenerator = new ImpulseGenerator()
39
+ .on('checkState', () => this.handleWithLock('checkState', async () => {
36
40
  await this.checkState();
37
- } catch (error) {
38
- this.emit('error', `Impulse generator error: ${error}`);
39
- };
40
- }).on('state', (state) => {
41
- this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}`);
42
- });
43
- };
41
+ }))
42
+ .on('state', (state) => {
43
+ this.emit('success', `Impulse generator ${state ? 'started' : 'stopped'}.`);
44
+ });
45
+ }
46
+
47
+ async handleWithLock(lockKey, fn) {
48
+ if (this.locks[lockKey]) return;
49
+
50
+ this.locks[lockKey] = true;
51
+ try {
52
+ await fn();
53
+ } catch (error) {
54
+ this.emit('error', `Inpulse generator error: ${error}`);
55
+ } finally {
56
+ this.locks[lockKey] = false;
57
+ }
58
+ }
44
59
 
45
60
  async checkState() {
46
61
  try {
47
62
  //read device info from file
48
- const devicesData = await this.readData(this.devicesFile);
63
+ const devicesData = await this.functions.readData(this.devicesFile);
49
64
 
50
65
  if (!Array.isArray(devicesData)) {
51
66
  this.emit('warn', `Device data not found`);
@@ -349,24 +364,6 @@ class MelCloudErv extends EventEmitter {
349
364
  };
350
365
  };
351
366
 
352
- async readData(path) {
353
- try {
354
- const savedData = await fsPromises.readFile(path)
355
- if (savedData.toString().trim().length === 0) {
356
- return null;
357
- }
358
-
359
- try {
360
- const data = JSON.parse(savedData);
361
- return data;
362
- } catch (error) {
363
- throw new Error(`Parse JSON error: ${error}`);
364
- }
365
- } catch (error) {
366
- throw new Error(`Read data error: ${error}`);
367
- }
368
- }
369
-
370
367
  async send(deviceData, displayMode) {
371
368
  try {
372
369
  //set target temp based on display mode and ventilation mode