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.
- package/package.json +1 -1
- 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.
|
|
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', `
|
|
328
|
+
this.emit('warn', `Launching Chromium...`);
|
|
328
329
|
browser = await puppeteer.launch({
|
|
329
330
|
headless: 'shell',
|
|
330
331
|
executablePath: chromiumPath,
|
|
331
|
-
timeout:
|
|
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
|
-
|
|
343
|
-
|
|
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('
|
|
356
|
-
page.on('
|
|
357
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
369
|
+
}, { timeout: GLOBAL_TIMEOUT / 6 });
|
|
379
370
|
} catch {
|
|
380
371
|
accountInfo.State = false;
|
|
381
|
-
accountInfo.Info = 'Login button not found
|
|
372
|
+
accountInfo.Info = 'Login button not found';
|
|
382
373
|
return accountInfo;
|
|
383
374
|
}
|
|
384
375
|
|
|
385
|
-
await Promise.race([
|
|
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
|
|
398
|
+
accountInfo.Info = 'Submit button not found';
|
|
405
399
|
return accountInfo;
|
|
406
400
|
}
|
|
407
401
|
|
|
408
|
-
await Promise.race([
|
|
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
|
-
|
|
410
|
+
// Extract cookies
|
|
411
411
|
let c1 = null, c2 = null;
|
|
412
412
|
const start = Date.now();
|
|
413
|
-
while ((!c1 || !c2) && Date.now() - start <
|
|
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) {
|
|
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 {
|