homebridge-melcloud-control 4.0.0-beta.507 → 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 +40 -40
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.507",
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,11 +325,11 @@ 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
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',
@@ -339,52 +340,47 @@ class MelCloud extends EventEmitter {
339
340
  ]
340
341
  });
341
342
 
342
- // Wait for Puppeteer target to be ready (browser internal page)
343
- await new Promise(r => setTimeout(r, 1000));
344
-
345
- // Defensive check for main frame availability
346
- this.emit('warn', `Test 2`);
347
- const pages = await browser.pages();
348
- let page = pages[0];
349
- if (!page) {
350
- if (this.logWarn) this.emit('warn', 'No initial page found, creating a new one');
351
- page = await browser.newPage();
352
- await new Promise(r => setTimeout(r, 1500));
353
- }
343
+ const [page] = await browser.pages();
344
+ page.setDefaultTimeout(GLOBAL_TIMEOUT);
345
+ page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
354
346
 
355
- this.emit('warn', `Test 3`);
356
- page.on('error', err => { if (this.logError) this.emit('error', `Page crashed: ${err.message}`); });
357
- page.on('pageerror', err => { if (this.logDebug) this.emit('error', `Browser error: ${err.message}`); });
358
- page.on('close', () => { if (this.logDebug) this.emit('debug', 'Page was closed unexpectedly'); });
359
- 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'));
360
350
 
361
- // Now safe to navigate
362
- this.emit('warn', `Test 4`);
351
+ this.emit('warn', `Navigating to MELCloud...`);
363
352
  try {
364
- await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 45000 });
353
+ await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
365
354
  } catch (error) {
366
355
  accountInfo.State = false;
367
356
  accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
368
357
  return accountInfo;
369
358
  }
370
- await new Promise(r => setTimeout(r, 4000));
371
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...`);
372
364
  let loginBtn;
373
365
  try {
374
- this.emit('warn', `Test 5`);
375
366
  loginBtn = await page.waitForFunction(() => {
376
367
  const btns = Array.from(document.querySelectorAll('button.btn--blue'));
377
368
  return btns.find(b => ['Zaloguj', 'Sign In', 'Login'].includes(b.textContent.trim()));
378
- }, { timeout: 5000 });
369
+ }, { timeout: GLOBAL_TIMEOUT / 6 });
379
370
  } catch {
380
371
  accountInfo.State = false;
381
- accountInfo.Info = 'Login button not found after 5s';
372
+ accountInfo.Info = 'Login button not found';
382
373
  return accountInfo;
383
374
  }
384
375
 
385
- 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
+ ]);
386
383
 
387
- this.emit('warn', `Test 6`);
388
384
  const usernameInput = await page.$('input[name="username"]');
389
385
  const passwordInput = await page.$('input[name="password"]');
390
386
  if (!usernameInput || !passwordInput) {
@@ -393,31 +389,34 @@ class MelCloud extends EventEmitter {
393
389
  return accountInfo;
394
390
  }
395
391
 
396
- this.emit('warn', `Test 7`);
397
392
  await page.type('input[name="username"]', this.user, { delay: 50 });
398
393
  await page.type('input[name="password"]', this.passwd, { delay: 50 });
399
394
 
400
- this.emit('warn', `Test 8`);
401
395
  const submitButton = await page.$('input[type="submit"], button[type="submit"]');
402
396
  if (!submitButton) {
403
397
  accountInfo.State = false;
404
- accountInfo.Info = 'Submit button not found on login form';
398
+ accountInfo.Info = 'Submit button not found';
405
399
  return accountInfo;
406
400
  }
407
401
 
408
- 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
+ ]);
409
409
 
410
- this.emit('warn', `Test 9`);
410
+ // Extract cookies
411
411
  let c1 = null, c2 = null;
412
412
  const start = Date.now();
413
- while ((!c1 || !c2) && Date.now() - start < 20000) {
413
+ while ((!c1 || !c2) && Date.now() - start < GLOBAL_TIMEOUT / 2) {
414
414
  const cookies = await page.browserContext().cookies();
415
415
  c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
416
416
  c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
417
417
  if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
418
418
  }
419
419
 
420
- this.emit('warn', `Test 10`);
421
420
  if (!c1 || !c2) {
422
421
  accountInfo.State = false;
423
422
  accountInfo.Info = 'Cookies C1/C2 missing';
@@ -435,21 +434,22 @@ class MelCloud extends EventEmitter {
435
434
  accountInfo.Info = 'Connect success';
436
435
  accountInfo.ContextKey = contextKey;
437
436
 
438
- this.emit('warn', `Test 11`);
439
437
  await this.functions.saveData(this.accountFile, accountInfo);
440
438
 
441
439
  if (!refresh) this.emit('success', 'Connect to MELCloud Home Success');
442
440
  return accountInfo;
441
+
443
442
  } catch (error) {
444
443
  throw new Error(`Connect error: ${error.message}`);
445
444
  } finally {
446
445
  if (browser) {
447
446
  try { await browser.close(); }
448
- 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}`); }
449
448
  }
450
449
  }
451
450
  }
452
451
 
452
+
453
453
  async checkDevicesList() {
454
454
  const TIMEOUT_MS = 30000; // 30 seconds timeout
455
455
  try {