homebridge-melcloud-control 4.0.0-beta.2 → 4.0.0-beta.21
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 +3 -3
- package/package.json +1 -1
- package/src/melcloud.js +65 -58
package/index.js
CHANGED
|
@@ -76,7 +76,7 @@ class MelCloudPlatform {
|
|
|
76
76
|
const accountFile = `${prefDir}/${accountName}_Account`;
|
|
77
77
|
const buildingsFile = `${prefDir}/${accountName}_Buildings`;
|
|
78
78
|
const devicesFile = `${prefDir}/${accountName}_Devices`;
|
|
79
|
-
const
|
|
79
|
+
const cookiesFile = `${prefDir}/${accountName}_Cookies`;
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
//set account refresh interval
|
|
@@ -88,14 +88,14 @@ class MelCloudPlatform {
|
|
|
88
88
|
.on('start', async () => {
|
|
89
89
|
try {
|
|
90
90
|
//melcloud account
|
|
91
|
-
const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile,
|
|
91
|
+
const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logLevel.warn, logLevel.debug, false)
|
|
92
92
|
.on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
|
|
93
93
|
.on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
|
|
94
94
|
.on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
|
|
95
95
|
.on('warn', (msg) => logLevel.warn && log.warn(`${accountName}, ${msg}`))
|
|
96
96
|
.on('error', (msg) => logLevel.error && log.error(`${accountName}, ${msg}`));
|
|
97
97
|
|
|
98
|
-
await melCloud.
|
|
98
|
+
await melCloud.connectHomeCookies();
|
|
99
99
|
return;
|
|
100
100
|
|
|
101
101
|
|
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.21",
|
|
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
|
@@ -7,7 +7,7 @@ import Functions from './functions.js';
|
|
|
7
7
|
import { ApiUrls, ApiUrlsHome } from './constants.js';
|
|
8
8
|
|
|
9
9
|
class MelCloud extends EventEmitter {
|
|
10
|
-
constructor(user, passwd, language, accountFile, buildingsFile, devicesFile,
|
|
10
|
+
constructor(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logWarn, logDebug, requestConfig) {
|
|
11
11
|
super();
|
|
12
12
|
this.user = user;
|
|
13
13
|
this.passwd = passwd;
|
|
@@ -15,7 +15,7 @@ class MelCloud extends EventEmitter {
|
|
|
15
15
|
this.accountFile = accountFile;
|
|
16
16
|
this.buildingsFile = buildingsFile;
|
|
17
17
|
this.devicesFile = devicesFile;
|
|
18
|
-
this.
|
|
18
|
+
this.cookiesFile = cookiesFile;
|
|
19
19
|
this.logWarn = logWarn;
|
|
20
20
|
this.logDebug = logDebug;
|
|
21
21
|
this.requestConfig = requestConfig;
|
|
@@ -213,73 +213,80 @@ class MelCloud extends EventEmitter {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
async
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.emit('warn', `Opening login page...`);
|
|
235
|
-
await page.goto(loginUrl, { waitUntil: 'networkidle2' });
|
|
236
|
-
|
|
237
|
-
// Wpisz login i hasło
|
|
238
|
-
this.emit('warn', `Typing credentials...`);
|
|
239
|
-
await page.type('input[name="username"]', this.user, { delay: 50 });
|
|
240
|
-
await page.type('input[name="password"]', this.passwd, { delay: 50 });
|
|
241
|
-
|
|
242
|
-
// Kliknij "Sign in" (przycisk AWS Cognito)
|
|
243
|
-
let buttonFound = false;
|
|
244
|
-
for (const selector of buttonSelectors) {
|
|
245
|
-
const button = await page.$(selector);
|
|
246
|
-
if (button) {
|
|
247
|
-
console.log(`Found submit button: ${selector}`);
|
|
248
|
-
await Promise.all([
|
|
249
|
-
button.click(),
|
|
250
|
-
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 })
|
|
251
|
-
]);
|
|
252
|
-
buttonFound = true;
|
|
253
|
-
break;
|
|
216
|
+
async connectHomeCookies() {
|
|
217
|
+
try {
|
|
218
|
+
const loginUrl = 'https://melcloudhome.com';
|
|
219
|
+
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
|
220
|
+
const page = await browser.newPage();
|
|
221
|
+
|
|
222
|
+
this.emit('warn', 'Opening login page...');
|
|
223
|
+
await page.goto(loginUrl, { waitUntil: 'networkidle2' });
|
|
224
|
+
|
|
225
|
+
// Kliknij przycisk logowania
|
|
226
|
+
const buttons = await page.$$('button.btn--blue'); // wszystkie buttony z klasą btn--blue
|
|
227
|
+
let loginBtn = null;
|
|
228
|
+
for (const btn of buttons) {
|
|
229
|
+
const text = await page.evaluate(el => el.textContent, btn);
|
|
230
|
+
if (text.trim() === 'Zaloguj') {
|
|
231
|
+
loginBtn = btn;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
254
234
|
}
|
|
255
|
-
}
|
|
256
235
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
236
|
+
if (!loginBtn) throw new Error('Login button not found on homepage');
|
|
237
|
+
|
|
238
|
+
await Promise.all([
|
|
239
|
+
loginBtn.click(),
|
|
240
|
+
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
// Poczekaj, aż strona przekieruje do Cognito login
|
|
244
|
+
await page.waitForSelector('input[name="username"]', { timeout: 15000 });
|
|
260
245
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
246
|
+
this.emit('warn', 'Typing credentials...');
|
|
247
|
+
await page.type('input[name="username"]', this.user, { delay: 50 });
|
|
248
|
+
await page.type('input[name="password"]', this.passwd, { delay: 50 });
|
|
264
249
|
|
|
265
|
-
|
|
266
|
-
|
|
250
|
+
// Kliknij przycisk logowania
|
|
251
|
+
const button1 = await page.$('input[type="submit"]');
|
|
252
|
+
await Promise.all([
|
|
253
|
+
button1.click(),
|
|
254
|
+
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })
|
|
255
|
+
]);
|
|
267
256
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || null;
|
|
257
|
+
let c1 = null, c2 = null;
|
|
258
|
+
const start = Date.now();
|
|
271
259
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
260
|
+
// Loop max 20s, czekamy aż pojawią się cookies
|
|
261
|
+
while ((!c1 || !c2) && Date.now() - start < 20000) {
|
|
262
|
+
const cookies = await page.cookies();
|
|
263
|
+
c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
|
|
264
|
+
c2 = cookies.find(c => c.name === '__Secure-monitorandcontrolC2')?.value || c2;
|
|
265
|
+
if (!c1 || !c2) await new Promise(r => setTimeout(r, 500));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!c1 || !c2) {
|
|
269
|
+
await browser.close();
|
|
270
|
+
throw new Error('❌ Cookies C1/C2 not found after login');
|
|
271
|
+
}
|
|
275
272
|
|
|
276
|
-
|
|
273
|
+
// Zapis do pliku
|
|
274
|
+
const data = { C1: c1, C2: c2, date: new Date().toISOString() };
|
|
275
|
+
await this.functions.saveData(this.cookiesFile, data);
|
|
277
276
|
|
|
278
|
-
|
|
277
|
+
this.emit('warn', 'Login successful.');
|
|
278
|
+
return data;
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
} catch (err) {
|
|
281
|
+
this.emit('error', `Login failed: ${err.message}`);
|
|
282
|
+
return null;
|
|
283
|
+
} finally {
|
|
284
|
+
await browser.close();
|
|
285
|
+
}
|
|
281
286
|
}
|
|
282
287
|
|
|
288
|
+
|
|
289
|
+
|
|
283
290
|
async send(accountInfo) {
|
|
284
291
|
try {
|
|
285
292
|
const options = { data: accountInfo };
|