homebridge-melcloud-control 4.3.11-beta.0 → 4.3.11-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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.3.11-beta.0",
4
+ "version": "4.3.11-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/constants.js CHANGED
@@ -58,9 +58,9 @@ export const TemperatureDisplayUnits = ["°C", "°F"];
58
58
 
59
59
  export const AirConditioner = {
60
60
  SystemMapEnumToString: { 0: "Air Conditioner Off", 1: "Air Conditioner On", 2: "Air Conditioner Offline" },
61
- OperationModeMapStringToEnum: { "0": 0, "Heat": 1, "Dry": 2, "Cool": 3, "4": 4, "5": 5, "6": 6, "Fan": 7, "Automatic": 8, "Isee Heat": 9, "Isee Dry": 10, "Isee Cool": 11 },
62
- OperationModeMapEnumToString: { 0: "0", 1: "Heat", 2: "Dry", 3: "Cool", 4: "4", 5: "5", 6: "6", 7: "Fan", 8: "Automatic", 9: "Isee Heat", 10: "Isee Dry", 11: "Isee Cool" },
63
- OperationModeMapEnumToEnumWs: { 0: 0, 1: 1, 2: 2, 3: 3, 4: 7, 5: 8, 6: 9, 7: 10, 8: 11 },
61
+ OperationModeMapStringToEnum: { "0": 0, "Heat": 1, "Dry": 2, "Cool": 3, "4": 4, "5": 5, "6": 6, "Fan": 7, "Automatic": 8, "Heat Isee": 9, "Dry Isee": 10, "Cool Isee": 11 },
62
+ OperationModeMapEnumToString: { 0: "0", 1: "Heat", 2: "Dry", 3: "Cool", 4: "4", 5: "5", 6: "6", 7: "Fan", 8: "Automatic", 9: "Heat Isee", 10: "Dry Isee", 11: "Cool Isee" },
63
+ OperationModeMapEnumToEnumWs: { 0: 0, 1: 1, 2: 2, 3: 3, 4: 7, 5: 8 },
64
64
  FanSpeedMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5, "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5 },
65
65
  FanSpeedMapEnumToString: { 0: "Auto", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five" },
66
66
  SetFanSpeedMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5, "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5 },
package/src/deviceata.js CHANGED
@@ -142,7 +142,7 @@ class DeviceAta extends EventEmitter {
142
142
  });
143
143
  }
144
144
  } catch (error) {
145
- this.emit('warn', `RESTFul integration start error: ${error}`);
145
+ if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
146
146
  };
147
147
  }
148
148
 
@@ -186,7 +186,7 @@ class DeviceAta extends EventEmitter {
186
186
  });
187
187
  }
188
188
  } catch (error) {
189
- this.emit('warn', `MQTT integration start error: ${error}`);
189
+ if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
190
190
  };
191
191
  }
192
192
 
@@ -317,6 +317,7 @@ class DeviceAta extends EventEmitter {
317
317
  const supportsOutdoorTemperature = this.accessory.supportsOutdoorTemperature;
318
318
  const numberOfFanSpeeds = this.accessory.numberOfFanSpeeds;
319
319
  const supportsSwingFunction = this.accessory.supportsSwingFunction;
320
+ const supportsVideWane = this.accessory.supportsVideWane;
320
321
  const autoDryFanMode = [this.accessory.operationMode, 8, supportsDry ? 2 : 8, 7][this.autoDryFanMode]; //NONE, AUTO - 8, DRY - 2, FAN - 7
321
322
  const heatDryFanMode = [this.accessory.operationMode, 1, supportsDry ? 2 : 1, 7][this.heatDryFanMode]; //NONE, HEAT - 1, DRY - 2, FAN - 7
322
323
  const coolDryFanMode = [this.accessory.operationMode, 3, supportsDry ? 2 : 3, 7][this.coolDryFanMode]; //NONE, COOL - 3, DRY - 2, FAN - 7
@@ -446,7 +447,7 @@ class DeviceAta extends EventEmitter {
446
447
  })
447
448
  .onSet(async (value) => {
448
449
  try {
449
- deviceData.Device.VaneHorizontalDirection = value ? 12 : 0;
450
+ if (supportsVideWane) deviceData.Device.VaneHorizontalDirection = value ? 12 : 0;
450
451
  deviceData.Device.VaneVerticalDirection = value ? 7 : 0;
451
452
  if (this.logInfo) this.emit('info', `Set air direction mode: ${AirConditioner.AirDirectionMapEnumToString[value]}`);
452
453
  await this.melCloudAta.send(this.accountType, this.displayType, deviceData, AirConditioner.EffectiveFlags.VaneVerticalVaneHorizontal);
@@ -1129,91 +1130,91 @@ class DeviceAta extends EventEmitter {
1129
1130
  case 7: //OPERATING MODE DRY CONTROL HIDE
1130
1131
  deviceData.HideDryModeControl = state;
1131
1132
  break;
1132
- case 10: //VANE H SWING MODE AUTO
1133
+ case 10: //VANE H MODE AUTO
1133
1134
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1134
1135
  deviceData.Device.Power = true;
1135
1136
  deviceData.Device.VaneHorizontalDirection = state ? 0 : button.previousValue;
1136
1137
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1137
1138
  break;
1138
- case 11: //VANE H SWING MODE 1
1139
+ case 11: //VANE H MODE 1
1139
1140
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1140
1141
  deviceData.Device.Power = true;
1141
1142
  deviceData.Device.VaneHorizontalDirection = state ? 1 : button.previousValue;
1142
1143
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1143
1144
  break;
1144
- case 12: //VANE H SWING MODE 2
1145
+ case 12: //VANE H MODE 2
1145
1146
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1146
1147
  deviceData.Device.Power = true;
1147
1148
  deviceData.Device.VaneHorizontalDirection = state ? 2 : button.previousValue;
1148
1149
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1149
1150
  break;
1150
- case 13: //VANE H SWING MODE 3
1151
+ case 13: //VANE H MODE 3
1151
1152
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1152
1153
  deviceData.Device.Power = true;
1153
1154
  deviceData.Device.VaneHorizontalDirection = state ? 3 : button.previousValue;
1154
1155
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1155
1156
  break;
1156
- case 14: //VANE H SWING MODE 4
1157
+ case 14: //VANE H MODE 4
1157
1158
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1158
1159
  deviceData.Device.Power = true;
1159
1160
  deviceData.Device.VaneHorizontalDirection = state ? 4 : button.previousValue;
1160
1161
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1161
1162
  break;
1162
- case 15: //VANE H SWING MODE 5
1163
+ case 15: //VANE H MODE 5
1163
1164
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1164
1165
  deviceData.Device.Power = true;
1165
1166
  deviceData.Device.VaneHorizontalDirection = state ? 5 : button.previousValue;
1166
1167
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1167
1168
  break;
1168
- case 16: //VANE H SWING MODE SPLIT
1169
+ case 16: //VANE H MODE SPLIT
1169
1170
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1170
1171
  deviceData.Device.Power = true;
1171
1172
  deviceData.Device.VaneHorizontalDirection = state ? 8 : button.previousValue;
1172
1173
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1173
1174
  break;
1174
- case 17: //VANE H SWING MODE SWING
1175
+ case 17: //VANE H MODE SWING
1175
1176
  button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
1176
1177
  deviceData.Device.Power = true;
1177
1178
  deviceData.Device.VaneHorizontalDirection = state ? 12 : button.previousValue;
1178
1179
  flag = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
1179
1180
  break;
1180
- case 20: //VANE V SWING MODE AUTO
1181
+ case 20: //VANE V MODE AUTO
1181
1182
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1182
1183
  deviceData.Device.Power = true;
1183
1184
  deviceData.Device.VaneVerticalDirection = state ? 0 : button.previousValue;
1184
1185
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1185
1186
  break;
1186
- case 21: //VANE V SWING MODE 1
1187
+ case 21: //VANE V MODE 1
1187
1188
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1188
1189
  deviceData.Device.Power = true;
1189
1190
  deviceData.Device.VaneVerticalDirection = state ? 1 : button.previousValue;
1190
1191
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1191
1192
  break;
1192
- case 22: //VANE V SWING MODE 2
1193
+ case 22: //VANE V MODE 2
1193
1194
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1194
1195
  deviceData.Device.Power = true;
1195
1196
  deviceData.Device.VaneVerticalDirection = state ? 2 : button.previousValue;
1196
1197
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1197
1198
  break;
1198
- case 23: //VANE V SWING MODE 3
1199
+ case 23: //VANE V MODE 3
1199
1200
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1200
1201
  deviceData.Device.Power = true;
1201
1202
  deviceData.Device.VaneVerticalDirection = state ? 3 : button.previousValue;
1202
1203
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1203
1204
  break;
1204
- case 24: //VANE V SWING MODE 4
1205
+ case 24: //VANE V MODE 4
1205
1206
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1206
1207
  deviceData.Device.Power = true;
1207
1208
  deviceData.Device.VaneVerticalDirection = state ? 4 : button.previousValue;
1208
1209
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1209
1210
  break;
1210
- case 25: //VANE V SWING MODE 5
1211
+ case 25: //VANE V MODE 5
1211
1212
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1212
1213
  deviceData.Device.Power = true;
1213
1214
  deviceData.Device.VaneVerticalDirection = state ? 5 : button.previousValue;
1214
1215
  flag = AirConditioner.EffectiveFlags.PowerVaneVertical;
1215
1216
  break;
1216
- case 26: //VANE V SWING MODE SWING
1217
+ case 26: //VANE V MODE SWING
1217
1218
  button.previousValue = state ? deviceData.Device.VaneVerticalDirection : button.previousValue ?? deviceData.Device.VaneVerticalDirection;
1218
1219
  deviceData.Device.Power = true;
1219
1220
  deviceData.Device.VaneVerticalDirection = state ? 7 : button.previousValue;
@@ -1425,6 +1426,7 @@ class DeviceAta extends EventEmitter {
1425
1426
  const outdoorTemperature = deviceData.Device.OutdoorTemperature;
1426
1427
  const isConnected = accountTypeMelcloud ? !deviceData.Device[connectKey] : deviceData.Device[connectKey];
1427
1428
  const isInError = deviceData.Device[errorKey];
1429
+ const currentSwingMode = supportsSwingFunction ? (supportsWideVane ? vaneHorizontalDirection === 12 && vaneVerticalDirection === 7 ? 1 : 0 : vaneVerticalDirection === 7 ? 1 : 0) : 0;
1428
1430
 
1429
1431
  //accessory
1430
1432
  const obj = {
@@ -1461,7 +1463,7 @@ class DeviceAta extends EventEmitter {
1461
1463
  automaticFanSpeed: automaticFanSpeed,
1462
1464
  vaneVerticalSwing: vaneVerticalSwing,
1463
1465
  vaneHorizontalSwing: vaneHorizontalSwing,
1464
- currentSwingMode: supportsSwingFunction && vaneHorizontalDirection === 12 && vaneVerticalDirection === 7 ? 1 : 0,
1466
+ currentSwingMode: currentSwingMode,
1465
1467
  lockPhysicalControl: prohibitSetTemperature && prohibitOperationMode && prohibitPower ? 1 : 0,
1466
1468
  temperatureStep: temperatureStep,
1467
1469
  useFahrenheit: this.accountInfo.useFahrenheit ? 1 : 0,
@@ -1753,49 +1755,49 @@ class DeviceAta extends EventEmitter {
1753
1755
  case 7: //OPERATING MODE DRY CONTROL HIDE
1754
1756
  button.state = power ? (hideDryModeControl === true) : false;
1755
1757
  break;
1756
- case 10: //VANE H SWING MODE AUTO
1758
+ case 10: //VANE H MODE AUTO
1757
1759
  button.state = power ? (vaneHorizontalDirection === 0) : false;
1758
1760
  break;
1759
- case 11: //VANE H SWING MODE 1
1761
+ case 11: //VANE H MODE 1
1760
1762
  button.state = power ? (vaneHorizontalDirection === 1) : false;
1761
1763
  break;
1762
- case 12: //VANE H SWING MODE 2
1764
+ case 12: //VANE H MODE 2
1763
1765
  button.state = power ? (vaneHorizontalDirection === 2) : false;
1764
1766
  break;
1765
- case 13: //VANE H SWING MODE 3
1767
+ case 13: //VANE H MODE 3
1766
1768
  button.state = power ? (vaneHorizontalDirection === 3) : false;
1767
1769
  break;
1768
- case 14: //VANE H SWING MODE 4
1770
+ case 14: //VANE H MODE 4
1769
1771
  button.state = power ? (vaneHorizontalDirection === 4) : false;
1770
1772
  break;
1771
- case 15: //VANE H SWING MODE 5
1773
+ case 15: //VANE H MODE 5
1772
1774
  button.state = power ? (vaneHorizontalDirection === 5) : false;
1773
1775
  break;
1774
- case 16: //VANE H SWING MODE SPLIT
1776
+ case 16: //VANE H MODE SPLIT
1775
1777
  button.state = power ? (vaneHorizontalDirection === 8) : false;
1776
1778
  break;
1777
- case 17: //VANE H SWING MODE SWING
1779
+ case 17: //VANE H MODE SWING
1778
1780
  button.state = power ? (vaneHorizontalDirection === 12) : false;
1779
1781
  break;
1780
- case 20: //VANE V SWING MODE AUTO
1782
+ case 20: //VANE V MODE AUTO
1781
1783
  button.state = power ? (vaneVerticalDirection === 0) : false;
1782
1784
  break;
1783
- case 21: //VANE V SWING MODE 1
1785
+ case 21: //VANE V MODE 1
1784
1786
  button.state = power ? (vaneVerticalDirection === 1) : false;
1785
1787
  break;
1786
- case 22: //VANE V SWING MODE 2
1788
+ case 22: //VANE V MODE 2
1787
1789
  button.state = power ? (vaneVerticalDirection === 2) : false;
1788
1790
  break;
1789
- case 23: //VANE V SWING MODE 3
1791
+ case 23: //VANE V MODE 3
1790
1792
  button.state = power ? (vaneVerticalDirection === 3) : false;
1791
1793
  break;
1792
- case 24: //VANE V SWING MODE 4
1794
+ case 24: //VANE V MODE 4
1793
1795
  button.state = power ? (vaneVerticalDirection === 4) : false;
1794
1796
  break;
1795
- case 25: //VANE V SWING MODE 5
1797
+ case 25: //VANE V MODE 5
1796
1798
  button.state = power ? (vaneVerticalDirection === 5) : false;
1797
1799
  break;
1798
- case 26: //VANE V SWING MODE SWING
1800
+ case 26: //VANE V MODE SWING
1799
1801
  button.state = power ? (vaneVerticalDirection === 7) : false;
1800
1802
  break;
1801
1803
  case 27: //VANE H/V CONTROLS HIDE
@@ -1859,7 +1861,7 @@ class DeviceAta extends EventEmitter {
1859
1861
  if (supportsFanSpeed) this.emit('info', `Current fan speed: ${AirConditioner.AktualFanSpeedMapEnumToString[actualFanSpeed]}`);
1860
1862
  if (vaneHorizontalDirection !== null) this.emit('info', `Vane horizontal: ${AirConditioner.VaneHorizontalDirectionMapEnumToString[vaneHorizontalDirection]}`);
1861
1863
  if (vaneVerticalDirection !== null) this.emit('info', `Vane vertical: ${AirConditioner.VaneVerticalDirectionMapEnumToString[vaneVerticalDirection]}`);
1862
- if (supportsSwingFunction) this.emit('info', `Air direction: ${AirConditioner.AirDirectionMapEnumToString[obj.currentSwingMode]}`);
1864
+ if (supportsSwingFunction) this.emit('info', `Air direction: ${AirConditioner.AirDirectionMapEnumToString[currentSwingMode]}`);
1863
1865
  this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
1864
1866
  this.emit('info', `Lock physical controls: ${obj.lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
1865
1867
  if (this.accountType === 'melcloudhome') this.emit('info', `Signal strength: ${deviceData.Rssi}dBm`);
package/src/deviceatw.js CHANGED
@@ -146,7 +146,7 @@ class DeviceAtw extends EventEmitter {
146
146
  });
147
147
  }
148
148
  } catch (error) {
149
- this.emit('warn', `RESTFul integration start error: ${error}`);
149
+ if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
150
150
  };
151
151
  }
152
152
 
@@ -190,7 +190,7 @@ class DeviceAtw extends EventEmitter {
190
190
  });
191
191
  }
192
192
  } catch (error) {
193
- this.emit('warn', `MQTT integration start error: ${error}`);
193
+ if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
194
194
  };
195
195
  }
196
196
  }
package/src/deviceerv.js CHANGED
@@ -138,7 +138,7 @@ class DeviceErv extends EventEmitter {
138
138
  });
139
139
  }
140
140
  } catch (error) {
141
- this.emit('warn', `RESTFul integration start error: ${error}`);
141
+ if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
142
142
  };
143
143
  }
144
144
 
@@ -182,7 +182,7 @@ class DeviceErv extends EventEmitter {
182
182
  });
183
183
  }
184
184
  } catch (error) {
185
- this.emit('warn', `MQTT integration start error: ${error}`);
185
+ if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
186
186
  };
187
187
  }
188
188
  }
package/src/functions.js CHANGED
@@ -58,57 +58,39 @@ class Functions extends EventEmitter {
58
58
  let chromiumPath = '/usr/bin/chromium-browser';
59
59
 
60
60
  try {
61
- // --- Detect OS ---
62
61
  const { stdout: osOut } = await execPromise('uname -s');
63
62
  const osName = osOut.trim();
64
63
  if (this.logDebug) this.emit('debug', `Detected OS: ${osName}`);
65
64
 
66
- // --- Detect Architecture ---
67
65
  const { stdout: archOut } = await execPromise('uname -m');
68
66
  const arch = archOut.trim();
69
67
  if (this.logDebug) this.emit('debug', `Detected architecture: ${arch}`);
70
68
 
71
- // --- Detect Docker ---
69
+ // Docker detection
72
70
  let isDocker = false;
73
- try {
74
- await access('/.dockerenv', fs.constants.F_OK); isDocker = true;
75
- } catch { }
76
-
71
+ try { await access('/.dockerenv', fs.constants.F_OK); isDocker = true; } catch { }
77
72
  try {
78
73
  const { stdout } = await execPromise('cat /proc/1/cgroup || true');
79
74
  if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
80
75
  } catch { }
81
-
82
76
  if (isDocker && this.logDebug) this.emit('debug', 'Running inside Docker container.');
83
77
 
84
- // === macOS ===
78
+ // macOS
85
79
  if (osName === 'Darwin') {
86
80
  chromiumPath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
87
- try {
88
- await access(chromiumPath, fs.constants.X_OK);
89
- return chromiumPath;
90
- } catch {
91
- return null;
92
- }
81
+ try { await access(chromiumPath, fs.constants.X_OK); return chromiumPath; } catch { return null; }
93
82
  }
94
83
 
95
- // === ARM ===
96
- if (arch.startsWith('arm')) {
97
- try {
98
- chromiumPath = '/usr/bin/chromium-browser';
99
- await access(chromiumPath, fs.constants.X_OK);
100
- return chromiumPath;
101
- } catch {
102
- try {
103
- await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg');
104
- return chromiumPath;
105
- } catch {
106
- return null;
107
- }
84
+ // ARM
85
+ if (arch.startsWith('arm') || arch.startsWith('aarch')) {
86
+ try { await access(chromiumPath, fs.constants.X_OK); return chromiumPath; }
87
+ catch {
88
+ try { await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg'); return chromiumPath; }
89
+ catch { return null; }
108
90
  }
109
91
  }
110
92
 
111
- // === Linux x64 ===
93
+ // Linux x64
112
94
  if (osName === 'Linux') {
113
95
  let systemChromium = null;
114
96
  try {
@@ -116,13 +98,9 @@ class Functions extends EventEmitter {
116
98
  systemChromium = checkOut.trim() || null;
117
99
  } catch { }
118
100
 
119
- // --- Detect Entware (QNAP) ---
101
+ // Entware (QNAP)
120
102
  let entwareExists = false;
121
- try {
122
- await access('/opt/bin/opkg', fs.constants.X_OK);
123
- entwareExists = true;
124
- } catch { }
125
-
103
+ try { await access('/opt/bin/opkg', fs.constants.X_OK); entwareExists = true; } catch { }
126
104
  if (entwareExists) {
127
105
  try {
128
106
  await execPromise('/opt/bin/opkg update');
@@ -131,25 +109,21 @@ class Functions extends EventEmitter {
131
109
  } catch { }
132
110
  }
133
111
 
134
- // --- Generic Linux installs missing libs for Puppeteer ---
135
- const depInstall = [
112
+ // Install missing libs
113
+ const depCommands = [
136
114
  'apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2',
137
115
  'apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib',
138
116
  'yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib'
139
117
  ];
140
- for (const cmd of depInstall) {
141
- try {
142
- await execPromise(`sudo ${cmd}`);
143
- } catch { }
144
- }
118
+ for (const cmd of depCommands) { try { await execPromise(`sudo ${cmd}`); } catch { } }
145
119
 
146
- // Set LD_LIBRARY_PATH so Puppeteer's Chromium can find libs
147
120
  process.env.LD_LIBRARY_PATH = `/usr/lib:/usr/lib64:${process.env.LD_LIBRARY_PATH || ''}`;
148
121
  return systemChromium;
149
122
  }
150
123
 
151
- if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}.`);
124
+ if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}`);
152
125
  return null;
126
+
153
127
  } catch (error) {
154
128
  if (this.logError) this.emit('error', `Chromium detection/install error: ${error.message}`);
155
129
  return null;
package/src/melcloud.js CHANGED
@@ -109,7 +109,6 @@ class MelCloud extends EventEmitter {
109
109
  devicesList.State = true;
110
110
  devicesList.Info = `Found ${devicesCount} devices`;
111
111
  devicesList.Devices = devices;
112
- devicesList.Headers = this.headers;
113
112
  this.emit('devicesList', devicesList);
114
113
 
115
114
  return devicesList;
@@ -163,6 +162,8 @@ class MelCloud extends EventEmitter {
163
162
  'X-MitsContextKey': contextKey,
164
163
  'Content-Type': 'application/json'
165
164
  };
165
+ this.emit('headers', headers);
166
+
166
167
  this.headers = headers;
167
168
  this.axiosInstance = axios.create({
168
169
  baseURL: ApiUrls.BaseURL,
@@ -24,20 +24,21 @@ class MelCloudAta extends EventEmitter {
24
24
 
25
25
  //set default values
26
26
  this.deviceData = {};
27
- this.headers = {};
27
+ this.headers = melcloud.headers;
28
28
 
29
29
  //handle melcloud events
30
30
  let deviceData = null;
31
- melcloud.on('devicesList', async (devicesData) => {
31
+ melcloud.on('headers', (headers) => {
32
+ this.headers = headers;
33
+ }).on('devicesList', async (devicesData) => {
32
34
  try {
33
- this.headers = devicesData.Headers;
34
35
  deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
35
36
  if (!deviceData) return;
36
37
  deviceData.Scenes = devicesData.Scenes ?? [];
37
38
 
38
39
  //update state
39
40
  if (this.logDebug) this.emit('debug', `Request update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
40
- await this.updateState(deviceData);
41
+ await this.updateState('request', deviceData);
41
42
  } catch (error) {
42
43
  if (this.logError) this.emit('error', `Request process message error: ${error}`);
43
44
  }
@@ -95,14 +96,14 @@ class MelCloudAta extends EventEmitter {
95
96
  }
96
97
 
97
98
  //update state
98
- if (updateState) await this.updateState(deviceData, 'ws');
99
+ if (updateState) await this.updateState('ws', deviceData);
99
100
  } catch (error) {
100
101
  if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
101
102
  }
102
103
  });
103
104
  }
104
105
 
105
- async updateState(deviceData, type) {
106
+ async updateState(type, deviceData) {
106
107
  try {
107
108
  if (this.accountType === 'melcloudhome') {
108
109
 
@@ -147,9 +148,6 @@ class MelCloudAta extends EventEmitter {
147
148
  return acc;
148
149
  }, { indoor: {}, outdoor: {} });
149
150
 
150
- //display info if units are not configured in MELCloud service
151
- if (unitsCount === 0 && this.logDebug) if (this.logDebug) this.emit('debug', `Units are not configured in MELCloud service`);
152
-
153
151
  //filter info
154
152
  const { Device: _ignored, ...info } = deviceData;
155
153
 
@@ -184,10 +182,9 @@ class MelCloudAta extends EventEmitter {
184
182
 
185
183
  async checkState(devicesData) {
186
184
  try {
187
- this.headers = devicesData.Headers;
188
185
  const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
189
186
  deviceData.Scenes = devicesData.Scenes ?? [];
190
- await this.updateState(deviceData);
187
+ await this.updateState('request', deviceData);
191
188
 
192
189
  return true;
193
190
  } catch (error) {
@@ -237,7 +234,7 @@ class MelCloudAta extends EventEmitter {
237
234
  break;
238
235
  }
239
236
 
240
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
237
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
241
238
 
242
239
  await axios(path, {
243
240
  method: 'POST',
@@ -327,7 +324,7 @@ class MelCloudAta extends EventEmitter {
327
324
  //sens payload
328
325
  headers['Content-Type'] = 'application/json; charset=utf-8';
329
326
  headers.Origin = ApiUrlsHome.Origin;
330
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
327
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
331
328
 
332
329
  await axios(path, {
333
330
  method: method,
@@ -24,20 +24,21 @@ class MelCloudAtw extends EventEmitter {
24
24
 
25
25
  //set default values
26
26
  this.deviceData = {};
27
- this.headers = {};
27
+ this.headers = melcloud.headers;
28
28
 
29
29
  //handle melcloud events
30
30
  let deviceData = null;
31
- melcloud.on('devicesList', async (devicesData) => {
31
+ melcloud.on('headers', (headers) => {
32
+ this.headers = headers;
33
+ }).on('devicesList', async (devicesData) => {
32
34
  try {
33
- this.headers = devicesData.Headers;
34
35
  deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
35
36
  if (!deviceData) return;
36
37
  deviceData.Scenes = devicesData.Scenes ?? [];
37
38
 
38
39
  //update state
39
40
  if (this.logDebug) this.emit('debug', `Request update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
40
- await this.updateState(deviceData);
41
+ await this.updateState('request', deviceData);
41
42
  } catch (error) {
42
43
  if (this.logError) this.emit('error', `Request process message error: ${error}`);
43
44
  }
@@ -95,14 +96,14 @@ class MelCloudAtw extends EventEmitter {
95
96
  }
96
97
 
97
98
  //update state
98
- if (updateState) await this.updateState(deviceData, 'ws');
99
+ if (updateState) await this.updateState('ws', deviceData);
99
100
  } catch (error) {
100
101
  if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
101
102
  }
102
103
  });
103
104
  }
104
105
 
105
- async updateState(deviceData, type) {
106
+ async updateState(type, deviceData) {
106
107
  try {
107
108
  if (this.accountType === 'melcloudhome') {
108
109
  deviceData.Device.OperationMode = HeatPump.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
@@ -134,9 +135,6 @@ class MelCloudAtw extends EventEmitter {
134
135
  return acc;
135
136
  }, { indoor: {}, outdoor: {} });
136
137
 
137
- //display info if units are not configured in MELCloud service
138
- if (unitsCount === 0 && this.logDebug) if (this.logDebug) this.emit('debug', `Units are not configured in MELCloud service`);
139
-
140
138
  //filter info
141
139
  const { Device: _ignored, ...info } = deviceData;
142
140
 
@@ -171,10 +169,9 @@ class MelCloudAtw extends EventEmitter {
171
169
 
172
170
  async checkState(devicesData) {
173
171
  try {
174
- this.headers = devicesData.Headers;
175
172
  const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
176
173
  deviceData.Scenes = devicesData.Scenes ?? [];
177
- await this.updateState(deviceData);
174
+ await this.updateState('request', deviceData);
178
175
 
179
176
  return true;
180
177
  } catch (error) {
@@ -239,7 +236,7 @@ class MelCloudAtw extends EventEmitter {
239
236
  break;
240
237
  }
241
238
 
242
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
239
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
243
240
 
244
241
  await axios(path, {
245
242
  method: 'POST',
@@ -299,7 +296,7 @@ class MelCloudAtw extends EventEmitter {
299
296
 
300
297
  headers['Content-Type'] = 'application/json; charset=utf-8';
301
298
  headers.Origin = ApiUrlsHome.Origin;
302
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
299
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
303
300
 
304
301
  await axios(path, {
305
302
  method: method,
@@ -24,20 +24,21 @@ class MelCloudErv extends EventEmitter {
24
24
 
25
25
  //set default values
26
26
  this.deviceData = {};
27
- this.headers = {};
27
+ this.headers = melcloud.headers;
28
28
 
29
29
  //handle melcloud events
30
30
  let deviceData = null;
31
- melcloud.on('devicesList', async (devicesData) => {
31
+ melcloud.on('headers', (headers) => {
32
+ this.headers = headers;
33
+ }).on('devicesList', async (devicesData) => {
32
34
  try {
33
- this.headers = devicesData.Headers;
34
35
  deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
35
36
  if (!deviceData) return;
36
37
  deviceData.Scenes = devicesData.Scenes ?? [];
37
38
 
38
39
  //update state
39
40
  if (this.logDebug) this.emit('debug', `Request update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
40
- await this.updateState(deviceData);
41
+ await this.updateState('request', deviceData);
41
42
  } catch (error) {
42
43
  if (this.logError) this.emit('error', `Request process message error: ${error}`);
43
44
  }
@@ -95,14 +96,14 @@ class MelCloudErv extends EventEmitter {
95
96
  }
96
97
 
97
98
  //update state
98
- if (updateState) await this.updateState(deviceData, 'ws');
99
+ if (updateState) await this.updateState('ws', deviceData);
99
100
  } catch (error) {
100
101
  if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
101
102
  }
102
103
  });
103
104
  }
104
105
 
105
- async updateState(deviceData, type) {
106
+ async updateState(type, deviceData) {
106
107
  try {
107
108
  if (this.accountType === 'melcloudhome') {
108
109
  //read default temps
@@ -133,9 +134,6 @@ class MelCloudErv extends EventEmitter {
133
134
  return acc;
134
135
  }, { indoor: {}, outdoor: {} });
135
136
 
136
- //display info if units are not configured in MELCloud service
137
- if (unitsCount === 0 && this.logDebug) if (this.logDebug) this.emit('debug', `Units are not configured in MELCloud service`);
138
-
139
137
  //filter info
140
138
  const { Device: _ignored, ...info } = deviceData;
141
139
 
@@ -170,10 +168,9 @@ class MelCloudErv extends EventEmitter {
170
168
 
171
169
  async checkState(devicesData) {
172
170
  try {
173
- this.headers = devicesData.Headers;
174
171
  const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
175
172
  deviceData.Scenes = devicesData.Scenes ?? [];
176
- await this.updateState(deviceData);
173
+ await this.updateState('request', deviceData);
177
174
 
178
175
  return true;
179
176
  } catch (error) {
@@ -239,7 +236,7 @@ class MelCloudErv extends EventEmitter {
239
236
  break;
240
237
  }
241
238
 
242
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
239
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
243
240
 
244
241
  await axios(path, {
245
242
  method: 'POST',
@@ -303,7 +300,7 @@ class MelCloudErv extends EventEmitter {
303
300
 
304
301
  headers['Content-Type'] = 'application/json; charset=utf-8';
305
302
  headers.Origin = ApiUrlsHome.Origin;
306
- if (this.logDebug) this.emit('debug', `Send Data: ${JSON.stringify(payload, null, 2)}`);
303
+ if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
307
304
 
308
305
  await axios(path, {
309
306
  method: method,
@@ -202,8 +202,11 @@ class MelCloudHome extends EventEmitter {
202
202
  // Get scenes
203
203
  let scenes = [];
204
204
  try {
205
- scenes = await this.checkScenesList();
206
- if (this.logDebug) this.emit('debug', `Found ${scenes.length} scenes`);
205
+ const scenesList = await this.checkScenesList();
206
+ if (this.logDebug) this.emit('debug', `Found ${scenesList.length} scenes`);
207
+ if (scenesList.length > 0) {
208
+ scenes = scenesList;
209
+ }
207
210
  } catch (error) {
208
211
  if (this.logError) this.emit('error', `Get scenes error: ${error}`);
209
212
  }
@@ -256,7 +259,6 @@ class MelCloudHome extends EventEmitter {
256
259
  devicesList.Info = `Found ${devicesCount} devices ${scenes.length > 0 ? `and ${scenes.length} scenes` : ''}`;
257
260
  devicesList.Devices = devices;
258
261
  devicesList.Scenes = scenes;
259
- devicesList.Headers = this.headers;
260
262
  this.emit('devicesList', devicesList);
261
263
 
262
264
  return devicesList;
@@ -272,19 +274,23 @@ class MelCloudHome extends EventEmitter {
272
274
  let browser;
273
275
  try {
274
276
  const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
277
+
278
+ // Get Chromium path
275
279
  let chromiumPath = await this.functions.ensureChromiumInstalled();
276
280
 
277
281
  // === Fallback to Puppeteer's built-in Chromium ===
278
282
  if (!chromiumPath) {
279
- try {
280
- const puppeteerPath = puppeteer.executablePath();
281
- if (puppeteerPath && fs.existsSync(puppeteerPath)) {
282
- chromiumPath = puppeteerPath;
283
- if (this.logDebug) this.emit('debug', `Using puppeteer Chromium at ${chromiumPath}`);
284
- }
285
- } catch { }
286
- } else {
287
- if (this.logDebug) this.emit('debug', `Using system Chromium at ${chromiumPath}`);
283
+ if (!arch.startsWith('arm')) {
284
+ try {
285
+ const puppeteerPath = puppeteer.executablePath();
286
+ if (puppeteerPath && fs.existsSync(puppeteerPath)) {
287
+ chromiumPath = puppeteerPath;
288
+ if (this.logDebug) this.emit('debug', `Using Puppeteer Chromium at ${chromiumPath}`);
289
+ }
290
+ } catch { }
291
+ } else {
292
+ if (this.logDebug) this.emit('debug', 'Skipping Puppeteer Chromium on ARM (incompatible)');
293
+ }
288
294
  }
289
295
 
290
296
  if (!chromiumPath) {
@@ -301,6 +307,7 @@ class MelCloudHome extends EventEmitter {
301
307
  return accountInfo;
302
308
  }
303
309
 
310
+ // Launch Chromium
304
311
  if (this.logDebug) this.emit('debug', `Launching Chromium...`);
305
312
  browser = await puppeteer.launch({
306
313
  headless: true,
@@ -412,6 +419,8 @@ class MelCloudHome extends EventEmitter {
412
419
  'User-Agent': userAgent,
413
420
  'x-csrf': '1'
414
421
  };
422
+ this.emit('headers', headers);
423
+
415
424
  this.headers = headers;
416
425
  this.axiosInstance = axios.create({
417
426
  baseURL: ApiUrlsHome.BaseURL,