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

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,155 @@ 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 (error) {
123
+ if (this.logError) this.emit('error', `Install package for Qnap error: ${error}`);
124
+ }
125
+
126
+ for (const path of qnapCandidates) {
113
127
  try {
114
- await execPromise("sudo apt-get update -y");
128
+ await access(path, fs.constants.X_OK);
129
+ result.path = path;
130
+ result.system = 'QNAP';
131
+ return result;
115
132
  } catch { }
133
+ }
134
+ return result;
135
+ }
136
+
137
+ /* ===================== Linux ARM ===================== */
138
+ if (isLinux && isARM) {
139
+ const armCandidates = ['/usr/bin/chromium-browser', '/usr/bin/chromium', '/snap/bin/chromium'];
140
+
141
+ for (const path of armCandidates) {
116
142
  try {
117
- await execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true");
143
+ await access(path, fs.constants.X_OK);
144
+ result.path = path;
145
+ result.system = 'Linux';
146
+ return result;
118
147
  } catch { }
148
+ }
149
+
150
+ if (!isDocker) {
119
151
  try {
120
- await execPromise("sudo apt-get install -y chromium || true");
121
- } catch { }
152
+ await execPromise('sudo apt update -y');
153
+ await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg');
154
+ await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2');
155
+ } catch (error) {
156
+ if (this.logError) this.emit('error', `Install package for Linux ARM error: ${error}`);
157
+ }
122
158
  }
123
159
 
124
- // Retry after installation
125
- for (const p of armCandidates) {
160
+ for (const path of armCandidates) {
126
161
  try {
127
- await access(p, fs.constants.X_OK);
128
- return p;
162
+ await access(path, fs.constants.X_OK);
163
+ result.path = path;
164
+ result.system = 'Linux';
165
+ return result;
129
166
  } catch { }
130
167
  }
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 { }
168
+ return result;
148
169
  }
149
170
 
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
171
+ /* ===================== Linux x64 ===================== */
163
172
  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
-
173
+ const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
172
174
  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;
175
+ const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome');
176
+ if (stdout.trim()) {
177
+ result.path = stdout.trim();
178
+ return result;
179
+ }
176
180
  } catch { }
177
181
 
178
- for (const p of linuxCandidates) {
182
+ for (const path of linuxCandidates) {
179
183
  try {
180
- await access(p, fs.constants.X_OK);
181
- return p;
184
+ await access(path, fs.constants.X_OK);
185
+ result.path = path;
186
+ result.system = 'Linux';
187
+ return result;
182
188
  } catch { }
183
189
  }
184
190
 
185
- // Docker: try installing chromium inside container (if allowed)
186
191
  if (isDocker) {
187
192
  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 { }
193
+ await execPromise('apt update -y && apt install -y chromium');
194
+ await access('/usr/bin/chromium', fs.constants.X_OK);
195
+ result.path = '/usr/bin/chromium';
196
+ result.system = 'Linux Docker';
197
+ return result;
198
+ } catch (error) {
199
+ if (this.logError) this.emit('error', `Install package for Linux Docker error: ${error}`);
200
+ }
194
201
  }
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 { }
206
- }
207
-
208
- return null;
209
202
  }
210
203
 
211
- return null;
204
+ return result;
212
205
  } catch (err) {
213
- if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
214
- return null;
206
+ if (this.logError) this.emit('error', `Chromium detection error: ${err.message}`);
207
+ return { path: null, arch: 'unknown' };
215
208
  }
216
209
  }
217
210
 
@@ -280,7 +273,6 @@ class Functions extends EventEmitter {
280
273
 
281
274
  return { min, max };
282
275
  }
283
-
284
276
  }
285
277
 
286
278
  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,36 @@ 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 for ${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
 
248
250
  // Verify Chromium executable
249
251
  try {
250
252
  const { stdout } = await execPromise(`"${chromiumPath}" --version`);
251
- if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
253
+ if (!this.logDebug) this.emit('debug', `Chromium for ${system} (${arch}) detected: ${stdout.trim()}`);
252
254
  } catch (error) {
253
- accountInfo.Info = `Chromium found at ${chromiumPath}, but cannot be executed: ${error.message}`;
255
+ accountInfo.Info = `Chromium for ${system} (${arch}) found at ${chromiumPath}, but cannot be executed: ${error.message}`;
254
256
  return accountInfo;
255
257
  }
256
258
 
@@ -282,22 +284,22 @@ class MelCloudHome extends EventEmitter {
282
284
  await client.send('Network.enable')
283
285
  client.on('Network.webSocketCreated', ({ url }) => {
284
286
  try {
285
- if (url.startsWith(`${ApiUrlsHome.WebSocketURL}`)) {
287
+ if (url.startsWith(`${ApiUrls.Home.WebSocket}`)) {
286
288
  const params = new URL(url).searchParams;
287
289
  const hash = params.get('hash');
288
290
  if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
289
291
 
290
- //web socket connection
292
+ // Web socket connection
291
293
  if (!this.connecting && !this.socketConnected) {
292
294
  this.connecting = true;
293
295
 
294
296
  try {
295
297
  const headers = {
296
- 'Origin': ApiUrlsHome.BaseURL,
298
+ 'Origin': ApiUrls.Home.Base,
297
299
  'Pragma': 'no-cache',
298
300
  'Cache-Control': 'no-cache'
299
301
  };
300
- const webSocket = new WebSocket(`${ApiUrlsHome.WebSocketURL}${hash}`, { headers: headers })
302
+ const webSocket = new WebSocket(`${ApiUrls.Home.WebSocket}${hash}`, { headers: headers })
301
303
  .on('error', (error) => {
302
304
  if (this.logError) this.emit('error', `Web socket error: ${error}`);
303
305
  try {
@@ -326,7 +328,7 @@ class MelCloudHome extends EventEmitter {
326
328
  })
327
329
  .on('message', (message) => {
328
330
  const parsedMessage = JSON.parse(message);
329
- if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
331
+ if (!this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
330
332
  if (parsedMessage.message === 'Forbidden') return;
331
333
 
332
334
  this.emit('webSocket', parsedMessage);
@@ -343,9 +345,9 @@ class MelCloudHome extends EventEmitter {
343
345
  });
344
346
 
345
347
  try {
346
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
348
+ await page.goto(ApiUrls.Home.Base, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
347
349
  } catch (error) {
348
- accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
350
+ accountInfo.Info = `Navigation to ${ApiUrls.Home.Base} failed: ${error.message}`;
349
351
  return accountInfo;
350
352
  }
351
353
 
@@ -407,7 +409,7 @@ class MelCloudHome extends EventEmitter {
407
409
  'Accept-Language': LanguageLocaleMap[this.language],
408
410
  'Cookie': cookies,
409
411
  'Priority': 'u=3, i',
410
- 'Referer': ApiUrlsHome.Dashboard,
412
+ 'Referer': ApiUrls.Home.Dashboard,
411
413
  'Sec-Fetch-Dest': 'empty',
412
414
  'Sec-Fetch-Mode': 'cors',
413
415
  'Sec-Fetch-Site': 'same-origin',
@@ -416,7 +418,7 @@ class MelCloudHome extends EventEmitter {
416
418
  };
417
419
 
418
420
  this.client = axios.create({
419
- baseURL: ApiUrlsHome.BaseURL,
421
+ baseURL: ApiUrls.Home.Base,
420
422
  timeout: 30000,
421
423
  headers: headers
422
424
  })