homebridge-melcloud-control 4.3.2-beta.0 → 4.3.2-beta.10

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/README.md CHANGED
@@ -91,6 +91,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
91
91
  * Shedule active.
92
92
  * Scene control.
93
93
  * In standby.
94
+ * Is connected.
94
95
  * Error.
95
96
  * Heat Pump:
96
97
  * Heater Cooler:
@@ -148,6 +149,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
148
149
  * Shedule active.
149
150
  * Scene control.
150
151
  * In standby.
152
+ * Is connected.
151
153
  * Error.
152
154
  * Energy Recovery Ventilation Lossnay:
153
155
  * Heater Cooler:
@@ -186,6 +188,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
186
188
  * Shedule active.
187
189
  * Scene control.
188
190
  * In standby.
191
+ * Is connected.
189
192
  * Error.
190
193
 
191
194
  ### HOME app current device mode display
@@ -1287,6 +1287,15 @@
1287
1287
  "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloudhome';"
1288
1288
  }
1289
1289
  },
1290
+ "frostProtectionSupport": {
1291
+ "title": "Frost Protection Support",
1292
+ "type": "boolean",
1293
+ "default": false,
1294
+ "description": "This enable extra frost protection control and sensors to use with automations in HomeKit app.",
1295
+ "condition": {
1296
+ "functionBody": "return model.accounts[arrayIndices[0]].type === 'melcloudhome';"
1297
+ }
1298
+ },
1290
1299
  "refreshInterval": {
1291
1300
  "title": "Refresh Interval",
1292
1301
  "type": "integer",
@@ -2769,6 +2778,7 @@
2769
2778
  "items": [
2770
2779
  "accounts[].atwDevices[].hideZone",
2771
2780
  "accounts[].atwDevices[].name",
2781
+ "accounts[].atwDevices[].frostProtectionSupport",
2772
2782
  "accounts[].atwDevices[].holidayModeSupport",
2773
2783
  "accounts[].atwDevices[].refreshInterval"
2774
2784
  ],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.3.2-beta.0",
4
+ "version": "4.3.2-beta.10",
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
@@ -649,7 +649,7 @@ class DeviceAta extends EventEmitter {
649
649
  return state;
650
650
  })
651
651
  accessory.addService(this.roomTemperatureSensorService);
652
- };
652
+ }
653
653
 
654
654
  if (this.temperatureOutdoorSensor && supportsOutdoorTemperature && this.accessory.outdoorTemperature !== null) {
655
655
  if (this.logDebug) this.emit('debug', `Prepare outdoor temperature sensor service`);
@@ -662,7 +662,7 @@ class DeviceAta extends EventEmitter {
662
662
  return state;
663
663
  })
664
664
  accessory.addService(this.outdoorTemperatureSensorService);
665
- };
665
+ }
666
666
 
667
667
  //in standby sensor
668
668
  if (this.inStandbySensor && this.accessory.inStandbyMode !== null) {
@@ -922,7 +922,7 @@ class DeviceAta extends EventEmitter {
922
922
  accessory.addService(presetControlSensorService);
923
923
  }
924
924
  });
925
- };
925
+ }
926
926
 
927
927
  //schedules services
928
928
  if (this.schedules.length > 0 && this.accessory.scheduleEnabled !== null) {
@@ -996,7 +996,7 @@ class DeviceAta extends EventEmitter {
996
996
  accessory.addService(scheduleSensorService);
997
997
  }
998
998
  });
999
- };
999
+ }
1000
1000
 
1001
1001
  //scenes
1002
1002
  if (this.scenes.length > 0) {
@@ -1055,7 +1055,7 @@ class DeviceAta extends EventEmitter {
1055
1055
  accessory.addService(sceneControlSensorService);
1056
1056
  }
1057
1057
  });
1058
- };
1058
+ }
1059
1059
 
1060
1060
  //buttons services
1061
1061
  if (this.buttons.length > 0) {
@@ -1318,7 +1318,7 @@ class DeviceAta extends EventEmitter {
1318
1318
  accessory.addService(buttonControlSensorService);
1319
1319
  }
1320
1320
  });
1321
- };
1321
+ }
1322
1322
 
1323
1323
  return accessory;
1324
1324
  } catch (error) {
@@ -1690,7 +1690,7 @@ class DeviceAta extends EventEmitter {
1690
1690
  //sensor
1691
1691
  if (preset.displayType < 7) this.presetControlSensorServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1692
1692
  });
1693
- };
1693
+ }
1694
1694
 
1695
1695
  ///schedules
1696
1696
  if (this.schedules.length > 0 && scheduleEnabled !== null) {
@@ -1710,7 +1710,7 @@ class DeviceAta extends EventEmitter {
1710
1710
  //sensor
1711
1711
  if (schedule.displayType < 7) this.scheduleSensorServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1712
1712
  });
1713
- };
1713
+ }
1714
1714
 
1715
1715
  //scenes
1716
1716
  if (this.scenes.length > 0) {
@@ -1727,7 +1727,7 @@ class DeviceAta extends EventEmitter {
1727
1727
  //sensor
1728
1728
  if (scene.displayType < 7) this.sceneControlSensorServices?.[i]?.updateCharacteristic(characteristicType, scene.state);
1729
1729
  });
1730
- };
1730
+ }
1731
1731
 
1732
1732
  //buttons
1733
1733
  if (this.buttons.length > 0) {
@@ -1851,7 +1851,7 @@ class DeviceAta extends EventEmitter {
1851
1851
  //sensor
1852
1852
  if (button.displayType < 7) this.buttonControlSensorServices?.[i]?.updateCharacteristic(characteristicType, button.state);
1853
1853
  });
1854
- };
1854
+ }
1855
1855
 
1856
1856
  //log current state
1857
1857
  if (this.logInfo) {
@@ -1868,7 +1868,8 @@ class DeviceAta extends EventEmitter {
1868
1868
  if (supportsSwingFunction) this.emit('info', `Air direction: ${AirConditioner.AirDirectionMapEnumToString[obj.currentSwingMode]}`);
1869
1869
  this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
1870
1870
  this.emit('info', `Lock physical controls: ${obj.lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
1871
- };
1871
+ if (this.accountType === 'melcloudhome') this.emit('info', `Signal strength: ${deviceData.Rssi}dBm`);
1872
+ }
1872
1873
  })
1873
1874
  .on('success', (success) => this.emit('success', success))
1874
1875
  .on('info', (info) => this.emit('info', info))
package/src/deviceatw.js CHANGED
@@ -44,6 +44,7 @@ class DeviceAtw extends EventEmitter {
44
44
  this.inStandbySensor = device.inStandbySensor || false;
45
45
  this.connectSensor = device.connectSensor || false;
46
46
  this.errorSensor = device.errorSensor || false;
47
+ this.frostProtectionSupport = device.frostProtectionSupport || false;
47
48
  this.holidayModeSupport = device.holidayModeSupport || false;
48
49
  this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
49
50
  this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
@@ -851,7 +852,7 @@ class DeviceAtw extends EventEmitter {
851
852
  break;
852
853
  };
853
854
  });
854
- };
855
+ }
855
856
 
856
857
  //sensor services
857
858
  if (zonesSensorsCount > 0) {
@@ -1022,7 +1023,7 @@ class DeviceAtw extends EventEmitter {
1022
1023
  break;
1023
1024
  };
1024
1025
  });
1025
- };
1026
+ }
1026
1027
 
1027
1028
  //in standby sensor
1028
1029
  if (this.inStandbySensor && this.accessory.inStandbyMode !== null) {
@@ -1066,6 +1067,53 @@ class DeviceAtw extends EventEmitter {
1066
1067
  accessory.addService(this.errorService);
1067
1068
  }
1068
1069
 
1070
+ //frost protection
1071
+ if (this.frostProtectionSupport && this.accessory.frostProtectionEnabled !== null) {
1072
+ //control
1073
+ if (this.logDebug) this.emit('debug', `Prepare frost protection control service`);
1074
+ this.frostProtectionControlService = new Service.Switch(`${serviceName} Frost Protection`, `frostProtectionControlService${deviceId}`);
1075
+ this.frostProtectionControlService.addOptionalCharacteristic(Characteristic.ConfiguredName);
1076
+ this.frostProtectionControlService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
1077
+ this.frostProtectionControlService.getCharacteristic(Characteristic.On)
1078
+ .onGet(async () => {
1079
+ const state = this.accessory.frostProtectionEnabled;
1080
+ return state;
1081
+ })
1082
+ .onSet(async (state) => {
1083
+ try {
1084
+ deviceData.FrostProtection.Enabled = state;
1085
+ if (this.logInfo) this.emit('info', `Frost protection: ${state ? 'Enabled' : 'Disabled'}`);
1086
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
1087
+ } catch (error) {
1088
+ if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
1089
+ };
1090
+ });
1091
+ accessory.addService(this.frostProtectionControlService);
1092
+
1093
+ if (this.logDebug) this.emit('debug', `Prepare frost protection control sensor service`);
1094
+ this.frostProtectionControlSensorService = new Service.ContactSensor(`${serviceName} Frost Protection Control`, `frostProtectionControlSensorService${deviceId}`);
1095
+ this.frostProtectionControlSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
1096
+ this.frostProtectionControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection Control`);
1097
+ this.frostProtectionControlSensorService.getCharacteristic(Characteristic.ContactSensorState)
1098
+ .onGet(async () => {
1099
+ const state = this.accessory.frostProtectionEnabled;
1100
+ return state;
1101
+ })
1102
+ accessory.addService(this.frostProtectionControlSensorService);
1103
+
1104
+ //sensor
1105
+ if (this.logDebug) this.emit('debug', `Prepare frost protection service`);
1106
+ this.frostProtectionSensorService = new Service.ContactSensor(`${serviceName} Frost Protection`, `frostProtectionSensorService${deviceId}`);
1107
+ this.frostProtectionSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
1108
+ this.frostProtectionSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
1109
+ this.frostProtectionSensorService.getCharacteristic(Characteristic.ContactSensorState)
1110
+ .onGet(async () => {
1111
+ const state = this.accessory.frostProtectionActive;
1112
+ return state;
1113
+ })
1114
+ accessory.addService(this.frostProtectionSensorService);
1115
+ }
1116
+
1069
1117
  //holiday mode
1070
1118
  if (this.holidayModeSupport && this.accessory.holidayModeEnabled !== null) {
1071
1119
  //control
@@ -1189,7 +1237,7 @@ class DeviceAtw extends EventEmitter {
1189
1237
  accessory.addService(presetControlSensorService);
1190
1238
  }
1191
1239
  });
1192
- };
1240
+ }
1193
1241
 
1194
1242
  //schedules services
1195
1243
  if (this.schedules.length > 0 && this.accessory.scheduleEnabled !== null) {
@@ -1263,7 +1311,7 @@ class DeviceAtw extends EventEmitter {
1263
1311
  accessory.addService(scheduleSensorService);
1264
1312
  }
1265
1313
  });
1266
- };
1314
+ }
1267
1315
 
1268
1316
  //scenes
1269
1317
  if (this.scenes.length > 0) {
@@ -1322,7 +1370,7 @@ class DeviceAtw extends EventEmitter {
1322
1370
  accessory.addService(sceneControlSensorService);
1323
1371
  }
1324
1372
  });
1325
- };
1373
+ }
1326
1374
 
1327
1375
  //buttons services
1328
1376
  if (this.buttons.length > 0) {
@@ -1517,7 +1565,7 @@ class DeviceAtw extends EventEmitter {
1517
1565
  accessory.addService(buttonControlSensorService);
1518
1566
  }
1519
1567
  });
1520
- };
1568
+ }
1521
1569
 
1522
1570
  return accessory;
1523
1571
  } catch (error) {
@@ -1574,6 +1622,10 @@ class DeviceAtw extends EventEmitter {
1574
1622
  const holidayModeEnabled = accountTypeMelcloud ? holidayMode : deviceData.HolidayMode?.Enabled;
1575
1623
  const holidayModeActive = deviceData.HolidayMode?.Active ?? false;
1576
1624
 
1625
+ //protection
1626
+ const frostProtectionEnabled = deviceData.FrostProtection?.Enabled;
1627
+ const frostProtectionActive = deviceData.FrostProtection?.Active ?? false;
1628
+
1577
1629
  //device info
1578
1630
  const supportsStanbyMode = deviceData.Device[supportStandbyKey];
1579
1631
  const supportsHeatPump = ![1, 2, 3, 4, 5, 6, 7, 15].includes(this.hideZone);
@@ -1681,6 +1733,8 @@ class DeviceAtw extends EventEmitter {
1681
1733
  temperatureUnit: TemperatureDisplayUnits[this.accountInfo.useFahrenheit ? 1 : 0],
1682
1734
  isConnected: isConnected,
1683
1735
  isInError: isInError,
1736
+ frostProtectionEnabled: frostProtectionEnabled,
1737
+ frostProtectionActive: frostProtectionActive,
1684
1738
  scheduleEnabled: scheduleEnabled,
1685
1739
  holidayModeEnabled: holidayModeEnabled,
1686
1740
  holidayModeActive: holidayModeActive,
@@ -1947,39 +2001,40 @@ class DeviceAtw extends EventEmitter {
1947
2001
  let operationModeText = '';
1948
2002
  switch (i) {
1949
2003
  case caseHeatPump: //Heat Pump - HEAT, COOL, OFF
1950
- this.emit('info', `${heatPumpName}, Power: ${power ? 'On' : 'Off'}`)
1951
- this.emit('info', `${heatPumpName}, Operation mode: ${HeatPump.SystemMapEnumToString[unitStatus]}`);
1952
- this.emit('info', `${heatPumpName},'Outdoor temperature: ${roomTemperature}${obj.temperatureUnit}`);
1953
- this.emit('info', `${heatPumpName}, Temperature display unit: ${obj.temperatureUnit}`);
1954
- this.emit('info', `${heatPumpName}, Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
2004
+ this.emit('info', `Power: ${power ? 'On' : 'Off'}`)
2005
+ this.emit('info', `Operation mode: ${HeatPump.SystemMapEnumToString[unitStatus]}`);
2006
+ this.emit('info', `Outdoor temperature: ${roomTemperature}${obj.temperatureUnit}`);
2007
+ this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
2008
+ this.emit('info', `Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
2009
+ if (this.accountType === 'melcloudhome') this.emit('info', `Signal strength: ${deviceData.Rssi}dBm`);
1955
2010
  break;
1956
2011
  case caseZone1: //Zone 1 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
1957
2012
  operationModeText = idleZone1 ? HeatPump.ZoneOperationMapEnumToString[6] : HeatPump.ZoneOperationMapEnumToString[operationModeZone1];
1958
- this.emit('info', `${zone1Name}, Operation mode: ${operationModeText}`);
1959
- this.emit('info', `${zone1Name}, Temperature: ${roomTemperature}${obj.temperatureUnit}`);
1960
- this.emit('info', `${zone1Name}, Target temperature: ${setTemperature}${obj.temperatureUnit}`)
1961
- this.emit('info', `${zone1Name}, Temperature display unit: ${obj.temperatureUnit}`);
1962
- this.emit('info', `${zone1Name}, Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
2013
+ this.emit('info', `Operation mode: ${operationModeText}`);
2014
+ this.emit('info', `Temperature: ${roomTemperature}${obj.temperatureUnit}`);
2015
+ this.emit('info', `Target temperature: ${setTemperature}${obj.temperatureUnit}`)
2016
+ this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
2017
+ this.emit('info', `Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
1963
2018
  break;
1964
2019
  case caseHotWater: //Hot Water - AUTO, HEAT NOW
1965
2020
  operationModeText = operationMode === 1 ? HeatPump.ForceDhwMapEnumToString[1] : HeatPump.ForceDhwMapEnumToString[forcedHotWaterMode ? 1 : 0];
1966
- this.emit('info', `${hotWaterName}, Operation mode: ${operationModeText}`);
1967
- this.emit('info', `${hotWaterName}, Temperature: ${roomTemperature}${obj.temperatureUnit}`);
1968
- this.emit('info', `${hotWaterName}, Target temperature: ${setTemperature}${obj.temperatureUnit}`)
1969
- this.emit('info', `${hotWaterName}, Temperature display unit: ${obj.temperatureUnit}`);
1970
- this.emit('info', `${hotWaterName}, Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
2021
+ this.emit('info', `Operation mode: ${operationModeText}`);
2022
+ this.emit('info', `Temperature: ${roomTemperature}${obj.temperatureUnit}`);
2023
+ this.emit('info', `Target temperature: ${setTemperature}${obj.temperatureUnit}`)
2024
+ this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
2025
+ this.emit('info', `Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
1971
2026
  break;
1972
2027
  case caseZone2: //Zone 2 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
1973
2028
  operationModeText = idleZone2 ? HeatPump.ZoneOperationMapEnumToString[6] : HeatPump.ZoneOperationMapEnumToString[operationModeZone2];
1974
- this.emit('info', `${zone2Name}, Operation mode: ${operationModeText}`);
1975
- this.emit('info', `${zone2Name}, Temperature: ${roomTemperature}${obj.temperatureUnit}`);
1976
- this.emit('info', `${zone2Name}, Target temperature: ${setTemperature}${obj.temperatureUnit}`)
1977
- this.emit('info', `${zone2Name}, Temperature display unit: ${obj.temperatureUnit}`);
1978
- this.emit('info', `${zone2Name}, Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
2029
+ this.emit('info', `Operation mode: ${operationModeText}`);
2030
+ this.emit('info', `Temperature: ${roomTemperature}${obj.temperatureUnit}`);
2031
+ this.emit('info', `Target temperature: ${setTemperature}${obj.temperatureUnit}`)
2032
+ this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
2033
+ this.emit('info', `Lock physical controls: ${lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
1979
2034
  break;
1980
2035
  };
1981
- };
1982
- };
2036
+ }
2037
+ }
1983
2038
 
1984
2039
  //update sensors characteristics
1985
2040
  for (let i = 0; i < zonesSensorsCount; i++) {
@@ -2064,7 +2119,7 @@ class DeviceAtw extends EventEmitter {
2064
2119
  break;
2065
2120
  };
2066
2121
  };
2067
- };
2122
+ }
2068
2123
  this.accessory = obj;
2069
2124
 
2070
2125
  //update services
@@ -2078,6 +2133,13 @@ class DeviceAtw extends EventEmitter {
2078
2133
  this.connectService?.updateCharacteristic(Characteristic.ContactSensorState, isConnected);
2079
2134
  this.errorService?.updateCharacteristic(Characteristic.ContactSensorState, isInError);
2080
2135
 
2136
+ //frost protection
2137
+ if (this.frostProtectionSupport && frostProtectionEnabled !== null) {
2138
+ this.frostProtectionControlService?.updateCharacteristic(Characteristic.On, frostProtectionEnabled);
2139
+ this.frostProtectionControlSensorService?.updateCharacteristic(Characteristic.ContactSensorState, frostProtectionEnabled);
2140
+ this.frostProtectionSensorService?.updateCharacteristic(Characteristic.ContactSensorState, frostProtectionActive);
2141
+ }
2142
+
2081
2143
  //holiday mode
2082
2144
  if (this.holidayModeSupport && holidayModeEnabled !== null) {
2083
2145
  this.holidayModeControlService?.updateCharacteristic(Characteristic.On, holidayModeEnabled);
@@ -2112,7 +2174,7 @@ class DeviceAtw extends EventEmitter {
2112
2174
  //sensor
2113
2175
  if (preset.displayType < 7) this.presetControlSensorServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
2114
2176
  });
2115
- };
2177
+ }
2116
2178
 
2117
2179
  ///schedules
2118
2180
  if (this.schedules.length > 0 && scheduleEnabled !== null) {
@@ -2132,7 +2194,7 @@ class DeviceAtw extends EventEmitter {
2132
2194
  //sensor
2133
2195
  if (schedule.displayType < 7) this.scheduleSensorServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
2134
2196
  });
2135
- };
2197
+ }
2136
2198
 
2137
2199
  //scenes
2138
2200
  if (this.scenes.length > 0) {
@@ -2149,7 +2211,7 @@ class DeviceAtw extends EventEmitter {
2149
2211
  //sensor
2150
2212
  if (scene.displayType < 7) this.sceneControlSensorServices?.[i]?.updateCharacteristic(characteristicType, scene.state);
2151
2213
  });
2152
- };
2214
+ }
2153
2215
 
2154
2216
  //buttons
2155
2217
  if (this.buttons.length > 0) {
@@ -2237,7 +2299,7 @@ class DeviceAtw extends EventEmitter {
2237
2299
  //sensor
2238
2300
  if (button.displayType < 7) this.buttonControlSensorServices?.[i]?.updateCharacteristic(characteristicType, button.state);
2239
2301
  });
2240
- };
2302
+ }
2241
2303
  })
2242
2304
  .on('success', (success) => this.emit('success', success))
2243
2305
  .on('info', (info) => this.emit('info', info))
package/src/deviceerv.js CHANGED
@@ -586,7 +586,7 @@ class DeviceErv extends EventEmitter {
586
586
  return state;
587
587
  })
588
588
  accessory.addService(this.roomTemperatureSensorService);
589
- };
589
+ }
590
590
 
591
591
  //temperature sensor service supply
592
592
  if (this.temperatureSupplySensor && supportsSupplyTemperature && this.accessory.supplyTemperature !== null) {
@@ -600,7 +600,7 @@ class DeviceErv extends EventEmitter {
600
600
  return state;
601
601
  })
602
602
  accessory.addService(this.supplyTemperatureSensorService);
603
- };
603
+ }
604
604
 
605
605
  //temperature sensor service outdoor
606
606
  if (this.temperatureOutdoorSensor && supportsOutdoorTemperature && this.accessory.outdoorTemperature !== null) {
@@ -614,7 +614,7 @@ class DeviceErv extends EventEmitter {
614
614
  return state;
615
615
  })
616
616
  accessory.addService(this.outdoorTemperatureSensorService);
617
- };
617
+ }
618
618
 
619
619
  //core maintenance
620
620
  if (this.accessory.coreMaintenanceRequired !== null) {
@@ -849,7 +849,7 @@ class DeviceErv extends EventEmitter {
849
849
  accessory.addService(presetControlSensorService);
850
850
  }
851
851
  });
852
- };
852
+ }
853
853
 
854
854
  //schedules services
855
855
  if (this.schedules.length > 0 && this.accessory.scheduleEnabled !== null) {
@@ -923,7 +923,7 @@ class DeviceErv extends EventEmitter {
923
923
  accessory.addService(scheduleSensorService);
924
924
  }
925
925
  });
926
- };
926
+ }
927
927
 
928
928
  //scenes
929
929
  if (this.scenes.length > 0) {
@@ -982,7 +982,7 @@ class DeviceErv extends EventEmitter {
982
982
  accessory.addService(sceneControlSensorService);
983
983
  }
984
984
  });
985
- };
985
+ }
986
986
 
987
987
  //buttons services
988
988
  if (this.buttons.length > 0) {
@@ -1118,7 +1118,7 @@ class DeviceErv extends EventEmitter {
1118
1118
  accessory.addService(buttonControlSensorService);
1119
1119
  }
1120
1120
  });
1121
- };
1121
+ }
1122
1122
 
1123
1123
  return accessory;
1124
1124
  } catch (error) {
@@ -1454,7 +1454,7 @@ class DeviceErv extends EventEmitter {
1454
1454
  //sensor
1455
1455
  if (preset.displayType < 7) this.presetControlSensorServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1456
1456
  });
1457
- };
1457
+ }
1458
1458
 
1459
1459
  ///schedules
1460
1460
  if (this.schedules.length > 0 && scheduleEnabled !== null) {
@@ -1474,7 +1474,7 @@ class DeviceErv extends EventEmitter {
1474
1474
  //sensor
1475
1475
  if (schedule.displayType < 7) this.scheduleSensorServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1476
1476
  });
1477
- };
1477
+ }
1478
1478
 
1479
1479
  //scenes
1480
1480
  if (this.scenes.length > 0) {
@@ -1491,7 +1491,7 @@ class DeviceErv extends EventEmitter {
1491
1491
  //sensor
1492
1492
  if (scene.displayType < 7) this.sceneControlSensorServices?.[i]?.updateCharacteristic(characteristicType, scene.state);
1493
1493
  });
1494
- };
1494
+ }
1495
1495
 
1496
1496
  //buttons
1497
1497
  if (this.buttons.length > 0) {
@@ -1552,7 +1552,7 @@ class DeviceErv extends EventEmitter {
1552
1552
  //sensor
1553
1553
  if (button.displayType < 7) this.buttonControlSensorServices?.[i]?.updateCharacteristic(characteristicType, button.state);
1554
1554
  });
1555
- };
1555
+ }
1556
1556
 
1557
1557
  //log current state
1558
1558
  if (this.logInfo) {
@@ -1571,7 +1571,8 @@ class DeviceErv extends EventEmitter {
1571
1571
  if (supportsCO2Sensor) this.emit('info', `CO2 level: ${roomCO2Level} ppm`);
1572
1572
  if (supportsPM25Sensor) this.emit('info', `PM2.5 air quality: ${Ventilation.PM25AirQualityMapEnumToString[pM25AirQuality]}`);
1573
1573
  if (supportsPM25Sensor) this.emit('info', `PM2.5 level: ${pM25Level} µg/m`);
1574
- };
1574
+ if (this.accountType === 'melcloudhome') this.emit('info', `Signal strength: ${deviceData.Rssi}dBm`);
1575
+ }
1575
1576
  })
1576
1577
  .on('success', (success) => this.emit('success', success))
1577
1578
  .on('info', (info) => this.emit('info', info))
package/src/functions.js CHANGED
@@ -160,5 +160,13 @@ class Functions extends EventEmitter {
160
160
  return v !== undefined && v !== null && !(typeof v === 'number' && Number.isNaN(v));
161
161
  }
162
162
 
163
+ convertValue(value) {
164
+ let parsedValue = value;
165
+ if (value === "True") parsedValue = true;
166
+ else if (value === "False") parsedValue = false;
167
+ else if (!isNaN(value) && value !== "") parsedValue = Number(value);
168
+ return parsedValue;
169
+ }
170
+
163
171
  }
164
172
  export default Functions
@@ -70,17 +70,8 @@ class MelCloudAta extends EventEmitter {
70
70
  this.socketConnected = false;
71
71
  }
72
72
 
73
- async checkState() {
73
+ async updateState(deviceData) {
74
74
  try {
75
-
76
- //read device info from file
77
- const devicesData = await this.functions.readData(this.devicesFile, true);
78
- if (!devicesData) return;
79
-
80
- this.headers = devicesData.Headers;
81
- const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
82
- deviceData.Scenes = devicesData.Scenes ?? [];
83
-
84
75
  if (this.accountType === 'melcloudhome') {
85
76
  deviceData.Device.OperationMode = AirConditioner.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
86
77
  deviceData.Device.ActualFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.ActualFanSpeed] ?? deviceData.Device.ActualFanSpeed;
@@ -93,87 +84,6 @@ class MelCloudAta extends EventEmitter {
93
84
  deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
94
85
  deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
95
86
 
96
- //web cocket connection
97
- if (!this.connecting && !this.socketConnected) {
98
- this.connecting = true;
99
-
100
- const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
101
- try {
102
- const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
103
- .on('error', (error) => {
104
- if (this.logError) this.emit('error', `Socket error: ${error}`);
105
- socket.close();
106
- })
107
- .on('close', () => {
108
- if (this.logDebug) this.emit('debug', `Socket closed`);
109
- this.cleanupSocket();
110
- })
111
- .on('open', () => {
112
- this.socket = socket;
113
- this.socketConnected = true;
114
- this.connecting = false;
115
- if (this.logSuccess) this.emit('success', `Socket Connect Success`);
116
-
117
- // heartbeat
118
- this.heartbeat = setInterval(() => {
119
- if (socket.readyState === socket.OPEN) {
120
- if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
121
- socket.ping();
122
- }
123
- }, 30000);
124
- })
125
- .on('pong', () => {
126
- if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
127
- })
128
- .on('message', (message) => {
129
- const parsedMessage = JSON.parse(message);
130
- const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
131
- if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
132
- if (parsedMessage.message === 'Forbidden') return;
133
-
134
- const messageData = parsedMessage?.[0]?.Data;
135
- if (!messageData) return;
136
-
137
- let updateDeviceState = false;
138
- const unitId = messageData?.id;
139
- switch (unitId) {
140
- case this.deviceId:
141
- const messageType = parsedMessage[0].messageType;
142
- switch (messageType) {
143
- case 'unitStateChanged':
144
- const settings = Object.fromEntries(
145
- messageData.settings.map(({ name, value }) => {
146
- let parsedValue = value;
147
- if (value === "True") parsedValue = true;
148
- else if (value === "False") parsedValue = false;
149
- else if (!isNaN(value) && value !== "") parsedValue = Number(value);
150
- return [name, parsedValue];
151
- })
152
- );
153
- Object.assign(deviceData.Device, settings);
154
- updateDeviceState = true;
155
- break;
156
- case 'unitWifiSignalChanged':
157
- Object.assign(deviceData, messageData.rssi);
158
- updateDeviceState = true;
159
- break;
160
- default:
161
- if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
162
- return;
163
- }
164
- break;
165
- default:
166
- if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
167
- return;
168
- }
169
-
170
- if (updateDeviceState) this.emit('deviceState', deviceData);
171
- });
172
- } catch (error) {
173
- if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
174
- this.cleanupSocket();
175
- }
176
- }
177
87
  }
178
88
  if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
179
89
 
@@ -233,6 +143,107 @@ class MelCloudAta extends EventEmitter {
233
143
  };
234
144
  };
235
145
 
146
+ async checkState() {
147
+ try {
148
+
149
+ //read device info from file
150
+ const devicesData = await this.functions.readData(this.devicesFile, true);
151
+ if (!devicesData) return;
152
+
153
+ this.headers = devicesData.Headers;
154
+ const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
155
+ deviceData.Scenes = devicesData.Scenes ?? [];
156
+
157
+ //web cocket connection
158
+ if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
159
+ this.connecting = true;
160
+
161
+ const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
162
+ try {
163
+ const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
164
+ .on('error', (error) => {
165
+ if (this.logError) this.emit('error', `Socket error: ${error}`);
166
+ socket.close();
167
+ })
168
+ .on('close', () => {
169
+ if (this.logDebug) this.emit('debug', `Socket closed`);
170
+ this.cleanupSocket();
171
+ })
172
+ .on('open', () => {
173
+ this.socket = socket;
174
+ this.socketConnected = true;
175
+ this.connecting = false;
176
+ if (this.logSuccess) this.emit('success', `Socket Connect Success`);
177
+
178
+ // heartbeat
179
+ this.heartbeat = setInterval(() => {
180
+ if (socket.readyState === socket.OPEN) {
181
+ if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
182
+ socket.ping();
183
+ }
184
+ }, 30000);
185
+ })
186
+ .on('pong', () => {
187
+ if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
188
+ })
189
+ .on('message', async (message) => {
190
+ const parsedMessage = JSON.parse(message);
191
+ const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
192
+ if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
193
+ if (parsedMessage.message === 'Forbidden') return;
194
+
195
+ const messageData = parsedMessage?.[0]?.Data;
196
+ if (!messageData) return;
197
+
198
+ let updateDeviceState = false;
199
+ const unitId = messageData?.id;
200
+ switch (unitId) {
201
+ case this.deviceId:
202
+ const messageType = parsedMessage[0].messageType;
203
+ switch (messageType) {
204
+ case 'unitStateChanged':
205
+ const settings = Object.fromEntries(
206
+ messageData.settings.map(({ name, value }) => {
207
+ let parsedValue = this.functions.convertValue(value);
208
+ return [name, parsedValue];
209
+ })
210
+ );
211
+ Object.assign(deviceData.Device, settings);
212
+ updateDeviceState = true;
213
+ break;
214
+ case 'unitWifiSignalChanged':
215
+ deviceData.Rssi = messageData.rssi;
216
+ updateDeviceState = true;
217
+ break;
218
+ default:
219
+ if (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
220
+ return;
221
+ }
222
+ break;
223
+ default:
224
+ if (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
225
+ return;
226
+ }
227
+
228
+ //update state
229
+ if (updateDeviceState) await this.updateState(deviceData);
230
+ });
231
+ } catch (error) {
232
+ if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
233
+ this.cleanupSocket();
234
+ }
235
+ }
236
+ if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
237
+
238
+ //update state
239
+ await this.updateState(deviceData);
240
+
241
+ return true;
242
+ } catch (error) {
243
+ throw new Error(`Check state error: ${error.message}`);
244
+ };
245
+ };
246
+
236
247
  async send(accountType, displayType, deviceData, flag, flagData) {
237
248
  try {
238
249
  let method = null
@@ -374,7 +385,7 @@ class MelCloudAta extends EventEmitter {
374
385
  data: payload
375
386
  });
376
387
 
377
- this.updateData(deviceData, updateState);
388
+ await this.updateData(deviceData, updateState);
378
389
  return true;
379
390
  default:
380
391
  return;
@@ -385,9 +396,9 @@ class MelCloudAta extends EventEmitter {
385
396
  }
386
397
  }
387
398
 
388
- updateData(deviceData, updateState = true) {
399
+ async updateData(deviceData, updateState = true) {
389
400
  this.locks = true;
390
- if (updateState) this.emit('deviceState', deviceData);
401
+ if (updateState) await this.updateState(deviceData);
391
402
 
392
403
  setTimeout(() => {
393
404
  this.locks = false
@@ -145,11 +145,7 @@ class MelCloudHome extends EventEmitter {
145
145
 
146
146
  const settingsObject = Object.fromEntries(
147
147
  settingsArray.map(({ name, value }) => {
148
- let parsedValue = value;
149
- if (value === "True") parsedValue = true;
150
- else if (value === "False") parsedValue = false;
151
- else if (!isNaN(value) && value !== "") parsedValue = Number(value);
152
-
148
+ let parsedValue = this.functions.convertValue(value);
153
149
  const key = name.charAt(0).toUpperCase() + name.slice(1);
154
150
  return [key, parsedValue];
155
151
  })