homebridge-melcloud-control 4.0.0-beta.541 → 4.0.0-beta.542
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 +43 -56
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.542",
|
|
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
|
@@ -307,18 +307,17 @@ class MelCloud extends EventEmitter {
|
|
|
307
307
|
async connectToMelCloudHome() {
|
|
308
308
|
if (this.logDebug) this.emit('debug', 'Connecting to MELCloud Home');
|
|
309
309
|
const GLOBAL_TIMEOUT = 90000;
|
|
310
|
-
let browser;
|
|
311
|
-
|
|
312
|
-
const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false };
|
|
313
310
|
|
|
311
|
+
let browser;
|
|
314
312
|
try {
|
|
313
|
+
const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false };
|
|
315
314
|
const chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
316
315
|
if (!chromiumPath) {
|
|
317
|
-
accountInfo.Info = 'Chromium not found on
|
|
316
|
+
accountInfo.Info = 'Chromium not found on Your device, please install it manually and try again';
|
|
318
317
|
return accountInfo;
|
|
319
318
|
}
|
|
320
319
|
|
|
321
|
-
//
|
|
320
|
+
// Verify executable works
|
|
322
321
|
try {
|
|
323
322
|
const { stdout } = await execPromise(`"${chromiumPath}" --version`);
|
|
324
323
|
this.emit('warn', `Chromium detected: ${stdout.trim()}`);
|
|
@@ -328,97 +327,86 @@ class MelCloud extends EventEmitter {
|
|
|
328
327
|
return accountInfo;
|
|
329
328
|
}
|
|
330
329
|
|
|
331
|
-
this.emit('warn',
|
|
330
|
+
this.emit('warn', `Launching Chromium...`);
|
|
332
331
|
browser = await puppeteer.launch({
|
|
333
|
-
headless:
|
|
332
|
+
headless: 'shell',
|
|
334
333
|
executablePath: chromiumPath,
|
|
335
334
|
timeout: GLOBAL_TIMEOUT,
|
|
336
335
|
args: [
|
|
337
336
|
'--no-sandbox',
|
|
338
337
|
'--disable-setuid-sandbox',
|
|
339
338
|
'--disable-dev-shm-usage',
|
|
339
|
+
'--single-process',
|
|
340
340
|
'--disable-gpu',
|
|
341
|
-
'--no-zygote'
|
|
342
|
-
|
|
343
|
-
'--disable-software-rasterizer',
|
|
344
|
-
],
|
|
341
|
+
'--no-zygote'
|
|
342
|
+
]
|
|
345
343
|
});
|
|
346
344
|
|
|
347
345
|
const page = await browser.newPage();
|
|
348
346
|
page.setDefaultTimeout(GLOBAL_TIMEOUT);
|
|
349
347
|
page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
|
|
350
348
|
|
|
351
|
-
// Handle page errors
|
|
352
349
|
page.on('error', err => this.emit('error', `Page crashed: ${err.message}`));
|
|
353
350
|
page.on('pageerror', err => this.emit('error', `Browser error: ${err.message}`));
|
|
354
351
|
browser.on('disconnected', () => this.emit('debug', 'Browser disconnected'));
|
|
355
352
|
|
|
356
|
-
|
|
357
|
-
this.emit('warn', 'Navigating to MELCloud...');
|
|
353
|
+
this.emit('warn', `Navigating to MELCloud...`);
|
|
358
354
|
try {
|
|
359
|
-
await page.goto(ApiUrlsHome.BaseURL, {
|
|
360
|
-
waitUntil: 'networkidle2',
|
|
361
|
-
timeout: GLOBAL_TIMEOUT,
|
|
362
|
-
});
|
|
355
|
+
await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
|
|
363
356
|
} catch (error) {
|
|
364
357
|
accountInfo.Info = `Navigation to ${ApiUrlsHome.BaseURL} failed: ${error.message}`;
|
|
365
358
|
return accountInfo;
|
|
366
359
|
}
|
|
367
360
|
|
|
368
|
-
|
|
361
|
+
// Wait extra to ensure UI is rendered
|
|
362
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
369
363
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
364
|
+
this.emit('warn', `Looking for login button...`);
|
|
365
|
+
let loginBtn;
|
|
366
|
+
try {
|
|
367
|
+
loginBtn = await page.waitForFunction(() => {
|
|
368
|
+
const btns = Array.from(document.querySelectorAll('button.btn--blue'));
|
|
369
|
+
return btns.find(b => ['Zaloguj', 'Sign In', 'Login'].includes(b.textContent.trim()));
|
|
370
|
+
}, { timeout: GLOBAL_TIMEOUT / 6 });
|
|
371
|
+
} catch {
|
|
372
|
+
accountInfo.Info = 'Login button not found';
|
|
377
373
|
return accountInfo;
|
|
378
374
|
}
|
|
379
375
|
|
|
380
|
-
|
|
381
|
-
await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: GLOBAL_TIMEOUT /
|
|
382
|
-
|
|
383
|
-
// Find and fill form
|
|
384
|
-
this.emit('warn', 'Looking for credentials form...');
|
|
385
|
-
const usernameInput = await page.waitForSelector('input[name="username"]', { timeout: GLOBAL_TIMEOUT / 6 });
|
|
386
|
-
const passwordInput = await page.waitForSelector('input[name="password"]', { timeout: GLOBAL_TIMEOUT / 6 });
|
|
376
|
+
this.emit('warn', `Found login button ${loginBtn}`);
|
|
377
|
+
await Promise.race([Promise.all([loginBtn.click(), page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT / 4 })]), new Promise(r => setTimeout(r, GLOBAL_TIMEOUT / 3))]);
|
|
387
378
|
|
|
379
|
+
this.emit('warn', `Looking for credentials form...`);
|
|
380
|
+
const usernameInput = await page.$('input[name="username"]');
|
|
381
|
+
const passwordInput = await page.$('input[name="password"]');
|
|
388
382
|
if (!usernameInput || !passwordInput) {
|
|
389
383
|
accountInfo.Info = 'Username or password input not found';
|
|
390
384
|
return accountInfo;
|
|
391
385
|
}
|
|
392
386
|
|
|
393
|
-
this.emit('warn',
|
|
394
|
-
await
|
|
395
|
-
await
|
|
387
|
+
this.emit('warn', `Type credentials data..`);
|
|
388
|
+
await page.type('input[name="username"]', this.user, { delay: 50 });
|
|
389
|
+
await page.type('input[name="password"]', this.passwd, { delay: 50 });
|
|
396
390
|
|
|
397
|
-
|
|
398
|
-
this.emit('warn', 'Submitting login form...');
|
|
391
|
+
this.emit('warn', `Looking for submit button...`);
|
|
399
392
|
const submitButton = await page.$('input[type="submit"], button[type="submit"]');
|
|
400
393
|
if (!submitButton) {
|
|
401
394
|
accountInfo.Info = 'Submit button not found';
|
|
402
395
|
return accountInfo;
|
|
403
396
|
}
|
|
404
397
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: GLOBAL_TIMEOUT / 2 }),
|
|
408
|
-
]);
|
|
409
|
-
|
|
410
|
-
await page.waitForTimeout(2000);
|
|
398
|
+
this.emit('warn', `Found submit button ${submitButton}`);
|
|
399
|
+
await Promise.race([Promise.all([submitButton.click(), page.waitForNavigation({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT / 4 })]), new Promise(r => setTimeout(r, GLOBAL_TIMEOUT / 3))]);
|
|
411
400
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const deadline = Date.now() + GLOBAL_TIMEOUT / 2;
|
|
401
|
+
this.emit('warn', `Looking for cookies...`);
|
|
402
|
+
// Extract cookies
|
|
415
403
|
let c1 = null, c2 = null;
|
|
416
|
-
|
|
417
|
-
while (Date.now()
|
|
418
|
-
const cookies = await page.cookies();
|
|
404
|
+
const start = Date.now();
|
|
405
|
+
while ((!c1 || !c2) && Date.now() - start < GLOBAL_TIMEOUT / 2) {
|
|
406
|
+
const cookies = await page.browserContext().cookies();
|
|
419
407
|
c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
|
|
420
408
|
c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
|
|
421
|
-
if (!c1 || !c2) await
|
|
409
|
+
if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
|
|
422
410
|
}
|
|
423
411
|
|
|
424
412
|
if (!c1 || !c2) {
|
|
@@ -426,20 +414,20 @@ class MelCloud extends EventEmitter {
|
|
|
426
414
|
return accountInfo;
|
|
427
415
|
}
|
|
428
416
|
|
|
417
|
+
this.emit('warn', `Found cookies`);
|
|
429
418
|
const contextKey = [
|
|
430
419
|
'__Secure-monitorandcontrol=chunks-2',
|
|
431
420
|
`__Secure-monitorandcontrolC1=${c1}`,
|
|
432
|
-
`__Secure-monitorandcontrolC2=${c2}
|
|
421
|
+
`__Secure-monitorandcontrolC2=${c2}`
|
|
433
422
|
].join('; ');
|
|
423
|
+
this.contextKey = contextKey;
|
|
434
424
|
|
|
435
425
|
accountInfo.State = true;
|
|
436
426
|
accountInfo.Info = 'Connect to MELCloud Home Success';
|
|
437
427
|
accountInfo.ContextKey = contextKey;
|
|
438
|
-
this.contextKey = contextKey;
|
|
439
|
-
|
|
440
428
|
await this.functions.saveData(this.accountFile, accountInfo);
|
|
441
|
-
return accountInfo;
|
|
442
429
|
|
|
430
|
+
return accountInfo;
|
|
443
431
|
} catch (error) {
|
|
444
432
|
throw new Error(`Connect error: ${error.message}`);
|
|
445
433
|
} finally {
|
|
@@ -450,7 +438,6 @@ class MelCloud extends EventEmitter {
|
|
|
450
438
|
}
|
|
451
439
|
}
|
|
452
440
|
|
|
453
|
-
|
|
454
441
|
async checkDevicesList() {
|
|
455
442
|
const TIMEOUT_MS = 30000; // 30 seconds timeout
|
|
456
443
|
try {
|