homebridge-melcloud-control 4.4.1-beta.1 → 4.4.1-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.
@@ -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,26 +223,45 @@ class MelCloudHome extends EventEmitter {
225
223
  try {
226
224
  const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
227
225
 
228
- // Get Chromium path
226
+ // Get Chromium path from resolver
229
227
  let chromiumPath = await this.functions.ensureChromiumInstalled();
230
228
 
231
- // Fallback to Puppeteer's bundled Chromium
229
+ // Detect architecture again (cheap & explicit)
230
+ const { stdout: archOut } = await execPromise('uname -m');
231
+ const arch = archOut.trim();
232
+ const isARM =
233
+ arch.startsWith('arm') ||
234
+ arch.startsWith('aarch64') ||
235
+ arch.startsWith('aarch');
236
+
237
+ // Detect QNAP
238
+ const isQnap =
239
+ fs.existsSync('/etc/config/uLinux.conf') ||
240
+ fs.existsSync('/etc/config/qpkg.conf');
241
+
242
+ // Conditional Puppeteer fallback
232
243
  if (!chromiumPath) {
233
- try {
234
- const puppeteerPath = puppeteer.executablePath();
235
- if (puppeteerPath && puppeteerPath.length > 0) {
244
+ if (!isARM && !isQnap) {
245
+ try {
246
+ const puppeteerPath = puppeteer.executablePath();
247
+ if (!puppeteerPath) {
248
+ accountInfo.Info = 'Puppeteer returned empty Chromium path';
249
+ return accountInfo;
250
+ }
251
+
236
252
  chromiumPath = puppeteerPath;
237
253
  if (this.logDebug) this.emit('debug', `Using Puppeteer bundled Chromium at ${chromiumPath}`);
238
- } else {
239
- accountInfo.Info = `Puppeteer returned empty Chromium path`;
254
+ } catch (error) {
255
+ accountInfo.Info = `Failed to get Puppeteer Chromium path: ${error.message}`;
240
256
  return accountInfo;
241
257
  }
242
- } catch (error) {
243
- accountInfo.Info = `Failed to get Puppeteer Chromium path: ${error.message}`;
258
+ } else {
259
+ accountInfo.Info = 'Chromium not available for this platform. Install system Chromium.';
244
260
  return accountInfo;
245
261
  }
246
262
  }
247
263
 
264
+
248
265
  // Verify Chromium executable
249
266
  try {
250
267
  const { stdout } = await execPromise(`"${chromiumPath}" --version`);
@@ -282,7 +299,7 @@ class MelCloudHome extends EventEmitter {
282
299
  await client.send('Network.enable')
283
300
  client.on('Network.webSocketCreated', ({ url }) => {
284
301
  try {
285
- if (url.startsWith(`${ApiUrlsHome.WebSocketURL}`)) {
302
+ if (url.startsWith(`${ApiUrls.Home.WebSocket}`)) {
286
303
  const params = new URL(url).searchParams;
287
304
  const hash = params.get('hash');
288
305
  if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
@@ -293,11 +310,11 @@ class MelCloudHome extends EventEmitter {
293
310
 
294
311
  try {
295
312
  const headers = {
296
- 'Origin': ApiUrlsHome.BaseURL,
313
+ 'Origin': ApiUrls.Home.Base,
297
314
  'Pragma': 'no-cache',
298
315
  'Cache-Control': 'no-cache'
299
316
  };
300
- const webSocket = new WebSocket(`${ApiUrlsHome.WebSocketURL}${hash}`, { headers: headers })
317
+ const webSocket = new WebSocket(`${ApiUrls.Home.WebSocket}${hash}`, { headers: headers })
301
318
  .on('error', (error) => {
302
319
  if (this.logError) this.emit('error', `Web socket error: ${error}`);
303
320
  try {
@@ -326,7 +343,7 @@ class MelCloudHome extends EventEmitter {
326
343
  })
327
344
  .on('message', (message) => {
328
345
  const parsedMessage = JSON.parse(message);
329
- if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
346
+ if (!this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
330
347
  if (parsedMessage.message === 'Forbidden') return;
331
348
 
332
349
  this.emit('webSocket', parsedMessage);
@@ -343,9 +360,9 @@ class MelCloudHome extends EventEmitter {
343
360
  });
344
361
 
345
362
  try {
346
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
363
+ await page.goto(ApiUrls.Home.Base, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
347
364
  } catch (error) {
348
- accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
365
+ accountInfo.Info = `Navigation to ${ApiUrls.Home.Base} failed: ${error.message}`;
349
366
  return accountInfo;
350
367
  }
351
368
 
@@ -407,7 +424,7 @@ class MelCloudHome extends EventEmitter {
407
424
  'Accept-Language': LanguageLocaleMap[this.language],
408
425
  'Cookie': cookies,
409
426
  'Priority': 'u=3, i',
410
- 'Referer': ApiUrlsHome.Dashboard,
427
+ 'Referer': ApiUrls.Home.Dashboard,
411
428
  'Sec-Fetch-Dest': 'empty',
412
429
  'Sec-Fetch-Mode': 'cors',
413
430
  'Sec-Fetch-Site': 'same-origin',
@@ -416,7 +433,7 @@ class MelCloudHome extends EventEmitter {
416
433
  };
417
434
 
418
435
  this.client = axios.create({
419
- baseURL: ApiUrlsHome.BaseURL,
436
+ baseURL: ApiUrls.Home.Base,
420
437
  timeout: 30000,
421
438
  headers: headers
422
439
  })