homebridge-melcloud-control 4.0.0-beta.31 → 4.0.0-beta.33

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.
@@ -201,6 +201,34 @@
201
201
  }
202
202
  ]
203
203
  },
204
+ "displayType": {
205
+ "title": "Account Type",
206
+ "type": "integer",
207
+ "minimum": 0,
208
+ "maximum": 2,
209
+ "default": 1,
210
+ "description": "Here select the language used in MELCloud account.",
211
+ "oneOf": [
212
+ {
213
+ "title": "None/Disabled",
214
+ "enum": [
215
+ 0
216
+ ]
217
+ },
218
+ {
219
+ "title": "MELCLoud",
220
+ "enum": [
221
+ 1
222
+ ]
223
+ },
224
+ {
225
+ "title": "MELCLoud Home",
226
+ "enum": [
227
+ 2
228
+ ]
229
+ }
230
+ ]
231
+ },
204
232
  "ataDevices": {
205
233
  "title": "Devices ATA",
206
234
  "type": "array",
@@ -1798,7 +1826,8 @@
1798
1826
  "name",
1799
1827
  "user",
1800
1828
  "passwd",
1801
- "language"
1829
+ "language",
1830
+ "displayType"
1802
1831
  ]
1803
1832
  }
1804
1833
  }
@@ -1817,6 +1846,7 @@
1817
1846
  "type": "password"
1818
1847
  },
1819
1848
  "accounts[].language",
1849
+ "accounts[].displayType",
1820
1850
  {
1821
1851
  "key": "accounts[]",
1822
1852
  "type": "tabarray",
package/index.js CHANGED
@@ -30,6 +30,9 @@ class MelCloudPlatform {
30
30
  api.on('didFinishLaunching', async () => {
31
31
  //loop through accounts
32
32
  for (const account of config.accounts) {
33
+ const displayType = account.displayType || 1;
34
+ if (displayType === 0) continue;
35
+
33
36
  const accountName = account.name;
34
37
  const user = account.user;
35
38
  const passwd = account.passwd;
@@ -76,7 +79,6 @@ class MelCloudPlatform {
76
79
  const accountFile = `${prefDir}/${accountName}_Account`;
77
80
  const buildingsFile = `${prefDir}/${accountName}_Buildings`;
78
81
  const devicesFile = `${prefDir}/${accountName}_Devices`;
79
- const cookiesFile = `${prefDir}/${accountName}_Cookies`;
80
82
 
81
83
 
82
84
  //set account refresh interval
@@ -88,7 +90,7 @@ class MelCloudPlatform {
88
90
  .on('start', async () => {
89
91
  try {
90
92
  //melcloud account
91
- const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, cookiesFile, logLevel.warn, logLevel.debug, false)
93
+ const melCloud = new MelCloud(user, passwd, language, accountFile, buildingsFile, devicesFile, logLevel.warn, logLevel.debug, false)
92
94
  .on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
93
95
  .on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
94
96
  .on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
@@ -98,15 +100,14 @@ class MelCloudPlatform {
98
100
  //connect
99
101
  let response;
100
102
  try {
101
- response = await melCloud.connect('home');
102
- return;
103
+ response = await melCloud.connect(displayType);
103
104
  } catch (error) {
104
105
  if (logLevel.error) log.error(`${accountName}, Connect error: ${error.message ?? error}`);
105
106
  return;
106
107
  }
107
108
 
108
109
  const accountInfo = response.accountInfo ?? false;
109
- const contextKey = response.contextKey ?? false;
110
+ const contextKey = displayType === 1 ? response.contextKey : response;
110
111
  const useFahrenheit = response.useFahrenheit ?? false;
111
112
 
112
113
  if (contextKey === false) {
@@ -116,7 +117,8 @@ class MelCloudPlatform {
116
117
  //check devices list
117
118
  let devicesInMelcloud;
118
119
  try {
119
- devicesInMelcloud = await melCloud.checkDevicesList(contextKey);
120
+ devicesInMelcloud = await melCloud.checkDevicesList(displayType, contextKey);
121
+ return
120
122
  } catch (error) {
121
123
  if (logLevel.error) log.error(`${accountName}, Check devices list error: ${error.message ?? error}`);
122
124
  return;
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.31",
4
+ "version": "4.0.0-beta.33",
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, cookiesFile, logWarn, logDebug, requestConfig) {
10
+ constructor(user, passwd, language, accountFile, buildingsFile, devicesFile, logWarn, logDebug, requestConfig) {
11
11
  super();
12
12
  this.user = user;
13
13
  this.passwd = passwd;
@@ -15,7 +15,6 @@ class MelCloud extends EventEmitter {
15
15
  this.accountFile = accountFile;
16
16
  this.buildingsFile = buildingsFile;
17
17
  this.devicesFile = devicesFile;
18
- this.cookiesFile = cookiesFile;
19
18
  this.logWarn = logWarn;
20
19
  this.logDebug = logDebug;
21
20
  this.requestConfig = requestConfig;
@@ -58,7 +57,7 @@ class MelCloud extends EventEmitter {
58
57
  }
59
58
  }
60
59
 
61
- async checkDevicesList(contextKey) {
60
+ async checkMelcloudDevicesList(contextKey) {
62
61
  try {
63
62
  const axiosInstanceGet = axios.create({
64
63
  method: 'GET',
@@ -109,7 +108,7 @@ class MelCloud extends EventEmitter {
109
108
  }
110
109
  }
111
110
 
112
- async connectMelCloud() {
111
+ async connectToMelCloud() {
113
112
  if (this.logDebug) this.emit('debug', `Connecting to MELCloud`);
114
113
 
115
114
  try {
@@ -156,25 +155,20 @@ class MelCloud extends EventEmitter {
156
155
 
157
156
  this.emit('success', `Connect to MELCloud Success`);
158
157
 
159
- return {
160
- accountInfo,
161
- contextKey,
162
- useFahrenheit
163
- };
158
+ return { accountInfo, contextKey, useFahrenheit };
164
159
  } catch (error) {
165
160
  throw new Error(`Connect to MELCloud error: ${error.message}`);
166
161
  }
167
162
  }
168
163
 
169
- async connectMelCloudHome() {
164
+ async checkMelcloudHomeDevicesList(cookies) {
170
165
  if (this.logDebug) this.emit('debug', `Connecting to MELCloud Home`);
171
166
 
172
167
  try {
173
- const coocies = await this.getCookies();
174
- const c1 = coocies.C1.trim();
175
- const c2 = coocies.C2.trim();
168
+ const c1 = cookies.c1.trim();
169
+ const c2 = cookies.c2.trim();
176
170
 
177
- const cookies = [
171
+ const cookie = [
178
172
  '__Secure-monitorandcontrol=chunks-2',
179
173
  `__Secure-monitorandcontrolC1=${c1}`,
180
174
  `__Secure-monitorandcontrolC2=${c2}`,
@@ -190,7 +184,7 @@ class MelCloud extends EventEmitter {
190
184
  headers: {
191
185
  'Accept': '*/*',
192
186
  'Accept-Language': 'en-US,en;q=0.9',
193
- 'Cookie': cookies,
187
+ 'Cookie': cookie,
194
188
  'User-Agent': 'homebridge-melcloud-control/4.0.0',
195
189
  'DNT': '1',
196
190
  'Origin': 'https://melcloudhome.com',
@@ -202,62 +196,74 @@ class MelCloud extends EventEmitter {
202
196
  }
203
197
  });
204
198
 
205
- const response = await axiosInstance.get(ApiUrlsHome.GetUserContext);
206
- const homeData = response.data;
207
- if (this.logDebug) this.emit('debug', `MELCloud Home data: ${JSON.stringify(homeData, null, 2)}`);
199
+ if (this.logDebug) this.emit('debug', `Scanning for devices`);
200
+ const listDevicesData = await axiosInstance.get(ApiUrlsHome.GetUserContext);
201
+ const buildingsList = listDevicesData.data.buildings;
202
+ if (this.logDebug) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
208
203
 
209
- this.emit('success', `Connect to MELCloud Home Success`);
204
+ if (!buildingsList) {
205
+ if (this.logWarn) this.emit('warn', `No building found`);
206
+ return null;
207
+ }
208
+
209
+ await this.functions.saveData(this.buildingsFile, buildingsList);
210
+ if (this.logDebug) this.emit('debug', `Buildings list saved`);
211
+
212
+ const devices = buildingsList.flatMap(building => [
213
+ ...(building.airToAirUnits || []),
214
+ ...(building.airToWaterUnits || [])
215
+ ]);
216
+
217
+ const devicesCount = devices.length;
218
+ if (devicesCount === 0) {
219
+ if (this.logWarn) this.emit('warn', `No devices found`);
220
+ return null;
221
+ }
210
222
 
211
- return
223
+ await this.functions.saveData(this.devicesFile, devices);
224
+ if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
225
+
226
+ return devices;
212
227
  } catch (error) {
213
228
  throw new Error(`Connect to MELCloud Home error: ${error.message}`);
214
229
  }
215
230
  }
216
231
 
217
- async getCookies() {
232
+ async connectToMelCloudHome() {
233
+ if (this.logDebug) this.emit('debug', `Connecting to MELCloud Home`);
234
+
218
235
  const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
219
236
  const page = await browser.newPage();
220
237
 
221
238
  try {
222
- this.emit('warn', 'Opening login page...');
239
+ // Open MELCloud Home
223
240
  await page.goto(ApiUrlsHome.BaseURL, { waitUntil: 'networkidle2' });
224
-
225
- // Kliknij przycisk logowania
226
241
  const buttons = await page.$$('button.btn--blue');
227
242
  let loginBtn = null;
228
243
  for (const btn of buttons) {
229
244
  const text = await page.evaluate(el => el.textContent, btn);
230
- if (text.trim() === 'Zaloguj') {
245
+ if (text.trim() === 'Zaloguj' || text.trim() === 'Log In') {
231
246
  loginBtn = btn;
232
247
  break;
233
248
  }
234
249
  }
235
250
 
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
- ]);
251
+ if (!loginBtn && this.logWarn) this.emit('warn', `Login button not found`);
242
252
 
243
- // Poczekaj, strona przekieruje do Cognito login
253
+ // Set credentials and login
254
+ await Promise.all([loginBtn.click(), page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })]);
244
255
  await page.waitForSelector('input[name="username"]', { timeout: 15000 });
245
-
246
- this.emit('warn', 'Typing credentials...');
247
256
  await page.type('input[name="username"]', this.user, { delay: 50 });
248
257
  await page.type('input[name="password"]', this.passwd, { delay: 50 });
249
258
 
250
- // Kliknij przycisk logowania
251
259
  const button1 = await page.$('input[type="submit"]');
252
- await Promise.all([
253
- button1.click(),
254
- page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })
255
- ]);
260
+ await Promise.all([button1.click(), page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 20000 })]);
256
261
 
262
+ // Get cookies C1 and C2
257
263
  let c1 = null, c2 = null;
258
264
  const start = Date.now();
259
265
 
260
- // Loop max 20s, czekamy aż pojawią się cookies
266
+ // Loop max 20s
261
267
  while ((!c1 || !c2) && Date.now() - start < 20000) {
262
268
  const cookies = await page.cookies();
263
269
  c1 = cookies.find(c => c.name === '__Secure-monitorandcontrolC1')?.value || c1;
@@ -266,30 +272,45 @@ class MelCloud extends EventEmitter {
266
272
  }
267
273
 
268
274
  if (!c1 || !c2) {
269
- await browser.close();
270
- throw new Error('Cookies C1/C2 not found after login');
275
+ if (this.logWarn) this.emit('warn', `Cookies C1/C2 missing`);
276
+ return null;
271
277
  }
272
278
 
273
- // Zapis do pliku
274
- const data = { C1: c1, C2: c2, date: new Date().toISOString() };
275
- await this.functions.saveData(this.cookiesFile, data);
279
+ this.emit('success', `Connect to MELCloud Home Success`);
276
280
 
277
- this.emit('warn', 'Login successful.');
278
- return data;
279
- } catch (err) {
280
- this.emit('error', `Login failed: ${err.message}`);
281
- return null;
281
+ return { c1, c2 };
282
+ } catch (error) {
283
+ throw new Error(`Connect to MELCloud Home error: ${error.message}`);
282
284
  } finally {
283
285
  await browser.close();
284
286
  }
285
287
  }
286
288
 
287
- async connect(mode) {
288
- switch (mode) {
289
- case 'home':
290
- return await this.connectMelCloudHome();
289
+ async connect(type) {
290
+ let response = null;
291
+ switch (type) {
292
+ case 1:
293
+ response = await this.connectToMelCloud();
294
+ return response
295
+ case 2:
296
+ response = await this.connectToMelCloudHome();
297
+ return response
291
298
  default:
292
- return await this.connectMelCloud();
299
+ return null
300
+ }
301
+ }
302
+
303
+ async checkDevicesList(type, key) {
304
+ let devices = null;
305
+ switch (type) {
306
+ case 1:
307
+ devices = await this.checkMelcloudDevicesList(key);
308
+ return devices
309
+ case 2:
310
+ devices = await this.checkMelcloudHomeDevicesList(key);
311
+ return devices
312
+ default:
313
+ return null;
293
314
  }
294
315
  }
295
316