homebridge-melcloud-control 4.4.1-beta.8 → 4.5.0

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/src/deviceata.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, AirConditioner } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAta extends EventEmitter {
10
- constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
10
+ constructor(api, account, device, presets, schedules, scenes, buttons, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -44,10 +44,10 @@ class DeviceAta extends EventEmitter {
44
44
  this.frostProtectionSupport = device.frostProtectionSupport || false;
45
45
  this.overheatProtectionSupport = device.overheatProtectionSupport || false;
46
46
  this.holidayModeSupport = device.holidayModeSupport || false;
47
- this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
48
- this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
49
- this.scenes = this.accountType === 'melcloudhome' ? (device.scenes || []).filter(scene => (scene.displayType ?? 0) > 0 && scene.id !== '0') : [];
50
- this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
47
+ this.presets = presets;
48
+ this.schedules = schedules;
49
+ this.scenes = scenes;
50
+ this.buttons = buttons;
51
51
 
52
52
  //files
53
53
  this.defaultTempsFile = defaultTempsFile;
@@ -693,7 +693,7 @@ class DeviceAta extends EventEmitter {
693
693
  }
694
694
 
695
695
  //frost protection
696
- if (this.frostProtectionSupport && this.accessory.frostProtection.Enabled !== null) {
696
+ if (supportsHeat && this.frostProtectionSupport && this.accessory.frostProtection.Enabled !== null) {
697
697
  //control
698
698
  if (this.logDebug) this.emit('debug', `Prepare frost protection control service`);
699
699
  const frostProtectionControlService = new Service.HeaterCooler(`${serviceName} Frost Protection`, `frostProtectionControlService${deviceId}`);
@@ -1610,6 +1610,7 @@ class DeviceAta extends EventEmitter {
1610
1610
 
1611
1611
  //characteristics array
1612
1612
  const characteristics = [];
1613
+ const operationModevalidValues = [];
1613
1614
 
1614
1615
  //operating mode 0, HEAT, DRY, COOL, 4, 5, 6, FAN, AUTO, ISEE HEAT, ISEE DRY, ISEE COOL
1615
1616
  switch (this.displayType) {
@@ -1655,11 +1656,15 @@ class DeviceAta extends EventEmitter {
1655
1656
  default:
1656
1657
  if (this.logWarn) this.emit('warn', `Unknown operating mode: ${operationMode}`);
1657
1658
  }
1658
-
1659
1659
  obj.currentOperationMode = !power ? 0 : (inStandbyMode ? 1 : obj.currentOperationMode);
1660
- obj.operationModeSetPropsMinValue = supportsAuto && supportsHeat ? 0 : !supportsAuto && supportsHeat ? 1 : supportsAuto && !supportsHeat ? 0 : 2;
1661
- obj.operationModeSetPropsMaxValue = 2
1662
- obj.operationModeSetPropsValidValues = supportsAuto && supportsHeat ? [0, 1, 2] : !supportsAuto && supportsHeat ? [1, 2] : supportsAuto && !supportsHeat ? [0, 2] : [2];
1660
+
1661
+ if (supportsAuto) operationModevalidValues.push(0);
1662
+ if (supportsHeat) operationModevalidValues.push(1);
1663
+ if (supportsCool) operationModevalidValues.push(2);
1664
+
1665
+ obj.operationModeSetPropsMinValue = operationModevalidValues[0];
1666
+ obj.operationModeSetPropsMaxValue = operationModevalidValues.at(-1);
1667
+ obj.operationModeSetPropsValidValues = operationModevalidValues;
1663
1668
 
1664
1669
  //fan speed mode
1665
1670
  if (supportsFanSpeed) {
@@ -1736,9 +1741,15 @@ class DeviceAta extends EventEmitter {
1736
1741
 
1737
1742
  obj.currentOperationMode = !power ? 0 : obj.currentOperationMode;
1738
1743
  obj.targetOperationMode = !power ? 0 : obj.targetOperationMode;
1739
- obj.operationModeSetPropsMinValue = 0
1740
- obj.operationModeSetPropsMaxValue = supportsAuto && supportsHeat ? 3 : !supportsAuto && supportsHeat ? 2 : supportsAuto && !supportsHeat ? 3 : 2;
1741
- obj.operationModeSetPropsValidValues = supportsAuto && supportsHeat ? [0, 1, 2, 3] : !supportsAuto && supportsHeat ? [0, 1, 2] : supportsAuto && !supportsHeat ? [0, 2, 3] : [0, 2];
1744
+
1745
+ operationModevalidValues.push(0);
1746
+ if (supportsHeat) operationModevalidValues.push(1);
1747
+ if (supportsCool) operationModevalidValues.push(2);
1748
+ if (supportsAuto) operationModevalidValues.push(3);
1749
+
1750
+ obj.operationModeSetPropsMinValue = operationModevalidValues[0];
1751
+ obj.operationModeSetPropsMaxValue = operationModevalidValues.at(-1);
1752
+ obj.operationModeSetPropsValidValues = operationModevalidValues;
1742
1753
 
1743
1754
  //create characteristics
1744
1755
  characteristics.push(
package/src/deviceatw.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, HeatPump } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAtw extends EventEmitter {
10
- constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
10
+ constructor(api, account, device, presets, schedules, scenes, buttons, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -48,10 +48,10 @@ class DeviceAtw extends EventEmitter {
48
48
  this.errorSensor = device.errorSensor || false;
49
49
  this.frostProtectionSupport = device.frostProtectionSupport || false;
50
50
  this.holidayModeSupport = device.holidayModeSupport || false;
51
- this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
52
- this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
53
- this.scenes = this.accountType === 'melcloudhome' ? (device.scenes || []).filter(scene => (scene.displayType ?? 0) > 0 && scene.id !== '0') : [];
54
- this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
51
+ this.presets = presets;
52
+ this.schedules = schedules;
53
+ this.scenes = scenes;
54
+ this.buttons = buttons;
55
55
 
56
56
  //files
57
57
  this.defaultTempsFile = defaultTempsFile;
package/src/deviceerv.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, Ventilation } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceErv extends EventEmitter {
10
- constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
10
+ constructor(api, account, device, presets, schedules, scenes, buttons, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -40,10 +40,10 @@ class DeviceErv extends EventEmitter {
40
40
  this.connectSensor = device.connectSensor || false;
41
41
  this.errorSensor = device.errorSensor || false;
42
42
  this.holidayModeSupport = device.holidayModeSupport || false;
43
- this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
44
- this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
45
- this.scenes = this.accountType === 'melcloudhome' ? (device.scenes || []).filter(scene => (scene.displayType ?? 0) > 0 && scene.id !== '0') : [];
46
- this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
43
+ this.presets = presets;
44
+ this.schedules = schedules;
45
+ this.scenes = scenes;
46
+ this.buttons = buttons;
47
47
 
48
48
  //files
49
49
  this.defaultTempsFile = defaultTempsFile;
@@ -1257,43 +1257,44 @@ class DeviceErv extends EventEmitter {
1257
1257
 
1258
1258
  //characteristics array
1259
1259
  const characteristics = [];
1260
+ const operationModevalidValues = [];
1260
1261
 
1261
1262
  //ventilation mode - 0, HEAT, 2, COOL, 4, 5, 6, FAN, AUTO
1262
1263
  switch (this.displayType) {
1263
1264
  case 1: //Heater Cooler
1264
1265
  switch (ventilationMode) {
1265
- case 0: //LOSSNAY
1266
- obj.currentOperationMode = 2; //INACTIVE, IDLE, HEATING, COOLIN
1267
- obj.targetOperationMode = 1; //AUTO, HEAT, COOL
1266
+ case 0: // LOSSNAY
1267
+ obj.currentOperationMode = 2; // heating
1268
+ obj.targetOperationMode = 1; // heat
1268
1269
  break;
1269
- case 1: //BYPASS
1270
- obj.currentOperationMode = 3;
1271
- obj.targetOperationMode = 2;
1270
+ case 1: // BYPASS
1271
+ obj.currentOperationMode = 3; // cooling
1272
+ obj.targetOperationMode = 2; // cool
1272
1273
  break;
1273
- case 2: //AUTO
1274
- switch (actualVentilationMode) {
1275
- case 0: //LOSSNAY
1276
- obj.currentOperationMode = 2;
1277
- break;
1278
- case 1: //BYPASS
1279
- obj.currentOperationMode = 3;
1280
- break;
1281
- default:
1282
- if (this.logWarn) this.emit('warn', `Unknown actual ventilation mode: ${actualVentilationMode}`);
1283
- break;
1284
- };
1285
- obj.targetOperationMode = 0;
1274
+ case 2: // AUTO
1275
+ if (actualVentilationMode === 0) {
1276
+ obj.currentOperationMode = 2; // heating
1277
+ } else if (actualVentilationMode === 1) {
1278
+ obj.currentOperationMode = 3; // cooling
1279
+ } else if (this.logWarn) this.emit('warn', `Unknown actual ventilation mode: ${actualVentilationMode}`);
1280
+
1281
+ obj.targetOperationMode = 0; // auto
1286
1282
  break;
1287
1283
  default:
1288
1284
  if (this.logWarn) this.emit('warn', `Unknown ventilation mode: ${ventilationMode}`);
1289
1285
  break;
1290
- };
1286
+ }
1287
+
1288
+ // power override
1289
+ if (!power) obj.currentOperationMode = 0; // inactive
1291
1290
 
1292
- obj.currentOperationMode = !power ? 0 : obj.currentOperationMode;
1293
- obj.targetOperationMode = obj.targetOperationMode;
1294
- obj.operationModeSetPropsMinValue = supportsAutoVentilationMode ? 0 : 1;
1295
- obj.operationModeSetPropsMaxValue = supportsAutoVentilationMode ? 2 : 2;
1296
- obj.operationModeSetPropsValidValues = supportsAutoVentilationMode ? (supportsBypassVentilationMode ? [0, 1, 2] : [0, 2]) : (supportsBypassVentilationMode ? [1, 2] : [2]);
1291
+ if (supportsAutoVentilationMode) operationModevalidValues.push(0);
1292
+ if (supportsBypassVentilationMode) operationModevalidValues.push(1);
1293
+ operationModevalidValues.push(2); // manual zawsze dostępny
1294
+
1295
+ obj.operationModeSetPropsMinValue = operationModevalidValues[0];
1296
+ obj.operationModeSetPropsMaxValue = operationModevalidValues.at(-1);
1297
+ obj.operationModeSetPropsValidValues = operationModevalidValues;
1297
1298
 
1298
1299
  //fan speed mode
1299
1300
  obj.fanSpeedSetPropsMaxValue = 2;
@@ -1330,38 +1331,42 @@ class DeviceErv extends EventEmitter {
1330
1331
  case 2: //Thermostat
1331
1332
  //operation mode - 0, HEAT, 2, COOL, 4, 5, 6, FAN, AUTO
1332
1333
  switch (ventilationMode) {
1333
- case 0: //LOSSNAY
1334
- obj.currentOperationMode = 1; //OFF, HEAT, COOL
1335
- obj.targetOperationMode = 1; //OFF, HEAT, COOL, AUTO
1334
+ case 0: // LOSSNAY
1335
+ obj.currentOperationMode = 1; // HEAT
1336
+ obj.targetOperationMode = 1; // HEAT
1336
1337
  break;
1337
- case 1: //BYPASS
1338
- obj.currentOperationMode = 2;
1339
- obj.targetOperationMode = 2;
1338
+ case 1: // BYPASS
1339
+ obj.currentOperationMode = 2; // COOL
1340
+ obj.targetOperationMode = 2; // COOL
1340
1341
  break;
1341
- case 2: //AUTO
1342
- switch (actualVentilationMode) {
1343
- case 0: //LOSSNAY
1344
- obj.currentOperationMode = 1;
1345
- break;
1346
- case 1: //BYPASS
1347
- obj.currentOperationMode = 2;
1348
- break;
1349
- default:
1350
- if (this.logWarn) this.emit('warn', `Unknown actual ventilation mode: ${actualVentilationMode}`);
1351
- break;
1352
- };
1353
- obj.targetOperationMode = 3;
1342
+ case 2: // AUTO
1343
+ if (actualVentilationMode === 0) {
1344
+ obj.currentOperationMode = 1; // HEAT
1345
+ } else if (actualVentilationMode === 1) {
1346
+ obj.currentOperationMode = 2; // COOL
1347
+ } else if (this.logWarn) this.emit('warn', `Unknown actual ventilation mode: ${actualVentilationMode}`);
1348
+
1349
+ obj.targetOperationMode = 3; // AUTO
1354
1350
  break;
1355
1351
  default:
1356
1352
  if (this.logWarn) this.emit('warn', `Unknown ventilation mode: ${ventilationMode}`);
1357
1353
  break;
1358
- };
1354
+ }
1355
+
1356
+ // power override (single source of truth)
1357
+ if (!power) {
1358
+ obj.currentOperationMode = 0;
1359
+ obj.targetOperationMode = 0;
1360
+ }
1361
+
1362
+ operationModevalidValues.push(0); // manual zawsze dostępny
1363
+ if (supportsBypassVentilationMode) operationModevalidValues.push(1);
1364
+ operationModevalidValues.push(2); // manual / normal
1365
+ if (supportsAutoVentilationMode) operationModevalidValues.push(3);
1359
1366
 
1360
- obj.currentOperationMode = !power ? 0 : obj.currentOperationMode;
1361
- obj.targetOperationMode = !power ? 0 : obj.targetOperationMode;
1362
- obj.operationModeSetPropsMinValue = supportsAutoVentilationMode ? 0 : 0;
1363
- obj.operationModeSetPropsMaxValue = supportsAutoVentilationMode ? 3 : 2;
1364
- obj.operationModeSetPropsValidValues = supportsAutoVentilationMode ? (supportsBypassVentilationMode ? [0, 1, 2, 3] : [0, 2, 3]) : (supportsBypassVentilationMode ? [0, 1, 2] : [0, 2]);
1367
+ obj.operationModeSetPropsMinValue = operationModevalidValues[0];
1368
+ obj.operationModeSetPropsMaxValue = operationModevalidValues.at(-1);
1369
+ obj.operationModeSetPropsValidValues = operationModevalidValues;
1365
1370
 
1366
1371
  //create characteristics
1367
1372
  characteristics.push(
package/src/functions.js CHANGED
@@ -56,162 +56,157 @@ class Functions extends EventEmitter {
56
56
 
57
57
  async ensureChromiumInstalled() {
58
58
  try {
59
- // Detect OS
60
- const { stdout: osOut } = await execPromise("uname -s");
59
+ const { stdout: osOut } = await execPromise('uname -s');
61
60
  const osName = osOut.trim();
62
- const { stdout: archOut } = await execPromise("uname -m");
63
- const arch = archOut.trim();
64
61
 
65
- const isARM = arch.startsWith("arm") || arch.startsWith("aarch64") || arch.startsWith("aarch");
66
- const isMac = osName === "Darwin";
67
- const isLinux = osName === "Linux";
62
+ const { stdout: archOut } = await execPromise('uname -m');
63
+ let arch = archOut.trim() || 'unknown';
64
+
65
+ // Normalizacja architektury
66
+ if (arch.startsWith('arm') || arch.startsWith('aarch')) arch = 'arm';
67
+ else if (arch.includes('64')) arch = 'x64';
68
+ else arch = 'x86';
69
+
70
+ const isARM = arch === 'arm';
71
+ const isMac = osName === 'Darwin';
72
+ const isLinux = osName === 'Linux';
73
+ const isQnap = fs.existsSync('/etc/config/uLinux.conf') || fs.existsSync('/etc/config/qpkg.conf');
68
74
 
69
75
  // Detect Docker
70
76
  let isDocker = false;
71
77
  try {
72
- await access("/.dockerenv");
78
+ await access('/.dockerenv');
73
79
  isDocker = true;
74
80
  } catch { }
81
+
75
82
  try {
76
- const { stdout } = await execPromise("cat /proc/1/cgroup || true");
77
- if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
83
+ const { stdout } = await execPromise('cat /proc/1/cgroup');
84
+ if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
78
85
  } catch { }
79
86
 
80
- // macOS
87
+ const result = { path: null, arch, system: 'unknown' };
88
+
89
+ /* ===================== macOS ===================== */
81
90
  if (isMac) {
82
- const macCandidates = [
83
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
84
- "/Applications/Chromium.app/Contents/MacOS/Chromium"
85
- ];
86
- for (const p of macCandidates) {
91
+ const macCandidates = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/Applications/Chromium.app/Contents/MacOS/Chromium'];
92
+ for (const path of macCandidates) {
87
93
  try {
88
- await access(p, fs.constants.X_OK);
89
- return p;
94
+ await access(path, fs.constants.X_OK);
95
+ result.path = path;
96
+ result.system = 'macOS';
97
+ return result;
90
98
  } catch { }
91
99
  }
92
- return null;
100
+ return result;
93
101
  }
94
102
 
95
- // ARM / Raspberry Pi
96
- if (isARM && isLinux) {
97
- const armCandidates = [
98
- "/usr/bin/chromium-browser",
99
- "/usr/bin/chromium",
100
- "/snap/bin/chromium"
101
- ];
102
-
103
- // Try existing
104
- for (const p of armCandidates) {
103
+ /* ===================== QNAP ===================== */
104
+ if (isQnap) {
105
+ const qnapCandidates = ['/opt/bin/chromium', '/opt/bin/chromium-browser'];
106
+ for (const path of qnapCandidates) {
105
107
  try {
106
- await access(p, fs.constants.X_OK);
107
- return p;
108
+ await access(path, fs.constants.X_OK);
109
+ result.path = path;
110
+ result.system = 'Qnap';
111
+ return result;
108
112
  } catch { }
109
113
  }
110
114
 
111
- // If not in Docker, try apt installation
112
- if (!isDocker) {
115
+ try {
116
+ await access('/opt/bin/opkg', fs.constants.X_OK);
117
+ await execPromise('/opt/bin/opkg update');
118
+ await execPromise('opkg install chromium nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib');
119
+ process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
120
+ } catch (error) {
121
+ if (this.logError) this.emit('error', `Install package for Qnap error: ${error}`);
122
+ }
123
+
124
+ for (const path of qnapCandidates) {
113
125
  try {
114
- await execPromise("sudo apt-get update -y");
126
+ await access(path, fs.constants.X_OK);
127
+ result.path = path;
128
+ result.system = 'Qnap';
129
+ return result;
115
130
  } catch { }
131
+ }
132
+ return result;
133
+ }
134
+
135
+ /* ===================== Linux ARM ===================== */
136
+ if (isLinux && isARM) {
137
+ const armCandidates = ['/usr/bin/chromium-browser', '/usr/bin/chromium', '/snap/bin/chromium'];
138
+ for (const path of armCandidates) {
116
139
  try {
117
- await execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true");
140
+ await access(path, fs.constants.X_OK);
141
+ result.path = path;
142
+ result.system = 'Linux';
143
+ return result;
118
144
  } catch { }
145
+ }
146
+
147
+ if (!isDocker) {
119
148
  try {
120
- await execPromise("sudo apt-get install -y chromium || true");
121
- } catch { }
149
+ await execPromise('sudo apt update -y');
150
+ await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2');
151
+ await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg');
152
+ } catch (error) {
153
+ if (this.logError) this.emit('error', `Install package for Linux ARM error: ${error}`);
154
+ }
122
155
  }
123
156
 
124
- // Retry after installation
125
- for (const p of armCandidates) {
157
+ for (const path of armCandidates) {
126
158
  try {
127
- await access(p, fs.constants.X_OK);
128
- return p;
159
+ await access(path, fs.constants.X_OK);
160
+ result.path = path;
161
+ result.system = 'Linux';
162
+ return result;
129
163
  } catch { }
130
164
  }
131
-
132
- return null;
133
- }
134
-
135
- // QNAP / Entware
136
- let entwareExists = false;
137
- try {
138
- await access("/opt/bin/opkg", fs.constants.X_OK);
139
- entwareExists = true;
140
- } catch { }
141
-
142
- if (entwareExists) {
143
- try {
144
- await execPromise("/opt/bin/opkg update");
145
- await execPromise("/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib");
146
- process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ""}`;
147
- } catch { }
165
+ return result;
148
166
  }
149
167
 
150
- // Synology DSM 7
151
- const synoCandidates = [
152
- "/var/packages/Chromium/target/usr/bin/chromium",
153
- "/usr/local/chromium/bin/chromium"
154
- ];
155
- for (const p of synoCandidates) {
156
- try {
157
- await access(p, fs.constants.X_OK);
158
- return p;
159
- } catch { }
160
- }
161
-
162
- // Linux x64
168
+ /* ===================== Linux x64 ===================== */
163
169
  if (isLinux) {
164
- const linuxCandidates = [
165
- "/usr/bin/chromium",
166
- "/usr/bin/chromium-browser",
167
- "/usr/bin/google-chrome",
168
- "/snap/bin/chromium",
169
- "/usr/local/bin/chromium"
170
- ];
171
-
170
+ const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
172
171
  try {
173
- const { stdout } = await execPromise("which chromium || which chromium-browser || which google-chrome || true");
174
- const found = stdout.trim();
175
- if (found) return found;
172
+ const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome');
173
+ if (stdout.trim()) {
174
+ result.path = stdout.trim();
175
+ return result;
176
+ }
176
177
  } catch { }
177
178
 
178
- for (const p of linuxCandidates) {
179
+ for (const path of linuxCandidates) {
179
180
  try {
180
- await access(p, fs.constants.X_OK);
181
- return p;
181
+ await access(path, fs.constants.X_OK);
182
+ result.path = path;
183
+ result.system = 'Linux';
184
+ return result;
182
185
  } catch { }
183
186
  }
184
187
 
185
- // Docker: try installing chromium inside container (if allowed)
186
188
  if (isDocker) {
187
189
  try {
188
- await execPromise("apt-get update -y && apt-get install -y chromium || true");
189
- } catch { }
190
- try {
191
- await access("/usr/bin/chromium", fs.constants.X_OK);
192
- return "/usr/bin/chromium";
193
- } catch { }
194
- }
195
-
196
- // Install missing libraries
197
- const depCommands = [
198
- "apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2 || true",
199
- "yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib || true",
200
- "apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib || true"
201
- ];
202
- for (const cmd of depCommands) {
203
- try {
204
- await execPromise(`sudo ${cmd}`);
205
- } catch { }
190
+ await execPromise('apt update -y && apt install -y chromium');
191
+ } catch (error) {
192
+ if (this.logError) this.emit('error', `Install package for Linux Docker error: ${error}`);
193
+ }
194
+
195
+ for (const path of linuxCandidates) {
196
+ try {
197
+ await access(path, fs.constants.X_OK);
198
+ result.path = path;
199
+ result.system = 'Linux Docker';
200
+ return result;
201
+ } catch { }
202
+ }
206
203
  }
207
-
208
- return null;
209
204
  }
210
205
 
211
- return null;
212
- } catch (err) {
213
- if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
214
- return null;
206
+ return result;
207
+ } catch (error) {
208
+ if (this.logError) this.emit('error', `Chromium detection error: ${error.message}`);
209
+ return { path: null, arch: 'unknown', system: 'unknown' };
215
210
  }
216
211
  }
217
212
 
@@ -280,7 +275,6 @@ class Functions extends EventEmitter {
280
275
 
281
276
  return { min, max };
282
277
  }
283
-
284
278
  }
285
279
 
286
280
  export default Functions
package/src/melcloud.js CHANGED
@@ -54,7 +54,7 @@ class MelCloud extends EventEmitter {
54
54
 
55
55
  async checkDevicesList() {
56
56
  try {
57
- const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
57
+ const devicesList = { State: false, Info: null, Buildings: [], Devices: [], Scenes: [] }
58
58
  if (this.logDebug) this.emit('debug', `Scanning for devices...`);
59
59
  const listDevicesData = await this.client(ApiUrls.Get.ListDevices, { method: 'GET', });
60
60
 
@@ -71,9 +71,6 @@ class MelCloud extends EventEmitter {
71
71
  return devicesList;
72
72
  }
73
73
 
74
- await this.functions.saveData(this.buildingsFile, buildingsList);
75
- if (this.logDebug) this.emit('debug', `Buildings list saved`);
76
-
77
74
  const devices = [];
78
75
  for (const building of buildingsList) {
79
76
  if (!building.Structure) {
@@ -108,7 +105,12 @@ class MelCloud extends EventEmitter {
108
105
 
109
106
  devicesList.State = true;
110
107
  devicesList.Info = `Found ${devicesCount} devices`;
108
+ devicesList.Buildings = buildingsList;
111
109
  devicesList.Devices = devices;
110
+
111
+ await this.functions.saveData(this.buildingsFile, devicesList);
112
+ if (this.logDebug) this.emit('debug', `Buildings list saved`);
113
+
112
114
  this.emit('devicesList', devicesList);
113
115
 
114
116
  return devicesList;
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrls, AirConditioner } from './constants.js';
3
+ import { ApiUrls, AirConditioner } from './constants.js';
4
4
 
5
5
  class MelCloudAta extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -72,7 +72,34 @@ class MelCloudAta extends EventEmitter {
72
72
  }
73
73
  }
74
74
 
75
- if (this.logDebug) this.emit('debug', `WS update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
75
+ updateState = true;
76
+ break;
77
+ case 'ataUnitFrostProtectionTriggered':
78
+ deviceData.FrostProtection.Active = messageData.active;
79
+
80
+ //update device settings
81
+ for (const [key, value] of Object.entries(settings)) {
82
+ if (!this.functions.isValidValue(value)) continue;
83
+
84
+ if (key in deviceData.Device) {
85
+ deviceData.Device[key] = value;
86
+ }
87
+ }
88
+
89
+ updateState = true;
90
+ break;
91
+ case 'ataUnitOverheatProtectionTriggered':
92
+ deviceData.OverheatProtection.Active = messageData.active;
93
+
94
+ //update device settings
95
+ for (const [key, value] of Object.entries(settings)) {
96
+ if (!this.functions.isValidValue(value)) continue;
97
+
98
+ if (key in deviceData.Device) {
99
+ deviceData.Device[key] = value;
100
+ }
101
+ }
102
+
76
103
  updateState = true;
77
104
  break;
78
105
  case 'unitHolidayModeTriggered':
@@ -85,6 +112,10 @@ class MelCloudAta extends EventEmitter {
85
112
  deviceData.Rssi = messageData.rssi;
86
113
  updateState = true;
87
114
  break;
115
+ case 'unitCommunicationRestored':
116
+ timestamp = messageData.timestamp;
117
+ deviceData.Device.IsConnected = true;
118
+ break;
88
119
  default:
89
120
  if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
90
121
  return;