homebridge-melcloud-control 4.4.1-beta.26 → 4.4.1-beta.27
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/melcloudhome.js +57 -23
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.4.1-beta.
|
|
4
|
+
"version": "4.4.1-beta.27",
|
|
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/melcloudhome.js
CHANGED
|
@@ -33,8 +33,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
33
33
|
this.socketConnected = false;
|
|
34
34
|
this.heartbeat = null;
|
|
35
35
|
|
|
36
|
-
this.cookies = [];
|
|
37
|
-
|
|
38
36
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
39
37
|
.on('warn', warn => this.emit('warn', warn))
|
|
40
38
|
.on('error', error => this.emit('error', error))
|
|
@@ -449,53 +447,93 @@ class MelCloudHome extends EventEmitter {
|
|
|
449
447
|
}
|
|
450
448
|
|
|
451
449
|
|
|
452
|
-
async axiosRequest(url, options, jar, maxRedirects =
|
|
450
|
+
async axiosRequest(url, options, jar, maxRedirects = 15) {
|
|
453
451
|
let currentUrl = url;
|
|
452
|
+
let previousUrl = null;
|
|
453
|
+
let method = options.method || 'GET';
|
|
454
|
+
let body = options.body;
|
|
454
455
|
|
|
455
456
|
for (let i = 0; i < maxRedirects; i++) {
|
|
457
|
+
const cookieHeader = jar.headerFor(currentUrl);
|
|
458
|
+
|
|
459
|
+
const headers = {
|
|
460
|
+
'User-Agent': options.headers?.['User-Agent'],
|
|
461
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
462
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
463
|
+
'Accept-Encoding': 'identity',
|
|
464
|
+
...(previousUrl ? { Referer: previousUrl } : {}),
|
|
465
|
+
...(cookieHeader ? { Cookie: cookieHeader } : {}),
|
|
466
|
+
...options.headers,
|
|
467
|
+
};
|
|
468
|
+
|
|
456
469
|
const response = await axios({
|
|
457
470
|
url: currentUrl,
|
|
458
|
-
method
|
|
459
|
-
data:
|
|
460
|
-
headers
|
|
461
|
-
...options.headers,
|
|
462
|
-
...(jar.headerFor(currentUrl) && { Cookie: jar.headerFor(currentUrl) }),
|
|
463
|
-
},
|
|
464
|
-
validateStatus: () => true,
|
|
471
|
+
method,
|
|
472
|
+
data: body,
|
|
473
|
+
headers,
|
|
465
474
|
maxRedirects: 0,
|
|
475
|
+
validateStatus: () => true,
|
|
466
476
|
responseType: 'text',
|
|
467
477
|
});
|
|
468
478
|
|
|
469
479
|
jar.addFromHeader(response.headers['set-cookie'], currentUrl);
|
|
470
480
|
|
|
481
|
+
/* ========= HTTP REDIRECT ========= */
|
|
471
482
|
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
472
483
|
const location = response.headers.location;
|
|
473
484
|
if (!location) break;
|
|
474
485
|
|
|
486
|
+
// SUCCESS: app redirect
|
|
475
487
|
if (location.startsWith('melcloudhome://')) {
|
|
476
488
|
return { url: location, data: response.data };
|
|
477
489
|
}
|
|
478
490
|
|
|
491
|
+
previousUrl = currentUrl;
|
|
479
492
|
currentUrl = location.startsWith('http')
|
|
480
493
|
? location
|
|
481
494
|
: new URL(location, currentUrl).toString();
|
|
482
495
|
|
|
483
|
-
|
|
484
|
-
|
|
496
|
+
method = 'GET';
|
|
497
|
+
body = undefined;
|
|
485
498
|
continue;
|
|
486
499
|
}
|
|
487
500
|
|
|
488
|
-
|
|
501
|
+
/* ========= META REFRESH ========= */
|
|
502
|
+
const metaMatch = response.data?.match(
|
|
503
|
+
/<meta[^>]+http-equiv=["']?refresh["']?[^>]+content=["']?\d+;\s*url=([^"'>]+)["']?/i
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
if (metaMatch) {
|
|
507
|
+
const redirectPath = metaMatch[1]
|
|
508
|
+
.replace(/&/g, '&')
|
|
509
|
+
.replace(///g, '/');
|
|
510
|
+
|
|
511
|
+
previousUrl = currentUrl;
|
|
512
|
+
currentUrl = redirectPath.startsWith('http')
|
|
513
|
+
? redirectPath
|
|
514
|
+
: new URL(redirectPath, currentUrl).toString();
|
|
515
|
+
|
|
516
|
+
method = 'GET';
|
|
517
|
+
body = undefined;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* ========= FINAL RESPONSE ========= */
|
|
522
|
+
return {
|
|
523
|
+
url: currentUrl,
|
|
524
|
+
data: response.data,
|
|
525
|
+
headers: response.headers,
|
|
526
|
+
};
|
|
489
527
|
}
|
|
490
528
|
|
|
491
|
-
throw new Error('
|
|
529
|
+
throw new Error('OAuth flow aborted: too many redirects');
|
|
492
530
|
}
|
|
493
531
|
|
|
532
|
+
|
|
494
533
|
async loginWithCredentials(email, password) {
|
|
495
534
|
const jar = new CookieJar();
|
|
496
535
|
|
|
497
|
-
const userAgent =
|
|
498
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Mobile Safari/604.1';
|
|
536
|
+
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Mobile Safari/604.1';
|
|
499
537
|
|
|
500
538
|
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
501
539
|
const codeChallenge = crypto
|
|
@@ -503,8 +541,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
503
541
|
.update(codeVerifier)
|
|
504
542
|
.digest('base64url');
|
|
505
543
|
|
|
506
|
-
const authUrl =
|
|
507
|
-
'https://auth.melcloudhome.com/connect/authorize?' +
|
|
544
|
+
const authUrl = 'https://auth.melcloudhome.com/connect/authorize?' +
|
|
508
545
|
new URLSearchParams({
|
|
509
546
|
client_id: 'homemobile',
|
|
510
547
|
redirect_uri: 'melcloudhome://',
|
|
@@ -519,9 +556,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
519
556
|
headers: { 'User-Agent': userAgent },
|
|
520
557
|
}, jar);
|
|
521
558
|
|
|
522
|
-
const csrfMatch =
|
|
523
|
-
loginPage.data.match(/name="(_csrf|__RequestVerificationToken)".*?value="([^"]+)"/);
|
|
524
|
-
|
|
559
|
+
const csrfMatch = loginPage.data.match(/name="(_csrf|__RequestVerificationToken)".*?value="([^"]+)"/);
|
|
525
560
|
if (!csrfMatch) throw new Error('CSRF token not found');
|
|
526
561
|
const csrfToken = csrfMatch[2];
|
|
527
562
|
|
|
@@ -550,8 +585,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
550
585
|
if (!code) throw new Error('Authorization code missing');
|
|
551
586
|
|
|
552
587
|
// STEP 3 – exchange token
|
|
553
|
-
const tokenResponse = await axios.post(
|
|
554
|
-
'https://auth.melcloudhome.com/connect/token',
|
|
588
|
+
const tokenResponse = await axios.post('https://auth.melcloudhome.com/connect/token',
|
|
555
589
|
new URLSearchParams({
|
|
556
590
|
grant_type: 'authorization_code',
|
|
557
591
|
client_id: 'homemobile',
|