homebridge-melcloud-control 4.0.0-beta.431 → 4.0.0-beta.433
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 +93 -71
- package/src/melcloud.js +25 -22
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.433",
|
|
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
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import { exec } from 'child_process';
|
|
3
2
|
import { promises as fsPromises } from 'fs';
|
|
4
|
-
import { promisify } from 'util';
|
|
5
|
-
import puppeteer from 'puppeteer-core';
|
|
6
|
-
const execAsync = promisify(exec);
|
|
7
3
|
|
|
8
4
|
class Functions {
|
|
9
5
|
constructor() {
|
|
@@ -49,85 +45,111 @@ class Functions {
|
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
47
|
|
|
48
|
+
async ensureChromiumInstalled() {
|
|
49
|
+
let chromiumPath = '/usr/bin/chromium-browser';
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
try {
|
|
52
|
+
// --- Detect architecture ---
|
|
53
|
+
let arch = '';
|
|
56
54
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// --- macOS handling ---
|
|
65
|
-
if (osName === 'Darwin') {
|
|
66
|
-
logInfo && logInfo('Running on macOS — using Puppeteer bundled Chromium');
|
|
67
|
-
chromiumPath = require('puppeteer-core').executablePath();
|
|
68
|
-
logInfo && logInfo(`Chromium path: ${chromiumPath}`);
|
|
69
|
-
return chromiumPath;
|
|
70
|
-
}
|
|
55
|
+
arch = (await new Promise((res, rej) =>
|
|
56
|
+
require('child_process').exec('uname -m', (e, stdout) => e ? rej(e) : res(stdout))
|
|
57
|
+
)).trim();
|
|
58
|
+
if (this.logDebug) this.emit('debug', `Detected architecture: ${arch}`);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (this.logWarn) this.emit('warn', `Failed to detect architecture: ${err.message}`);
|
|
61
|
+
}
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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}`);
|
|
86
|
-
return chromiumPath;
|
|
87
|
-
}
|
|
63
|
+
// --- Detect OS ---
|
|
64
|
+
let osName = '';
|
|
65
|
+
try {
|
|
66
|
+
osName = (await new Promise((res, rej) =>
|
|
67
|
+
require('child_process').exec('uname -s', (e, stdout) => e ? rej(e) : res(stdout))
|
|
68
|
+
)).trim();
|
|
69
|
+
if (this.logDebug) this.emit('debug', `Detected OS: ${osName}`);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (this.logWarn) this.emit('warn', `Failed to detect OS: ${err.message}`);
|
|
72
|
+
}
|
|
88
73
|
|
|
89
|
-
|
|
74
|
+
// --- macOS fallback ---
|
|
75
|
+
if (osName === 'Darwin') {
|
|
76
|
+
if (this.logDebug) this.emit('debug', 'Running on macOS — using Puppeteer bundled Chromium');
|
|
77
|
+
chromiumPath = require('puppeteer-core').executablePath();
|
|
78
|
+
if (this.logDebug) this.emit('debug', `Chromium path: ${chromiumPath}`);
|
|
79
|
+
return chromiumPath;
|
|
80
|
+
}
|
|
90
81
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
// --- Linux distro detection ---
|
|
83
|
+
let linuxDistro = 'unknown';
|
|
84
|
+
try {
|
|
85
|
+
linuxDistro = (await new Promise((res, rej) =>
|
|
86
|
+
require('child_process').exec('cat /etc/os-release', (e, stdout) => e ? rej(e) : res(stdout))
|
|
87
|
+
)).split('\n')[0] || 'unknown';
|
|
88
|
+
} catch {
|
|
89
|
+
if (this.logWarn) this.emit('warn', '/etc/os-release not found, skipping OS detection.');
|
|
90
|
+
}
|
|
91
|
+
if (this.logDebug) this.emit('debug', `Linux distro: ${linuxDistro}`);
|
|
92
|
+
|
|
93
|
+
// --- Check if Chromium exists ---
|
|
94
|
+
const chromiumCheck = (await new Promise((res) =>
|
|
95
|
+
require('child_process').exec('which chromium || which chromium-browser || true', (e, stdout) => res(stdout))
|
|
96
|
+
)).trim();
|
|
97
|
+
if (chromiumCheck) {
|
|
98
|
+
chromiumPath = chromiumCheck;
|
|
99
|
+
if (this.logDebug) this.emit('debug', `Found system Chromium: ${chromiumPath}`);
|
|
100
|
+
return chromiumPath;
|
|
101
|
+
}
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
await new Promise((res, rej) => require('child_process').exec('sudo apk add --no-cache chromium ffmpeg', (e) => e ? rej(e) : res()));
|
|
104
|
-
chromiumPath = '/usr/bin/chromium-browser';
|
|
105
|
-
logInfo && logInfo('Chromium installed successfully via apk.');
|
|
106
|
-
return chromiumPath;
|
|
107
|
-
} catch {
|
|
108
|
-
logWarn && logWarn('apk install failed. Trying yum...');
|
|
109
|
-
}
|
|
103
|
+
if (this.logWarn) this.emit('warn', 'Chromium not found. Attempting installation...');
|
|
110
104
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
// --- apt-get ---
|
|
106
|
+
try {
|
|
107
|
+
await new Promise((res, rej) =>
|
|
108
|
+
require('child_process').exec('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg', (e) => e ? rej(e) : res())
|
|
109
|
+
);
|
|
110
|
+
chromiumPath = '/usr/bin/chromium-browser';
|
|
111
|
+
if (this.logDebug) this.emit('debug', 'Chromium installed successfully via apt-get.');
|
|
112
|
+
return chromiumPath;
|
|
113
|
+
} catch {
|
|
114
|
+
if (this.logWarn) this.emit('warn', 'apt-get install failed. Trying apk or yum...');
|
|
115
|
+
}
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
// --- apk (Alpine) ---
|
|
118
|
+
try {
|
|
119
|
+
await new Promise((res, rej) =>
|
|
120
|
+
require('child_process').exec('sudo apk add --no-cache chromium ffmpeg', (e) => e ? rej(e) : res())
|
|
121
|
+
);
|
|
122
|
+
chromiumPath = '/usr/bin/chromium-browser';
|
|
123
|
+
if (this.logDebug) this.emit('debug', 'Chromium installed successfully via apk.');
|
|
124
124
|
return chromiumPath;
|
|
125
|
+
} catch {
|
|
126
|
+
if (this.logWarn) this.emit('warn', 'apk install failed. Trying yum...');
|
|
127
|
+
}
|
|
125
128
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
// --- yum (RHEL/CentOS) ---
|
|
130
|
+
try {
|
|
131
|
+
await new Promise((res, rej) =>
|
|
132
|
+
require('child_process').exec('sudo yum install -y chromium chromium-codecs-ffmpeg', (e) => e ? rej(e) : res())
|
|
133
|
+
);
|
|
134
|
+
chromiumPath = '/usr/bin/chromium-browser';
|
|
135
|
+
if (this.logDebug) this.emit('debug', 'Chromium installed successfully via yum.');
|
|
136
|
+
return chromiumPath;
|
|
137
|
+
} catch {
|
|
138
|
+
if (this.logWarn) this.emit('warn', 'yum install failed. Falling back to Puppeteer bundled Chromium.');
|
|
129
139
|
}
|
|
140
|
+
|
|
141
|
+
// --- Fallback ---
|
|
142
|
+
chromiumPath = require('puppeteer-core').executablePath();
|
|
143
|
+
if (this.logWarn) this.emit('warn', `Using bundled Puppeteer Chromium at ${chromiumPath}`);
|
|
144
|
+
return chromiumPath;
|
|
145
|
+
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (this.logError) this.emit('error', `Chromium detection/install error: ${err.message}`);
|
|
148
|
+
throw err;
|
|
130
149
|
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
131
153
|
|
|
132
154
|
async isRunningInDocker() {
|
|
133
155
|
try {
|
package/src/melcloud.js
CHANGED
|
@@ -322,18 +322,18 @@ class MelCloud extends EventEmitter {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
async connectToMelCloudHome(
|
|
326
|
-
if (logDebug)
|
|
325
|
+
async connectToMelCloudHome(refresh = false) {
|
|
326
|
+
if (this.logDebug) this.emit('debug', 'Connecting to MELCloud Home');
|
|
327
|
+
|
|
327
328
|
let browser;
|
|
328
329
|
|
|
329
330
|
try {
|
|
330
|
-
const chromiumPath = await ensureChromiumInstalled(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
);
|
|
331
|
+
const chromiumPath = await this.ensureChromiumInstalled();
|
|
332
|
+
|
|
333
|
+
// dynamiczny require dopiero przy uruchomieniu
|
|
334
|
+
const puppeteer = require('puppeteer-core');
|
|
335
335
|
|
|
336
|
-
browser = await
|
|
336
|
+
browser = await puppeteer.launch({
|
|
337
337
|
headless: true,
|
|
338
338
|
executablePath: chromiumPath,
|
|
339
339
|
args: [
|
|
@@ -347,15 +347,15 @@ class MelCloud extends EventEmitter {
|
|
|
347
347
|
|
|
348
348
|
const page = await browser.newPage();
|
|
349
349
|
|
|
350
|
-
page.on('error', err =>
|
|
351
|
-
page.on('pageerror', err =>
|
|
352
|
-
page.on('close', () =>
|
|
353
|
-
browser.on('disconnected', () =>
|
|
350
|
+
page.on('error', err => { if (this.logError) this.emit('error', `Page crashed: ${err.message}`); });
|
|
351
|
+
page.on('pageerror', err => { if (this.logError) this.emit('error', `Browser error: ${err.message}`); });
|
|
352
|
+
page.on('close', () => { if (this.logDebug) this.emit('debug', 'Page was closed unexpectedly'); });
|
|
353
|
+
browser.on('disconnected', () => { if (this.logDebug) this.emit('debug', 'Browser disconnected unexpectedly'); });
|
|
354
354
|
|
|
355
355
|
page.setDefaultTimeout(30000);
|
|
356
356
|
page.setDefaultNavigationTimeout(30000);
|
|
357
357
|
|
|
358
|
-
await page.goto(ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'] });
|
|
358
|
+
await page.goto(this.ApiUrlsHome.BaseURL, { waitUntil: ['domcontentloaded', 'networkidle2'] });
|
|
359
359
|
|
|
360
360
|
const buttons = await page.$$('button.btn--blue');
|
|
361
361
|
let loginBtn = null;
|
|
@@ -367,7 +367,7 @@ class MelCloud extends EventEmitter {
|
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
if (!loginBtn) {
|
|
370
|
-
|
|
370
|
+
this.emit('warn', 'Login button not found');
|
|
371
371
|
return null;
|
|
372
372
|
}
|
|
373
373
|
|
|
@@ -382,16 +382,16 @@ class MelCloud extends EventEmitter {
|
|
|
382
382
|
const usernameInput = await page.$('input[name="username"]');
|
|
383
383
|
const passwordInput = await page.$('input[name="password"]');
|
|
384
384
|
if (!usernameInput || !passwordInput) {
|
|
385
|
-
|
|
385
|
+
this.emit('warn', 'Username or password input not found');
|
|
386
386
|
return null;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
await page.type('input[name="username"]', user, { delay: 50 });
|
|
390
|
-
await page.type('input[name="password"]', passwd, { delay: 50 });
|
|
389
|
+
await page.type('input[name="username"]', this.user, { delay: 50 });
|
|
390
|
+
await page.type('input[name="password"]', this.passwd, { delay: 50 });
|
|
391
391
|
|
|
392
392
|
const submitButton = await page.$('input[type="submit"], button[type="submit"]');
|
|
393
393
|
if (!submitButton) {
|
|
394
|
-
|
|
394
|
+
this.emit('warn', 'Submit button not found on login form');
|
|
395
395
|
return null;
|
|
396
396
|
}
|
|
397
397
|
|
|
@@ -413,7 +413,7 @@ class MelCloud extends EventEmitter {
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
if (!c1 || !c2) {
|
|
416
|
-
|
|
416
|
+
this.emit('warn', 'Cookies C1/C2 missing after login');
|
|
417
417
|
return null;
|
|
418
418
|
}
|
|
419
419
|
|
|
@@ -424,9 +424,11 @@ class MelCloud extends EventEmitter {
|
|
|
424
424
|
].join('; ');
|
|
425
425
|
|
|
426
426
|
const accountInfo = { ContextKey: contextKey, UseFahrenheit: false };
|
|
427
|
-
|
|
427
|
+
this.contextKey = contextKey;
|
|
428
428
|
|
|
429
|
-
|
|
429
|
+
await this.functions.saveData(this.accountFile, accountInfo);
|
|
430
|
+
|
|
431
|
+
if (!refresh) this.emit('success', 'Connect to MELCloud Home Success');
|
|
430
432
|
return accountInfo;
|
|
431
433
|
|
|
432
434
|
} catch (error) {
|
|
@@ -434,11 +436,12 @@ class MelCloud extends EventEmitter {
|
|
|
434
436
|
} finally {
|
|
435
437
|
if (browser) {
|
|
436
438
|
try { await browser.close(); }
|
|
437
|
-
catch (closeErr) { logError
|
|
439
|
+
catch (closeErr) { if (this.logError) this.emit('error', `Failed to close Puppeteer browser: ${closeErr.message}`); }
|
|
438
440
|
}
|
|
439
441
|
}
|
|
440
442
|
}
|
|
441
443
|
|
|
444
|
+
|
|
442
445
|
async checkDevicesList() {
|
|
443
446
|
let devices = [];
|
|
444
447
|
switch (this.accountType) {
|