homebridge-melcloud-control 4.0.0-beta.430 → 4.0.0-beta.432
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/functions.js +32 -37
- package/src/melcloud.js +21 -32
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.432",
|
|
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/functions.js
CHANGED
|
@@ -51,89 +51,84 @@ class Functions {
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
async ensureChromiumInstalled(logInfo, logWarn, logError) {
|
|
54
|
-
let chromiumPath = '/usr/bin/chromium-browser';
|
|
54
|
+
let chromiumPath = '/usr/bin/chromium-browser';
|
|
55
55
|
|
|
56
56
|
try {
|
|
57
57
|
// --- Detect platform ---
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
logInfo(`Detected architecture: ${arch}`);
|
|
58
|
+
const arch = (await new Promise((res, rej) => require('child_process').exec('uname -m', (e, stdout) => e ? rej(e) : res(stdout)))).trim();
|
|
59
|
+
logInfo && logInfo(`Detected architecture: ${arch}`);
|
|
61
60
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
logInfo(`Detected OS: ${osName}`);
|
|
61
|
+
const osName = (await new Promise((res, rej) => require('child_process').exec('uname -s', (e, stdout) => e ? rej(e) : res(stdout)))).trim();
|
|
62
|
+
logInfo && logInfo(`Detected OS: ${osName}`);
|
|
65
63
|
|
|
66
64
|
// --- macOS handling ---
|
|
67
65
|
if (osName === 'Darwin') {
|
|
68
|
-
logInfo('Running on macOS — using Puppeteer bundled Chromium');
|
|
69
|
-
chromiumPath = puppeteer.executablePath();
|
|
70
|
-
logInfo(`Chromium path: ${chromiumPath}`);
|
|
66
|
+
logInfo && logInfo('Running on macOS — using Puppeteer bundled Chromium');
|
|
67
|
+
chromiumPath = require('puppeteer-core').executablePath();
|
|
68
|
+
logInfo && logInfo(`Chromium path: ${chromiumPath}`);
|
|
71
69
|
return chromiumPath;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
// --- Linux handling ---
|
|
75
|
-
// Detect OS release if available
|
|
76
73
|
let linuxDistro = 'unknown';
|
|
77
74
|
try {
|
|
78
|
-
|
|
79
|
-
linuxDistro = osRelease.split('\n')[0] || 'unknown';
|
|
75
|
+
linuxDistro = (await new Promise((res, rej) => require('child_process').exec('cat /etc/os-release', (e, stdout) => e ? rej(e) : res(stdout)))).split('\n')[0] || 'unknown';
|
|
80
76
|
} catch {
|
|
81
|
-
logWarn('/etc/os-release not found, skipping OS detection.');
|
|
77
|
+
logWarn && logWarn('/etc/os-release not found, skipping OS detection.');
|
|
82
78
|
}
|
|
83
|
-
logInfo(`Linux distro: ${linuxDistro}`);
|
|
79
|
+
logInfo && logInfo(`Linux distro: ${linuxDistro}`);
|
|
84
80
|
|
|
85
81
|
// --- Check existing Chromium ---
|
|
86
|
-
const
|
|
87
|
-
if (chromiumCheck
|
|
88
|
-
chromiumPath = chromiumCheck
|
|
89
|
-
logInfo(`Found system Chromium: ${chromiumPath}`);
|
|
82
|
+
const chromiumCheck = (await new Promise((res) => require('child_process').exec('which chromium || which chromium-browser || true', (e, stdout) => res(stdout)))).trim();
|
|
83
|
+
if (chromiumCheck) {
|
|
84
|
+
chromiumPath = chromiumCheck;
|
|
85
|
+
logInfo && logInfo(`Found system Chromium: ${chromiumPath}`);
|
|
90
86
|
return chromiumPath;
|
|
91
87
|
}
|
|
92
88
|
|
|
93
|
-
logWarn('Chromium not found. Attempting installation...');
|
|
89
|
+
logWarn && logWarn('Chromium not found. Attempting installation...');
|
|
94
90
|
|
|
95
|
-
// --- Try apt-get
|
|
91
|
+
// --- Try apt-get ---
|
|
96
92
|
try {
|
|
97
|
-
await
|
|
93
|
+
await new Promise((res, rej) => require('child_process').exec('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg', (e) => e ? rej(e) : res()));
|
|
98
94
|
chromiumPath = '/usr/bin/chromium-browser';
|
|
99
|
-
logInfo('Chromium installed successfully via apt-get.');
|
|
95
|
+
logInfo && logInfo('Chromium installed successfully via apt-get.');
|
|
100
96
|
return chromiumPath;
|
|
101
|
-
} catch
|
|
102
|
-
logWarn('apt-get install failed. Trying apk
|
|
97
|
+
} catch {
|
|
98
|
+
logWarn && logWarn('apt-get install failed. Trying apk or yum...');
|
|
103
99
|
}
|
|
104
100
|
|
|
105
101
|
// --- Try Alpine APK ---
|
|
106
102
|
try {
|
|
107
|
-
await
|
|
103
|
+
await new Promise((res, rej) => require('child_process').exec('sudo apk add --no-cache chromium ffmpeg', (e) => e ? rej(e) : res()));
|
|
108
104
|
chromiumPath = '/usr/bin/chromium-browser';
|
|
109
|
-
logInfo('Chromium installed successfully via apk.');
|
|
105
|
+
logInfo && logInfo('Chromium installed successfully via apk.');
|
|
110
106
|
return chromiumPath;
|
|
111
|
-
} catch
|
|
112
|
-
logWarn('apk install failed. Trying yum
|
|
107
|
+
} catch {
|
|
108
|
+
logWarn && logWarn('apk install failed. Trying yum...');
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
// --- Try YUM ---
|
|
116
112
|
try {
|
|
117
|
-
await
|
|
113
|
+
await new Promise((res, rej) => require('child_process').exec('sudo yum install -y chromium chromium-codecs-ffmpeg', (e) => e ? rej(e) : res()));
|
|
118
114
|
chromiumPath = '/usr/bin/chromium-browser';
|
|
119
|
-
logInfo('Chromium installed successfully via yum.');
|
|
115
|
+
logInfo && logInfo('Chromium installed successfully via yum.');
|
|
120
116
|
return chromiumPath;
|
|
121
|
-
} catch
|
|
122
|
-
logWarn('yum install failed. Falling back to Puppeteer bundled Chromium.');
|
|
117
|
+
} catch {
|
|
118
|
+
logWarn && logWarn('yum install failed. Falling back to Puppeteer bundled Chromium.');
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
// --- Fallback: Puppeteer Chromium ---
|
|
126
|
-
chromiumPath = puppeteer.executablePath();
|
|
127
|
-
logWarn(`Using bundled Puppeteer Chromium at ${chromiumPath}`);
|
|
122
|
+
chromiumPath = require('puppeteer-core').executablePath();
|
|
123
|
+
logWarn && logWarn(`Using bundled Puppeteer Chromium at ${chromiumPath}`);
|
|
128
124
|
return chromiumPath;
|
|
129
125
|
|
|
130
126
|
} catch (err) {
|
|
131
|
-
logError(`Chromium detection/install error: ${err.message}`);
|
|
127
|
+
logError && logError(`Chromium detection/install error: ${err.message}`);
|
|
132
128
|
throw err;
|
|
133
129
|
}
|
|
134
130
|
}
|
|
135
131
|
|
|
136
|
-
|
|
137
132
|
async isRunningInDocker() {
|
|
138
133
|
try {
|
|
139
134
|
if (fs.existsSync('/.dockerenv')) return true;
|
package/src/melcloud.js
CHANGED
|
@@ -322,19 +322,18 @@ class MelCloud extends EventEmitter {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
async connectToMelCloudHome(refresh = false) {
|
|
326
|
-
if (
|
|
325
|
+
async connectToMelCloudHome(user, passwd, functions, ApiUrlsHome, accountFile, logDebug, logWarn, logError, logSuccess, refresh = false) {
|
|
326
|
+
if (logDebug) logDebug('Connecting to MELCloud Home');
|
|
327
327
|
let browser;
|
|
328
328
|
|
|
329
329
|
try {
|
|
330
|
-
// --- System Detection & Chromium install ---
|
|
331
330
|
const chromiumPath = await this.functions.ensureChromiumInstalled(
|
|
332
|
-
msg =>
|
|
333
|
-
msg =>
|
|
334
|
-
msg =>
|
|
331
|
+
msg => logDebug && logDebug(msg),
|
|
332
|
+
msg => logWarn && logWarn(msg),
|
|
333
|
+
msg => logError && logError(msg)
|
|
335
334
|
);
|
|
336
335
|
|
|
337
|
-
|
|
336
|
+
browser = await require('puppeteer-core').launch({
|
|
338
337
|
headless: true,
|
|
339
338
|
executablePath: chromiumPath,
|
|
340
339
|
args: [
|
|
@@ -344,24 +343,20 @@ class MelCloud extends EventEmitter {
|
|
|
344
343
|
'--single-process',
|
|
345
344
|
'--no-zygote'
|
|
346
345
|
]
|
|
347
|
-
};
|
|
346
|
+
});
|
|
348
347
|
|
|
349
|
-
browser = await puppeteer.launch(launchOptions);
|
|
350
348
|
const page = await browser.newPage();
|
|
351
349
|
|
|
352
|
-
|
|
353
|
-
page.on('
|
|
354
|
-
page.on('
|
|
355
|
-
|
|
356
|
-
browser.on('disconnected', () => this.emit('debug', 'Browser disconnected unexpectedly'));
|
|
350
|
+
page.on('error', err => logError && logError(`Page crashed: ${err.message}`));
|
|
351
|
+
page.on('pageerror', err => logError && logError(`Browser error: ${err.message}`));
|
|
352
|
+
page.on('close', () => logDebug && logDebug('Page was closed unexpectedly'));
|
|
353
|
+
browser.on('disconnected', () => logDebug && logDebug('Browser disconnected unexpectedly'));
|
|
357
354
|
|
|
358
355
|
page.setDefaultTimeout(30000);
|
|
359
356
|
page.setDefaultNavigationTimeout(30000);
|
|
360
357
|
|
|
361
|
-
// --- Go to login page ---
|
|
362
358
|
await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'] });
|
|
363
359
|
|
|
364
|
-
// --- Login flow ---
|
|
365
360
|
const buttons = await page.$$('button.btn--blue');
|
|
366
361
|
let loginBtn = null;
|
|
367
362
|
for (const btn of buttons) {
|
|
@@ -372,7 +367,7 @@ class MelCloud extends EventEmitter {
|
|
|
372
367
|
}
|
|
373
368
|
}
|
|
374
369
|
if (!loginBtn) {
|
|
375
|
-
|
|
370
|
+
logWarn && logWarn('Login button not found');
|
|
376
371
|
return null;
|
|
377
372
|
}
|
|
378
373
|
|
|
@@ -384,20 +379,19 @@ class MelCloud extends EventEmitter {
|
|
|
384
379
|
new Promise(r => setTimeout(r, 12000))
|
|
385
380
|
]);
|
|
386
381
|
|
|
387
|
-
// --- Credentials ---
|
|
388
382
|
const usernameInput = await page.$('input[name="username"]');
|
|
389
383
|
const passwordInput = await page.$('input[name="password"]');
|
|
390
384
|
if (!usernameInput || !passwordInput) {
|
|
391
|
-
|
|
385
|
+
logWarn && logWarn('Username or password input not found');
|
|
392
386
|
return null;
|
|
393
387
|
}
|
|
394
388
|
|
|
395
|
-
await page.type('input[name="username"]',
|
|
396
|
-
await page.type('input[name="password"]',
|
|
389
|
+
await page.type('input[name="username"]', user, { delay: 50 });
|
|
390
|
+
await page.type('input[name="password"]', passwd, { delay: 50 });
|
|
397
391
|
|
|
398
392
|
const submitButton = await page.$('input[type="submit"], button[type="submit"]');
|
|
399
393
|
if (!submitButton) {
|
|
400
|
-
|
|
394
|
+
logWarn && logWarn('Submit button not found on login form');
|
|
401
395
|
return null;
|
|
402
396
|
}
|
|
403
397
|
|
|
@@ -409,7 +403,6 @@ class MelCloud extends EventEmitter {
|
|
|
409
403
|
new Promise(r => setTimeout(r, 15000))
|
|
410
404
|
]);
|
|
411
405
|
|
|
412
|
-
// --- Cookie extraction ---
|
|
413
406
|
let c1 = null, c2 = null;
|
|
414
407
|
const start = Date.now();
|
|
415
408
|
while ((!c1 || !c2) && Date.now() - start < 20000) {
|
|
@@ -420,7 +413,7 @@ class MelCloud extends EventEmitter {
|
|
|
420
413
|
}
|
|
421
414
|
|
|
422
415
|
if (!c1 || !c2) {
|
|
423
|
-
|
|
416
|
+
logWarn && logWarn('Cookies C1/C2 missing after login');
|
|
424
417
|
return null;
|
|
425
418
|
}
|
|
426
419
|
|
|
@@ -431,21 +424,17 @@ class MelCloud extends EventEmitter {
|
|
|
431
424
|
].join('; ');
|
|
432
425
|
|
|
433
426
|
const accountInfo = { ContextKey: contextKey, UseFahrenheit: false };
|
|
434
|
-
|
|
435
|
-
await this.functions.saveData(this.accountFile, accountInfo);
|
|
427
|
+
await functions.saveData(accountFile, accountInfo);
|
|
436
428
|
|
|
437
|
-
if (!refresh)
|
|
429
|
+
if (!refresh && logSuccess) logSuccess('Connect to MELCloud Home Success');
|
|
438
430
|
return accountInfo;
|
|
439
431
|
|
|
440
432
|
} catch (error) {
|
|
441
433
|
throw new Error(`Connect error: ${error.message}`);
|
|
442
434
|
} finally {
|
|
443
435
|
if (browser) {
|
|
444
|
-
try {
|
|
445
|
-
|
|
446
|
-
} catch (closeErr) {
|
|
447
|
-
this.emit('error', `Failed to close Puppeteer browser: ${closeErr.message}`);
|
|
448
|
-
}
|
|
436
|
+
try { await browser.close(); }
|
|
437
|
+
catch (closeErr) { logError && logError(`Failed to close Puppeteer browser: ${closeErr.message}`); }
|
|
449
438
|
}
|
|
450
439
|
}
|
|
451
440
|
}
|