homebridge-melcloud-control 4.4.1-beta.2 → 4.4.1-beta.20

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/functions.js CHANGED
@@ -56,162 +56,152 @@ 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 { }
75
81
  try {
76
- const { stdout } = await execPromise("cat /proc/1/cgroup || true");
77
- if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
82
+ const { stdout } = await execPromise('cat /proc/1/cgroup || true');
83
+ if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
78
84
  } catch { }
79
85
 
80
- // macOS
86
+ const result = { path: null, arch, system: null };
87
+
88
+ /* ===================== macOS ===================== */
81
89
  if (isMac) {
82
90
  const macCandidates = [
83
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
84
- "/Applications/Chromium.app/Contents/MacOS/Chromium"
91
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
92
+ '/Applications/Chromium.app/Contents/MacOS/Chromium'
85
93
  ];
86
- for (const p of macCandidates) {
94
+ for (const path of macCandidates) {
87
95
  try {
88
- await access(p, fs.constants.X_OK);
89
- return p;
96
+ await access(path, fs.constants.X_OK);
97
+ result.path = path;
98
+ result.system = 'macOS';
99
+ return result;
90
100
  } catch { }
91
101
  }
92
- return null;
102
+ return result;
93
103
  }
94
104
 
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) {
105
+ /* ===================== QNAP ===================== */
106
+ if (isQnap) {
107
+ const qnapCandidates = ['/opt/bin/chromium', '/opt/bin/chromium-browser'];
108
+ for (const path of qnapCandidates) {
105
109
  try {
106
- await access(p, fs.constants.X_OK);
107
- return p;
110
+ await access(path, fs.constants.X_OK);
111
+ result.path = path;
112
+ result.system = 'QNAP';
113
+ return result;
108
114
  } catch { }
109
115
  }
110
116
 
111
- // If not in Docker, try apt installation
112
- if (!isDocker) {
117
+ try {
118
+ await access('/opt/bin/opkg', fs.constants.X_OK);
119
+ await execPromise('/opt/bin/opkg update');
120
+ await execPromise('opkg install chromium nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib');
121
+ process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
122
+ } catch { }
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
+
139
+ for (const path of armCandidates) {
116
140
  try {
117
- await execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true");
141
+ await access(path, fs.constants.X_OK);
142
+ result.path = path;
143
+ result.system = 'Linux';
144
+ return result;
118
145
  } catch { }
146
+ }
147
+
148
+ if (!isDocker) {
119
149
  try {
120
- await execPromise("sudo apt-get install -y chromium || true");
121
- } catch { }
150
+ this.emit('debug', `Try install chromium`);
151
+ await execPromise('sudo apt update -y');
152
+ await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg || true');
153
+ await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2 || true');
154
+ } catch (error) {
155
+ this.emit('debug', `Install chromium failed ${error}`);
156
+ }
122
157
  }
123
158
 
124
- // Retry after installation
125
- for (const p of armCandidates) {
159
+ for (const path of armCandidates) {
126
160
  try {
127
- await access(p, fs.constants.X_OK);
128
- return p;
161
+ await access(path, fs.constants.X_OK);
162
+ result.path = path;
163
+ result.system = 'Linux';
164
+ return result;
129
165
  } catch { }
130
166
  }
131
-
132
- return null;
167
+ return result;
133
168
  }
134
169
 
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 { }
148
- }
149
-
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
170
+ /* ===================== Linux x64 ===================== */
163
171
  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
-
172
+ const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
172
173
  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;
174
+ const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome || true');
175
+ if (stdout.trim()) {
176
+ result.path = stdout.trim();
177
+ return result;
178
+ }
176
179
  } catch { }
177
180
 
178
- for (const p of linuxCandidates) {
181
+ for (const path of linuxCandidates) {
179
182
  try {
180
- await access(p, fs.constants.X_OK);
181
- return p;
183
+ await access(path, fs.constants.X_OK);
184
+ result.path = path;
185
+ result.system = 'Linux';
186
+ return result;
182
187
  } catch { }
183
188
  }
184
189
 
185
- // Docker: try installing chromium inside container (if allowed)
186
190
  if (isDocker) {
187
191
  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}`);
192
+ await execPromise('apt update -y && apt install -y chromium || true');
193
+ await access('/usr/bin/chromium', fs.constants.X_OK);
194
+ result.path = '/usr/bin/chromium';
195
+ result.system = 'Linux Docker';
196
+ return result;
205
197
  } catch { }
206
198
  }
207
-
208
- return null;
209
199
  }
210
200
 
211
- return null;
201
+ return result;
212
202
  } catch (err) {
213
- if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
214
- return null;
203
+ if (this.logError) this.emit('error', `Chromium detection error: ${err.message}`);
204
+ return { path: null, arch: 'unknown' };
215
205
  }
216
206
  }
217
207
 
@@ -280,7 +270,6 @@ class Functions extends EventEmitter {
280
270
 
281
271
  return { min, max };
282
272
  }
283
-
284
273
  }
285
274
 
286
275
  export default Functions
package/src/melcloud.js CHANGED
@@ -56,7 +56,7 @@ class MelCloud extends EventEmitter {
56
56
  try {
57
57
  const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
58
58
  if (this.logDebug) this.emit('debug', `Scanning for devices...`);
59
- const listDevicesData = await this.client(ApiUrls.ListDevices, { method: 'GET', });
59
+ const listDevicesData = await this.client(ApiUrls.Get.ListDevices, { method: 'GET', });
60
60
 
61
61
  if (!listDevicesData || !listDevicesData.data) {
62
62
  devicesList.Info = 'Invalid or empty response from MELCloud API'
@@ -132,9 +132,9 @@ class MelCloud extends EventEmitter {
132
132
  CaptchaResponse: '',
133
133
  Persist: true
134
134
  };
135
- const accountData = await axios(ApiUrls.ClientLogin, {
135
+ const accountData = await axios(ApiUrls.Post.ClientLogin, {
136
136
  method: 'POST',
137
- baseURL: ApiUrls.BaseURL,
137
+ baseURL: ApiUrls.Base,
138
138
  timeout: 15000,
139
139
  data: payload
140
140
  });
@@ -164,7 +164,7 @@ class MelCloud extends EventEmitter {
164
164
  };
165
165
 
166
166
  this.client = axios.create({
167
- baseURL: ApiUrls.BaseURL,
167
+ baseURL: ApiUrls.Base,
168
168
  timeout: 30000,
169
169
  headers: headers
170
170
  });
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, 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) {
@@ -204,7 +204,7 @@ class MelCloudAta extends EventEmitter {
204
204
  case 'account':
205
205
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
206
206
  payload = { data: flagData.LoginData };
207
- path = ApiUrls.UpdateApplicationOptions;
207
+ path = ApiUrls.Post.UpdateApplicationOptions;
208
208
  await this.functions.saveData(this.accountFile, flagData);
209
209
  break;
210
210
  default:
@@ -230,7 +230,7 @@ class MelCloudAta extends EventEmitter {
230
230
  HideDryModeControl: deviceData.HideDryModeControl,
231
231
  HasPendingCommand: true
232
232
  };
233
- path = ApiUrls.SetAta;
233
+ path = ApiUrls.Post.Ata;
234
234
  update = true;
235
235
  break;
236
236
  }
@@ -254,7 +254,7 @@ class MelCloudAta extends EventEmitter {
254
254
  units: { ATA: [deviceData.DeviceID] }
255
255
  };
256
256
  method = 'POST';
257
- path = ApiUrlsHome.PostProtectionFrost;
257
+ path = ApiUrls.Home.Post.ProtectionFrost;
258
258
  update = true;
259
259
  break;
260
260
  case 'overheatprotection':
@@ -265,7 +265,7 @@ class MelCloudAta extends EventEmitter {
265
265
  units: { ATA: [deviceData.DeviceID] }
266
266
  };
267
267
  method = 'POST';
268
- path = ApiUrlsHome.PostProtectionOverheat;
268
+ path = ApiUrls.Home.Post.ProtectionOverheat;
269
269
  update = true;
270
270
  break;
271
271
  case 'holidaymode':
@@ -276,17 +276,17 @@ class MelCloudAta extends EventEmitter {
276
276
  units: { ATA: [deviceData.DeviceID] }
277
277
  };
278
278
  method = 'POST';
279
- path = ApiUrlsHome.PostHolidayMode;
279
+ path = ApiUrls.Home.Post.HolidayMode;
280
280
  break;
281
281
  case 'schedule':
282
282
  payload = { enabled: deviceData.ScheduleEnabled };
283
283
  method = 'PUT';
284
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
284
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
285
285
  update = true;
286
286
  break;
287
287
  case 'scene':
288
288
  method = 'PUT';
289
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
289
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
290
290
  break;
291
291
  default:
292
292
  if (displayType === 1 && deviceData.Device.OperationMode === 8) {
@@ -312,7 +312,7 @@ class MelCloudAta extends EventEmitter {
312
312
  inStandbyMode: null
313
313
  };
314
314
  method = 'PUT';
315
- path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
315
+ path = ApiUrls.Home.Put.Ata.replace('deviceid', deviceData.DeviceID);
316
316
  break;
317
317
  }
318
318
 
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
3
+ import { ApiUrls, HeatPump } from './constants.js';
4
4
 
5
5
  class MelCloudAtw extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -204,7 +204,7 @@ class MelCloudAtw extends EventEmitter {
204
204
  case 'account':
205
205
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
206
206
  payload = { data: flagData.LoginData };
207
- path = ApiUrls.UpdateApplicationOptions;
207
+ path = ApiUrls.Post.UpdateApplicationOptions;
208
208
  await this.functions.saveData(this.accountFile, flagData);
209
209
  break;
210
210
  default:
@@ -231,7 +231,7 @@ class MelCloudAtw extends EventEmitter {
231
231
  ProhibitHotWater: deviceData.Device.ProhibitHotWater,
232
232
  HasPendingCommand: true
233
233
  }
234
- path = ApiUrls.SetAtw;
234
+ path = ApiUrls.Post.Atw;
235
235
  update = true;
236
236
  break;
237
237
  }
@@ -255,7 +255,7 @@ class MelCloudAtw extends EventEmitter {
255
255
  units: { ATA: [deviceData.DeviceID] }
256
256
  };
257
257
  method = 'POST';
258
- path = ApiUrlsHome.PostProtectionFrost;
258
+ path = ApiUrls.Home.Post.ProtectionFrost;
259
259
  update = true;
260
260
  break;
261
261
  case 'holidaymode':
@@ -266,17 +266,17 @@ class MelCloudAtw extends EventEmitter {
266
266
  units: { ATW: [deviceData.DeviceID] }
267
267
  };
268
268
  method = 'POST';
269
- path = ApiUrlsHome.PostHolidayMode;
269
+ path = ApiUrls.Home.Post.HolidayMode;
270
270
  break;
271
271
  case 'schedule':
272
272
  payload = { enabled: deviceData.ScheduleEnabled };
273
273
  method = 'PUT';
274
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
274
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
275
275
  update = true;
276
276
  break;
277
277
  case 'scene':
278
278
  method = 'PUT';
279
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
279
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
280
280
  break;
281
281
  default:
282
282
  payload = {
@@ -295,7 +295,7 @@ class MelCloudAtw extends EventEmitter {
295
295
  ecoHotWater: deviceData.Device.EcoHotWater,
296
296
  };
297
297
  method = 'PUT';
298
- path = ApiUrlsHome.PutAtw.replace('deviceid', deviceData.DeviceID);
298
+ path = ApiUrls.Home.Put.Atw.replace('deviceid', deviceData.DeviceID);
299
299
  break
300
300
  }
301
301
 
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
3
+ import { ApiUrls, Ventilation } from './constants.js';
4
4
 
5
5
  class MelCloudErv extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -189,7 +189,7 @@ class MelCloudErv extends EventEmitter {
189
189
  case 'account':
190
190
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
191
191
  payload = { data: flagData.LoginData };
192
- path = ApiUrls.UpdateApplicationOptions;
192
+ path = ApiUrls.Post.UpdateApplicationOptions;
193
193
  await this.functions.saveData(this.accountFile, flagData);
194
194
  break;
195
195
  default:
@@ -231,7 +231,7 @@ class MelCloudErv extends EventEmitter {
231
231
  NightPurgeMode: deviceData.Device.NightPurgeMode,
232
232
  HasPendingCommand: true
233
233
  }
234
- path = ApiUrls.SetErv;
234
+ path = ApiUrls.Post.Erv;
235
235
  update = true;
236
236
  break;
237
237
  }
@@ -255,17 +255,17 @@ class MelCloudErv extends EventEmitter {
255
255
  units: { ERV: [deviceData.DeviceID] }
256
256
  };
257
257
  method = 'POST';
258
- path = ApiUrlsHome.PostHolidayMode;
258
+ path = ApiUrls.Home.Post.HolidayMode;
259
259
  break;
260
260
  case 'schedule':
261
261
  payload = { enabled: deviceData.ScheduleEnabled };
262
262
  method = 'PUT';
263
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
263
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
264
264
  update = true;
265
265
  break;
266
266
  case 'scene':
267
267
  method = 'PUT';
268
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
268
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
269
269
  break;
270
270
  default:
271
271
  if (displayType === 1 && deviceData.Device.VentilationMode === 2) {
@@ -288,7 +288,7 @@ class MelCloudErv extends EventEmitter {
288
288
  ventilationMode: Ventilation.VentilationModeMapEnumToString[deviceData.Device.VentilationMode],
289
289
  };
290
290
  method = 'PUT';
291
- path = ApiUrlsHome.PutErv.replace('deviceid', deviceData.DeviceID);
291
+ path = ApiUrls.Home.Put.Erv.replace('deviceid', deviceData.DeviceID);
292
292
  break
293
293
  }
294
294
 
@@ -1,4 +1,3 @@
1
- import fs from 'fs';
2
1
  import axios from 'axios';
3
2
  import WebSocket from 'ws';
4
3
  import { exec } from 'child_process';
@@ -7,7 +6,7 @@ import EventEmitter from 'events';
7
6
  import puppeteer from 'puppeteer';
8
7
  import ImpulseGenerator from './impulsegenerator.js';
9
8
  import Functions from './functions.js';
10
- import { ApiUrlsHome, LanguageLocaleMap } from './constants.js';
9
+ import { ApiUrls, LanguageLocaleMap } from './constants.js';
11
10
  const execPromise = promisify(exec);
12
11
 
13
12
  class MelCloudHome extends EventEmitter {
@@ -17,7 +16,6 @@ class MelCloudHome extends EventEmitter {
17
16
  this.user = account.user;
18
17
  this.passwd = account.passwd;
19
18
  this.language = account.language;
20
- this.logSuccess = account.log?.success;
21
19
  this.logWarn = account.log?.warn;
22
20
  this.logError = account.log?.error;
23
21
  this.logDebug = account.log?.debug;
@@ -79,7 +77,7 @@ class MelCloudHome extends EventEmitter {
79
77
  async checkScenesList() {
80
78
  try {
81
79
  if (this.logDebug) this.emit('debug', `Scanning for scenes`);
82
- const listScenesData = await this.client(ApiUrlsHome.GetUserScenes, { method: 'GET', });
80
+ const listScenesData = await this.client(ApiUrls.Home.Get.Scenes, { method: 'GET', });
83
81
 
84
82
  const scenesList = listScenesData.data;
85
83
  if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
@@ -111,7 +109,7 @@ class MelCloudHome extends EventEmitter {
111
109
  try {
112
110
  const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
113
111
  if (this.logDebug) this.emit('debug', `Scanning for devices`);
114
- const listDevicesData = await this.client(ApiUrlsHome.GetUserContext, { method: 'GET' });
112
+ const listDevicesData = await this.client(ApiUrls.Home.Get.ListDevices, { method: 'GET' });
115
113
 
116
114
  const userContext = listDevicesData.data;
117
115
  const buildings = userContext.buildings ?? [];
@@ -225,32 +223,37 @@ class MelCloudHome extends EventEmitter {
225
223
  try {
226
224
  const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
227
225
 
228
- // Get Chromium path
229
- let chromiumPath = await this.functions.ensureChromiumInstalled();
230
-
231
- // Fallback to Puppeteer's bundled Chromium
232
- if (!chromiumPath) {
233
- try {
234
- const puppeteerPath = puppeteer.executablePath();
235
- if (puppeteerPath && puppeteerPath.length > 0) {
236
- chromiumPath = puppeteerPath;
237
- if (this.logDebug) this.emit('debug', `Using Puppeteer bundled Chromium at ${chromiumPath}`);
238
- } else {
239
- accountInfo.Info = `Puppeteer returned empty Chromium path`;
226
+ // Get Chromium path from resolver
227
+ const chromiumInfo = await this.functions.ensureChromiumInstalled();
228
+ let chromiumPath = chromiumInfo.path;
229
+ const arch = chromiumInfo.arch;
230
+ const system = chromiumInfo.system;
231
+
232
+ // If path is found, use it
233
+ if (chromiumPath) {
234
+ if (!this.logDebug) this.emit('debug', `Using Chromium ${system} (${arch}) at ${chromiumPath}`);
235
+ } else {
236
+ if (arch === 'arm') {
237
+ accountInfo.Info = `No Chromium found for ${system} (${arch}). Please install it manually and try again.`;
238
+ return accountInfo;
239
+ } else {
240
+ try {
241
+ chromiumPath = puppeteer.executablePath();
242
+ if (!this.logDebug) this.emit('debug', `Using Puppeteer Chromium for ${system} (${arch}) at ${chromiumPath}`);
243
+ } catch (error) {
244
+ accountInfo.Info = `No Chromium available for ${system} (${arch})`;
240
245
  return accountInfo;
241
246
  }
242
- } catch (error) {
243
- accountInfo.Info = `Failed to get Puppeteer Chromium path: ${error.message}`;
244
- return accountInfo;
245
247
  }
246
248
  }
247
249
 
250
+
248
251
  // Verify Chromium executable
249
252
  try {
250
253
  const { stdout } = await execPromise(`"${chromiumPath}" --version`);
251
- if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
254
+ if (!this.logDebug) this.emit('debug', `Chromium for ${system} (${arch}) detected: ${stdout.trim()}`);
252
255
  } catch (error) {
253
- accountInfo.Info = `Chromium found at ${chromiumPath}, but cannot be executed: ${error.message}`;
256
+ accountInfo.Info = `Chromium for ${system} (${arch}) found at ${chromiumPath}, but cannot be executed: ${error.message}`;
254
257
  return accountInfo;
255
258
  }
256
259
 
@@ -282,22 +285,22 @@ class MelCloudHome extends EventEmitter {
282
285
  await client.send('Network.enable')
283
286
  client.on('Network.webSocketCreated', ({ url }) => {
284
287
  try {
285
- if (url.startsWith(`${ApiUrlsHome.WebSocketURL}`)) {
288
+ if (url.startsWith(`${ApiUrls.Home.WebSocket}`)) {
286
289
  const params = new URL(url).searchParams;
287
290
  const hash = params.get('hash');
288
291
  if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
289
292
 
290
- //web socket connection
293
+ // Web socket connection
291
294
  if (!this.connecting && !this.socketConnected) {
292
295
  this.connecting = true;
293
296
 
294
297
  try {
295
298
  const headers = {
296
- 'Origin': ApiUrlsHome.BaseURL,
299
+ 'Origin': ApiUrls.Home.Base,
297
300
  'Pragma': 'no-cache',
298
301
  'Cache-Control': 'no-cache'
299
302
  };
300
- const webSocket = new WebSocket(`${ApiUrlsHome.WebSocketURL}${hash}`, { headers: headers })
303
+ const webSocket = new WebSocket(`${ApiUrls.Home.WebSocket}${hash}`, { headers: headers })
301
304
  .on('error', (error) => {
302
305
  if (this.logError) this.emit('error', `Web socket error: ${error}`);
303
306
  try {
@@ -326,7 +329,7 @@ class MelCloudHome extends EventEmitter {
326
329
  })
327
330
  .on('message', (message) => {
328
331
  const parsedMessage = JSON.parse(message);
329
- if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
332
+ if (!this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
330
333
  if (parsedMessage.message === 'Forbidden') return;
331
334
 
332
335
  this.emit('webSocket', parsedMessage);
@@ -343,9 +346,9 @@ class MelCloudHome extends EventEmitter {
343
346
  });
344
347
 
345
348
  try {
346
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
349
+ await page.goto(ApiUrls.Home.Base, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
347
350
  } catch (error) {
348
- accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
351
+ accountInfo.Info = `Navigation to ${ApiUrls.Home.Base} failed: ${error.message}`;
349
352
  return accountInfo;
350
353
  }
351
354
 
@@ -407,7 +410,7 @@ class MelCloudHome extends EventEmitter {
407
410
  'Accept-Language': LanguageLocaleMap[this.language],
408
411
  'Cookie': cookies,
409
412
  'Priority': 'u=3, i',
410
- 'Referer': ApiUrlsHome.Dashboard,
413
+ 'Referer': ApiUrls.Home.Dashboard,
411
414
  'Sec-Fetch-Dest': 'empty',
412
415
  'Sec-Fetch-Mode': 'cors',
413
416
  'Sec-Fetch-Site': 'same-origin',
@@ -416,7 +419,7 @@ class MelCloudHome extends EventEmitter {
416
419
  };
417
420
 
418
421
  this.client = axios.create({
419
- baseURL: ApiUrlsHome.BaseURL,
422
+ baseURL: ApiUrls.Home.Base,
420
423
  timeout: 30000,
421
424
  headers: headers
422
425
  })