homebridge-melcloud-control 4.0.0-beta.413 → 4.0.0-beta.415
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/index.js +1 -1
- package/package.json +1 -1
- package/src/melcloud.js +21 -20
- package/src/melcloudhometoken.js +15 -15
- package/src/restful.js +1 -1
package/index.js
CHANGED
|
@@ -89,7 +89,7 @@ class MelCloudPlatform {
|
|
|
89
89
|
.on('start', async () => {
|
|
90
90
|
try {
|
|
91
91
|
//melcloud account
|
|
92
|
-
const melCloud = new MelCloud(accountType, user, passwd, language, accountFile, buildingsFile, devicesFile, logLevel.warn, logLevel.debug, false)
|
|
92
|
+
const melCloud = new MelCloud(accountType, user, passwd, language, accountFile, buildingsFile, devicesFile, logLevel.warn, logLevel.error, logLevel.debug, false)
|
|
93
93
|
.on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
|
|
94
94
|
.on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
|
|
95
95
|
.on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
|
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.415",
|
|
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
|
@@ -9,7 +9,7 @@ import Functions from './functions.js';
|
|
|
9
9
|
import { ApiUrls, ApiUrlsHome } from './constants.js';
|
|
10
10
|
|
|
11
11
|
class MelCloud extends EventEmitter {
|
|
12
|
-
constructor(accountType, user, passwd, language, accountFile, buildingsFile, devicesFile, logWarn, logDebug, requestConfig) {
|
|
12
|
+
constructor(accountType, user, passwd, language, accountFile, buildingsFile, devicesFile, logWarn, logError, logDebug, requestConfig) {
|
|
13
13
|
super();
|
|
14
14
|
this.accountType = accountType;
|
|
15
15
|
this.user = user;
|
|
@@ -19,6 +19,7 @@ class MelCloud extends EventEmitter {
|
|
|
19
19
|
this.buildingsFile = buildingsFile;
|
|
20
20
|
this.devicesFile = devicesFile;
|
|
21
21
|
this.logWarn = logWarn;
|
|
22
|
+
this.logError= logError;
|
|
22
23
|
this.logDebug = logDebug;
|
|
23
24
|
this.requestConfig = requestConfig;
|
|
24
25
|
this.devicesId = [];
|
|
@@ -73,7 +74,7 @@ class MelCloud extends EventEmitter {
|
|
|
73
74
|
const listDevicesData = await axiosInstance.get(ApiUrls.ListDevices);
|
|
74
75
|
|
|
75
76
|
if (!listDevicesData || !listDevicesData.data) {
|
|
76
|
-
this.emit('warn', `Invalid or empty response from MELCloud API`);
|
|
77
|
+
if (this.logWarn) this.emit('warn', `Invalid or empty response from MELCloud API`);
|
|
77
78
|
return null;
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -83,7 +84,7 @@ class MelCloud extends EventEmitter {
|
|
|
83
84
|
this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
|
|
84
85
|
|
|
85
86
|
if (!Array.isArray(buildingsList) || buildingsList.length === 0) {
|
|
86
|
-
this.emit('warn', `No buildings found in MELCloud account`);
|
|
87
|
+
if (this.logWarn) this.emit('warn', `No buildings found in MELCloud account`);
|
|
87
88
|
return null;
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -129,7 +130,7 @@ class MelCloud extends EventEmitter {
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
if (devices.length === 0) {
|
|
132
|
-
this.emit('warn', `No devices found in any building`);
|
|
133
|
+
if (this.logWarn) this.emit('warn', `No devices found in any building`);
|
|
133
134
|
return null;
|
|
134
135
|
}
|
|
135
136
|
|
|
@@ -342,10 +343,10 @@ class MelCloud extends EventEmitter {
|
|
|
342
343
|
|
|
343
344
|
const page = await browser.newPage();
|
|
344
345
|
|
|
345
|
-
page.on('error', err => this.emit('
|
|
346
|
-
page.on('pageerror', err => this.emit('
|
|
347
|
-
page.on('close', () => this.emit('
|
|
348
|
-
browser.on('disconnected', () => this.emit('
|
|
346
|
+
page.on('error', err => this.emit('error', `Page crashed: ${err.message}`));
|
|
347
|
+
page.on('pageerror', err => this.emit('error', `Browser error: ${err.message}`));
|
|
348
|
+
page.on('close', () => this.emit('debug', 'Page was closed unexpectedly'));
|
|
349
|
+
browser.on('disconnected', () => this.emit('debug', 'Browser disconnected unexpectedly'));
|
|
349
350
|
|
|
350
351
|
page.setDefaultTimeout(30000);
|
|
351
352
|
page.setDefaultNavigationTimeout(30000);
|
|
@@ -362,12 +363,12 @@ class MelCloud extends EventEmitter {
|
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
if (!loginBtn) {
|
|
365
|
-
this.emit('warn', 'Login button not found');
|
|
366
|
+
if (this.logWarn) this.emit('warn', 'Login button not found');
|
|
366
367
|
return null;
|
|
367
368
|
}
|
|
368
369
|
|
|
369
370
|
if (page.isClosed()) {
|
|
370
|
-
this.emit('warn', 'Page closed before login click');
|
|
371
|
+
if (this.logWarn) this.emit('warn', 'Page closed before login click');
|
|
371
372
|
return null;
|
|
372
373
|
}
|
|
373
374
|
|
|
@@ -382,12 +383,12 @@ class MelCloud extends EventEmitter {
|
|
|
382
383
|
const usernameInput = await page.$('input[name="username"]');
|
|
383
384
|
const passwordInput = await page.$('input[name="password"]');
|
|
384
385
|
if (!usernameInput || !passwordInput) {
|
|
385
|
-
this.emit('warn', 'Username or password input not found');
|
|
386
|
+
if (this.logWarn) this.emit('warn', 'Username or password input not found');
|
|
386
387
|
return null;
|
|
387
388
|
}
|
|
388
389
|
|
|
389
390
|
if (page.isClosed()) {
|
|
390
|
-
this.emit('warn', 'Page closed before typing credentials');
|
|
391
|
+
if (this.logWarn) this.emit('warn', 'Page closed before typing credentials');
|
|
391
392
|
return null;
|
|
392
393
|
}
|
|
393
394
|
|
|
@@ -396,12 +397,12 @@ class MelCloud extends EventEmitter {
|
|
|
396
397
|
|
|
397
398
|
const submitButton = await page.$('input[type="submit"], button[type="submit"]');
|
|
398
399
|
if (!submitButton) {
|
|
399
|
-
this.emit('warn', 'Submit button not found on login form');
|
|
400
|
+
if (this.logWarn) this.emit('warn', 'Submit button not found on login form');
|
|
400
401
|
return null;
|
|
401
402
|
}
|
|
402
403
|
|
|
403
404
|
if (page.isClosed()) {
|
|
404
|
-
this.emit('warn', 'Page closed before submitting form');
|
|
405
|
+
if (this.logWarn) this.emit('warn', 'Page closed before submitting form');
|
|
405
406
|
return null;
|
|
406
407
|
}
|
|
407
408
|
|
|
@@ -415,13 +416,13 @@ class MelCloud extends EventEmitter {
|
|
|
415
416
|
|
|
416
417
|
const pageText = await page.content();
|
|
417
418
|
if (pageText.includes('incorrect') || pageText.includes('Invalid') || pageText.includes('nieprawidłowe')) {
|
|
418
|
-
this.emit('warn', 'Login failed: incorrect email or password');
|
|
419
|
+
if (this.logWarn) this.emit('warn', 'Login failed: incorrect email or password');
|
|
419
420
|
return null;
|
|
420
421
|
}
|
|
421
422
|
|
|
422
423
|
const captchaPresent = await page.$('iframe[src*="captcha"], div[class*="captcha"]');
|
|
423
424
|
if (captchaPresent) {
|
|
424
|
-
this.emit('warn', 'Login blocked by CAPTCHA. Manual verification required.');
|
|
425
|
+
if (this.logWarn) this.emit('warn', 'Login blocked by CAPTCHA. Manual verification required.');
|
|
425
426
|
return null;
|
|
426
427
|
}
|
|
427
428
|
|
|
@@ -435,7 +436,7 @@ class MelCloud extends EventEmitter {
|
|
|
435
436
|
}
|
|
436
437
|
|
|
437
438
|
if (!c1 || !c2) {
|
|
438
|
-
this.emit('warn', 'Cookies C1/C2 missing after login');
|
|
439
|
+
if (this.logWarn) this.emit('warn', 'Cookies C1/C2 missing after login');
|
|
439
440
|
return null;
|
|
440
441
|
}
|
|
441
442
|
|
|
@@ -456,7 +457,7 @@ class MelCloud extends EventEmitter {
|
|
|
456
457
|
// Puppeteer / system error handling
|
|
457
458
|
if (error.message.includes('libnspr4.so') || error.message.includes('Failed to launch the browser process')) {
|
|
458
459
|
const inDocker = await this.functions.isRunningInDocker();
|
|
459
|
-
this.emit('
|
|
460
|
+
if (this.logError) this.emit('error', `Missing system libraries detected.`);
|
|
460
461
|
|
|
461
462
|
const installCmd =
|
|
462
463
|
'apt-get update && apt-get install -y ' +
|
|
@@ -465,7 +466,7 @@ class MelCloud extends EventEmitter {
|
|
|
465
466
|
'libgbm1 libasound2 libxshmfence1 fonts-liberation libappindicator3-1 libu2f-udev';
|
|
466
467
|
|
|
467
468
|
if (inDocker) {
|
|
468
|
-
this.emit('warn', `Running in Docker — attempting automatic fix...`);
|
|
469
|
+
if (this.logWarn) this.emit('warn', `Running in Docker — attempting automatic fix...`);
|
|
469
470
|
try {
|
|
470
471
|
const { execSync } = require('child_process');
|
|
471
472
|
execSync(installCmd, { stdio: 'inherit' });
|
|
@@ -486,7 +487,7 @@ class MelCloud extends EventEmitter {
|
|
|
486
487
|
try {
|
|
487
488
|
await browser.close();
|
|
488
489
|
} catch (closeErr) {
|
|
489
|
-
this.emit('
|
|
490
|
+
if (this.logError) this.emit('error', `Failed to close Puppeteer browser: ${closeErr.message}`);
|
|
490
491
|
}
|
|
491
492
|
}
|
|
492
493
|
}
|
package/src/melcloudhometoken.js
CHANGED
|
@@ -59,7 +59,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
59
59
|
const csrf = dom.window.document.querySelector('input[name="_csrf"]')?.value;
|
|
60
60
|
|
|
61
61
|
if (!csrf) {
|
|
62
|
-
this.emit('warn', 'CSRF token not found');
|
|
62
|
+
if (this.logWarn) this.emit('warn', 'CSRF token not found');
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -85,7 +85,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
if (response.status === 400) {
|
|
88
|
-
this.emit('warn', `Login failed: ${response.data}`);
|
|
88
|
+
if (this.logWarn) this.emit('warn', `Login failed: ${response.data}`);
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -106,7 +106,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
106
106
|
return code || null;
|
|
107
107
|
|
|
108
108
|
} catch (err) {
|
|
109
|
-
this.emit('warn', `loginToMelCloudHome error: ${err}`);
|
|
109
|
+
if (this.logWarn) this.emit('warn', `loginToMelCloudHome error: ${err}`);
|
|
110
110
|
return null;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -120,7 +120,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
120
120
|
if (locationHeader && locationHeader.startsWith('melcloudhome://')) {
|
|
121
121
|
const match = locationHeader.match(/[?&]code=([^&]+)/);
|
|
122
122
|
if (match) {
|
|
123
|
-
this.emit('warn', `Found code in Location header: ${match[1]}`);
|
|
123
|
+
if (this.logWarn) this.emit('warn', `Found code in Location header: ${match[1]}`);
|
|
124
124
|
resolve(match[1]);
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
@@ -132,14 +132,14 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
132
132
|
const formActionMatch = data.match(/action="([^"]+)"/);
|
|
133
133
|
|
|
134
134
|
if (formCodeMatch && formStateMatch && formActionMatch) {
|
|
135
|
-
this.emit('warn', 'Found form_post, submitting...');
|
|
135
|
+
if (this.logWarn) this.emit('warn', 'Found form_post, submitting...');
|
|
136
136
|
try {
|
|
137
137
|
const code = await this.submitFormPost(formActionMatch[1], formCodeMatch[1], formStateMatch[1]);
|
|
138
|
-
this.emit('warn', `submitFormPost returned code: ${code}`);
|
|
138
|
+
if (this.logWarn) this.emit('warn', `submitFormPost returned code: ${code}`);
|
|
139
139
|
resolve(code);
|
|
140
140
|
return;
|
|
141
141
|
} catch (err) {
|
|
142
|
-
this.emit('warn', `submitFormPost failed: ${err}`);
|
|
142
|
+
if (this.logWarn) this.emit('warn', `submitFormPost failed: ${err}`);
|
|
143
143
|
reject(err);
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
@@ -148,29 +148,29 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
148
148
|
// 3️⃣ JS redirect in body
|
|
149
149
|
const bodyCodeMatch = data.match(/melcloudhome:\/\/[^"'\s]*[?&]code=([^&"'\s]+)/);
|
|
150
150
|
if (bodyCodeMatch) {
|
|
151
|
-
this.emit('warn', `Found code in body: ${bodyCodeMatch[1]}`);
|
|
151
|
+
if (this.logWarn) this.emit('warn', `Found code in body: ${bodyCodeMatch[1]}`);
|
|
152
152
|
resolve(bodyCodeMatch[1]);
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// 4️⃣ Follow redirect
|
|
157
157
|
if (locationHeader && ['301', '302', '303'].includes(headers['status'] || '')) {
|
|
158
|
-
this.emit('warn', `Following redirect to ${locationHeader}`);
|
|
158
|
+
if (this.logWarn) this.emit('warn', `Following redirect to ${locationHeader}`);
|
|
159
159
|
try {
|
|
160
160
|
const code = await followRedirect(locationHeader);
|
|
161
161
|
resolve(code);
|
|
162
162
|
return;
|
|
163
163
|
} catch (err) {
|
|
164
|
-
this.emit('warn', `Follow redirect failed: ${err}`);
|
|
164
|
+
if (this.logWarn) this.emit('warn', `Follow redirect failed: ${err}`);
|
|
165
165
|
reject(err);
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
this.emit('warn', 'Authorization code not found in response');
|
|
170
|
+
if (this.logWarn) this.emit('warn', 'Authorization code not found in response');
|
|
171
171
|
reject(new Error('Authorization code not found'));
|
|
172
172
|
} catch (err) {
|
|
173
|
-
this.emit('warn', `extractCodeFromResponse error: ${err}`);
|
|
173
|
+
if (this.logWarn) this.emit('warn', `extractCodeFromResponse error: ${err}`);
|
|
174
174
|
reject(err);
|
|
175
175
|
}
|
|
176
176
|
});
|
|
@@ -178,7 +178,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
178
178
|
|
|
179
179
|
async submitFormPost(actionUrl, code, state) {
|
|
180
180
|
const formData = new URLSearchParams({ code, state });
|
|
181
|
-
this.emit('warn', `Submitting form_post to ${actionUrl}`);
|
|
181
|
+
if (this.logWarn) this.emit('warn', `Submitting form_post to ${actionUrl}`);
|
|
182
182
|
const res = await this.client.post(actionUrl, formData.toString(), {
|
|
183
183
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
184
184
|
maxRedirects: 0,
|
|
@@ -191,7 +191,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
191
191
|
if (match) return match[1];
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
this.emit('warn', 'Code not found after form_post submission');
|
|
194
|
+
if (this.logWarn) this.emit('warn', 'Code not found after form_post submission');
|
|
195
195
|
throw new Error('Code not found after form_post submission');
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -213,7 +213,7 @@ class MelCloudHomeToken extends EventEmitter {
|
|
|
213
213
|
});
|
|
214
214
|
|
|
215
215
|
const tokens = tokenResponse.data;
|
|
216
|
-
this.emit('warn', `Token obtained: ${JSON.stringify(tokens)}`);
|
|
216
|
+
if (this.logWarn) this.emit('warn', `Token obtained: ${JSON.stringify(tokens)}`);
|
|
217
217
|
return tokens;
|
|
218
218
|
|
|
219
219
|
} catch (err) {
|
package/src/restful.js
CHANGED
|
@@ -65,7 +65,7 @@ class RestFul extends EventEmitter {
|
|
|
65
65
|
|
|
66
66
|
// Start the server
|
|
67
67
|
app.listen(this.port, () => {
|
|
68
|
-
this.emit('connected', `RESTful started on port: ${this.
|
|
68
|
+
this.emit('connected', `RESTful started on port: ${this.port}`);
|
|
69
69
|
});
|
|
70
70
|
} catch (error) {
|
|
71
71
|
if (this.logWarn) this.emit('warn', `RESTful Connect error: ${error}`);
|