homebridge-melcloud-control 4.0.0-beta.506 → 4.0.0-beta.508

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/melcloud.js +42 -41
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.0.0-beta.506",
4
+ "version": "4.0.0-beta.508",
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/melcloud.js CHANGED
@@ -303,20 +303,21 @@ class MelCloud extends EventEmitter {
303
303
 
304
304
  async connectToMelCloudHome(refresh = false) {
305
305
  if (this.logDebug) this.emit('debug', 'Connecting to MELCloud Home');
306
+ const GLOBAL_TIMEOUT = 45000;
306
307
 
307
308
  let browser;
308
309
  try {
309
- const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false }
310
+ const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false };
310
311
  const chromiumPath = await this.functions.ensureChromiumInstalled();
311
312
  if (!chromiumPath) {
312
313
  accountInfo.State = false;
313
- accountInfo.Info = 'Chromium not found on Your device, please install it manually and try again'
314
+ accountInfo.Info = 'Chromium not found on Your device, please install it manually and try again';
314
315
  return accountInfo;
315
316
  }
316
317
 
318
+ // Verify executable works
317
319
  try {
318
320
  const { stdout } = await execPromise(`"${chromiumPath}" --version`);
319
- this.emit('warn', `Test 0`);
320
321
  if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
321
322
  } catch (error) {
322
323
  accountInfo.State = false;
@@ -324,66 +325,62 @@ class MelCloud extends EventEmitter {
324
325
  return accountInfo;
325
326
  }
326
327
 
327
- this.emit('warn', `Test 1`);
328
+ this.emit('warn', `Launching Chromium...`);
328
329
  browser = await puppeteer.launch({
329
- headless: true,
330
+ headless: 'shell',
330
331
  executablePath: chromiumPath,
331
- timeout: 30000,
332
+ timeout: GLOBAL_TIMEOUT,
332
333
  args: [
333
334
  '--no-sandbox',
334
335
  '--disable-setuid-sandbox',
335
336
  '--disable-dev-shm-usage',
336
337
  '--single-process',
338
+ '--disable-gpu',
337
339
  '--no-zygote'
338
340
  ]
339
341
  });
340
342
 
341
- // Wait for Puppeteer target to be ready (browser internal page)
342
- await new Promise(r => setTimeout(r, 1000));
343
-
344
- // Defensive check for main frame availability
345
- this.emit('warn', `Test 2`);
346
- const pages = await browser.pages();
347
- let page = pages[0];
348
- if (!page) {
349
- if (this.logWarn) this.emit('warn', 'No initial page found, creating a new one');
350
- page = await browser.newPage();
351
- await new Promise(r => setTimeout(r, 1500));
352
- }
343
+ const [page] = await browser.pages();
344
+ page.setDefaultTimeout(GLOBAL_TIMEOUT);
345
+ page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
353
346
 
354
- this.emit('warn', `Test 3`);
355
- page.on('error', err => { if (this.logError) this.emit('error', `Page crashed: ${err.message}`); });
356
- page.on('pageerror', err => { if (this.logDebug) this.emit('error', `Browser error: ${err.message}`); });
357
- page.on('close', () => { if (this.logDebug) this.emit('debug', 'Page was closed unexpectedly'); });
358
- browser.on('disconnected', () => { if (this.logWarn) this.emit('debug', 'Browser disconnected unexpectedly'); });
347
+ page.on('error', err => this.emit('error', `Page crashed: ${err.message}`));
348
+ page.on('pageerror', err => this.emit('error', `Browser error: ${err.message}`));
349
+ browser.on('disconnected', () => this.emit('warn', 'Browser disconnected unexpectedly'));
359
350
 
360
- // Now safe to navigate
361
- this.emit('warn', `Test 4`);
351
+ this.emit('warn', `Navigating to MELCloud...`);
362
352
  try {
363
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 45000 });
353
+ await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
364
354
  } catch (error) {
365
355
  accountInfo.State = false;
366
356
  accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
367
357
  return accountInfo;
368
358
  }
369
- await new Promise(r => setTimeout(r, 4000));
370
359
 
360
+ // Wait extra to ensure UI is rendered
361
+ await new Promise(r => setTimeout(r, 3000));
362
+
363
+ this.emit('warn', `Looking for login button...`);
371
364
  let loginBtn;
372
365
  try {
373
- this.emit('warn', `Test 5`);
374
366
  loginBtn = await page.waitForFunction(() => {
375
367
  const btns = Array.from(document.querySelectorAll('button.btn--blue'));
376
368
  return btns.find(b => ['Zaloguj', 'Sign In', 'Login'].includes(b.textContent.trim()));
377
- }, { timeout: 5000 });
369
+ }, { timeout: GLOBAL_TIMEOUT / 6 });
378
370
  } catch {
379
371
  accountInfo.State = false;
380
- accountInfo.Info = 'Login button not found after 5s';
372
+ accountInfo.Info = 'Login button not found';
381
373
  return accountInfo;
382
374
  }
383
375
 
384
- await Promise.race([Promise.all([loginBtn.click(), page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 10000 })]), new Promise(r => setTimeout(r, 8000))]);
376
+ await Promise.race([
377
+ Promise.all([
378
+ loginBtn.click(),
379
+ page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT / 4 })
380
+ ]),
381
+ new Promise(r => setTimeout(r, GLOBAL_TIMEOUT / 3))
382
+ ]);
385
383
 
386
- this.emit('warn', `Test 6`);
387
384
  const usernameInput = await page.$('input[name="username"]');
388
385
  const passwordInput = await page.$('input[name="password"]');
389
386
  if (!usernameInput || !passwordInput) {
@@ -392,31 +389,34 @@ class MelCloud extends EventEmitter {
392
389
  return accountInfo;
393
390
  }
394
391
 
395
- this.emit('warn', `Test 7`);
396
392
  await page.type('input[name="username"]', this.user, { delay: 50 });
397
393
  await page.type('input[name="password"]', this.passwd, { delay: 50 });
398
394
 
399
- this.emit('warn', `Test 8`);
400
395
  const submitButton = await page.$('input[type="submit"], button[type="submit"]');
401
396
  if (!submitButton) {
402
397
  accountInfo.State = false;
403
- accountInfo.Info = 'Submit button not found on login form';
398
+ accountInfo.Info = 'Submit button not found';
404
399
  return accountInfo;
405
400
  }
406
401
 
407
- await Promise.race([Promise.all([submitButton.click(), page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 10000 })]), new Promise(r => setTimeout(r, 8000))]);
402
+ await Promise.race([
403
+ Promise.all([
404
+ submitButton.click(),
405
+ page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT / 4 })
406
+ ]),
407
+ new Promise(r => setTimeout(r, GLOBAL_TIMEOUT / 3))
408
+ ]);
408
409
 
409
- this.emit('warn', `Test 9`);
410
+ // Extract cookies
410
411
  let c1 = null, c2 = null;
411
412
  const start = Date.now();
412
- while ((!c1 || !c2) && Date.now() - start < 20000) {
413
+ while ((!c1 || !c2) && Date.now() - start < GLOBAL_TIMEOUT / 2) {
413
414
  const cookies = await page.browserContext().cookies();
414
415
  c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
415
416
  c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
416
417
  if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
417
418
  }
418
419
 
419
- this.emit('warn', `Test 10`);
420
420
  if (!c1 || !c2) {
421
421
  accountInfo.State = false;
422
422
  accountInfo.Info = 'Cookies C1/C2 missing';
@@ -434,21 +434,22 @@ class MelCloud extends EventEmitter {
434
434
  accountInfo.Info = 'Connect success';
435
435
  accountInfo.ContextKey = contextKey;
436
436
 
437
- this.emit('warn', `Test 11`);
438
437
  await this.functions.saveData(this.accountFile, accountInfo);
439
438
 
440
439
  if (!refresh) this.emit('success', 'Connect to MELCloud Home Success');
441
440
  return accountInfo;
441
+
442
442
  } catch (error) {
443
443
  throw new Error(`Connect error: ${error.message}`);
444
444
  } finally {
445
445
  if (browser) {
446
446
  try { await browser.close(); }
447
- catch (closeErr) { if (this.logError) this.emit('error', `Failed to close Puppeteer browser: ${closeErr.message}`); }
447
+ catch (closeErr) { this.emit('error', `Failed to close Puppeteer: ${closeErr.message}`); }
448
448
  }
449
449
  }
450
450
  }
451
451
 
452
+
452
453
  async checkDevicesList() {
453
454
  const TIMEOUT_MS = 30000; // 30 seconds timeout
454
455
  try {