homebridge-melcloud-control 4.2.3-beta.8 → 4.2.4

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.
@@ -6,7 +6,7 @@ import EventEmitter from 'events';
6
6
  import puppeteer from 'puppeteer';
7
7
  import ImpulseGenerator from './impulsegenerator.js';
8
8
  import Functions from './functions.js';
9
- import { ApiUrlsHome } from './constants.js';
9
+ import { ApiUrlsHome, LanguageLocaleMap } from './constants.js';
10
10
  const execPromise = promisify(exec);
11
11
 
12
12
  class MelCloud extends EventEmitter {
@@ -22,7 +22,8 @@ class MelCloud extends EventEmitter {
22
22
  this.accountFile = accountFile;
23
23
  this.buildingsFile = buildingsFile;
24
24
  this.devicesFile = devicesFile;
25
- this.contextKey = null;
25
+ this.headers = {};
26
+
26
27
  this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
27
28
  .on('warn', warn => this.emit('warn', warn))
28
29
  .on('error', error => this.emit('error', error))
@@ -36,7 +37,7 @@ class MelCloud extends EventEmitter {
36
37
  };
37
38
  this.impulseGenerator = new ImpulseGenerator()
38
39
  .on('connect', () => this.handleWithLock('connect', async () => {
39
- await this.connect(true);
40
+ await this.connect();
40
41
  }))
41
42
  .on('checkDevicesList', () => this.handleWithLock('checkDevicesList', async () => {
42
43
  await this.checkDevicesList();
@@ -61,31 +62,57 @@ class MelCloud extends EventEmitter {
61
62
  }
62
63
 
63
64
  // MELCloud Home
64
- async checkDevicesList() {
65
+ async checkScenesList() {
65
66
  try {
66
- const devicesList = { State: false, Info: null, Devices: [] }
67
- const headers = {
68
- 'Accept': '*/*',
69
- 'Accept-Language': 'en-US,en;q=0.9',
70
- 'Cookie': this.contextKey,
71
- 'User-Agent': 'homebridge-melcloud-control/4.0.0',
72
- 'DNT': '1',
73
- 'Origin': 'https://melcloudhome.com',
74
- 'Referer': 'https://melcloudhome.com/dashboard',
75
- 'Sec-Fetch-Dest': 'empty',
76
- 'Sec-Fetch-Mode': 'cors',
77
- 'Sec-Fetch-Site': 'same-origin',
78
- 'X-CSRF': '1'
67
+ if (this.logDebug) this.emit('debug', `Scanning for scenes`);
68
+ const listScenesData = await axios(ApiUrlsHome.GetUserScenes, {
69
+ method: 'GET',
70
+ baseURL: ApiUrlsHome.BaseURL,
71
+ timeout: 25000,
72
+ headers: this.headers
73
+ });
74
+
75
+ const scenesList = listScenesData.data;
76
+ if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
77
+
78
+ const capitalizeKeysDeep = obj => {
79
+ if (Array.isArray(obj)) {
80
+ return obj.map(item => capitalizeKeysDeep(item));
81
+ }
82
+
83
+ if (obj && typeof obj === 'object') {
84
+ return Object.fromEntries(
85
+ Object.entries(obj).map(([key, value]) => [
86
+ key.charAt(0).toUpperCase() + key.slice(1),
87
+ capitalizeKeysDeep(value)
88
+ ])
89
+ );
90
+ }
91
+
92
+ return obj;
79
93
  };
80
94
 
95
+ return capitalizeKeysDeep(scenesList);
96
+ } catch (error) {
97
+ throw new Error(`Check scenes list error: ${error.message}`);
98
+ }
99
+ }
100
+
101
+ async checkDevicesList() {
102
+ try {
103
+ const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
81
104
  if (this.logDebug) this.emit('debug', `Scanning for devices`);
82
105
  const listDevicesData = await axios(ApiUrlsHome.GetUserContext, {
83
106
  method: 'GET',
84
107
  baseURL: ApiUrlsHome.BaseURL,
85
108
  timeout: 25000,
86
- headers: headers
109
+ headers: this.headers
87
110
  });
88
- const buildingsList = listDevicesData.data.buildings;
111
+
112
+ const userContext = listDevicesData.data;
113
+ const buildings = userContext.buildings ?? [];
114
+ const guestBuildings = userContext.guestBuildings ?? [];
115
+ const buildingsList = [...buildings, ...guestBuildings];
89
116
  if (this.logDebug) this.emit('debug', `Buildings: ${JSON.stringify(buildingsList, null, 2)}`);
90
117
 
91
118
  if (!buildingsList) {
@@ -93,7 +120,7 @@ class MelCloud extends EventEmitter {
93
120
  return devicesList;
94
121
  }
95
122
 
96
- await this.functions.saveData(this.buildingsFile, buildingsList);
123
+ await this.functions.saveData(this.buildingsFile, userContext);
97
124
  if (this.logDebug) this.emit('debug', `Buildings list saved`);
98
125
 
99
126
  const devices = buildingsList.flatMap(building => {
@@ -142,6 +169,7 @@ class MelCloud extends EventEmitter {
142
169
  ...capitalizeKeys(device.Capabilities || {}),
143
170
  ...settingsObject,
144
171
  DeviceType: type,
172
+ FirmwareAppVersion: device.ConnectedInterfaceIdentifier,
145
173
  IsConnected: device.IsConnected
146
174
  };
147
175
 
@@ -149,7 +177,7 @@ class MelCloud extends EventEmitter {
149
177
  if (device.FrostProtection) device.FrostProtection = { ...capitalizeKeys(device.FrostProtection || {}) };
150
178
  if (device.OverheatProtection) device.OverheatProtection = { ...capitalizeKeys(device.OverheatProtection || {}) };
151
179
  if (device.HolidayMode) device.HolidayMode = { ...capitalizeKeys(device.HolidayMode || {}) };
152
- if (Array.isArray(device.Schedule)) device.Schedule = device.Schedule.map(capitalizeKeysDeep);
180
+ if (Array.isArray(device.Schedule)) device.Schedule = device.Schedule.map(capitalizeKeysDeep || []);
153
181
 
154
182
  // Usuń stare pola Settings i Capabilities
155
183
  const { Settings, Capabilities, Id, GivenDisplayName, ...rest } = device;
@@ -159,8 +187,9 @@ class MelCloud extends EventEmitter {
159
187
  Type: type,
160
188
  DeviceID: Id,
161
189
  DeviceName: GivenDisplayName,
190
+ SerialNumber: Id,
162
191
  Device: deviceObject,
163
- Headers: headers
192
+ Headers: this.headers
164
193
  };
165
194
  };
166
195
 
@@ -177,17 +206,21 @@ class MelCloud extends EventEmitter {
177
206
  return devicesList;
178
207
  }
179
208
 
180
- await this.functions.saveData(this.devicesFile, devices);
181
- if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
209
+ const scenes = await this.checkScenesList();
182
210
 
183
211
  devicesList.State = true;
184
212
  devicesList.Info = `Found ${devicesCount} devices`;
185
213
  devicesList.Devices = devices;
214
+ devicesList.Scenes = scenes;
215
+
216
+ await this.functions.saveData(this.devicesFile, devicesList);
217
+ if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
218
+
186
219
  return devicesList;
187
220
  } catch (error) {
188
221
  if (error.response?.status === 401) {
189
- await connectToMelCloudHome();
190
222
  if (this.logWarn) this.emit('warn', 'Check devices list not possible, cookies expired, trying to get new.');
223
+ await this.connect();
191
224
  return;
192
225
  }
193
226
 
@@ -201,7 +234,7 @@ class MelCloud extends EventEmitter {
201
234
 
202
235
  let browser;
203
236
  try {
204
- const accountInfo = { State: false, Info: '', ContextKey: null, UseFahrenheit: false };
237
+ const accountInfo = { State: false, Info: '', Headers: {}, UseFahrenheit: false };
205
238
  let chromiumPath = await this.functions.ensureChromiumInstalled();
206
239
 
207
240
  // === Fallback to Puppeteer's built-in Chromium ===
@@ -313,16 +346,28 @@ class MelCloud extends EventEmitter {
313
346
  return accountInfo;
314
347
  }
315
348
 
316
- const contextKey = [
349
+ const cookies = [
317
350
  '__Secure-monitorandcontrol=chunks-2',
318
351
  `__Secure-monitorandcontrolC1=${c1}`,
319
352
  `__Secure-monitorandcontrolC2=${c2}`
320
353
  ].join('; ');
321
- this.contextKey = contextKey;
354
+
355
+ this.headers = {
356
+ 'Accept': '*/*',
357
+ 'Accept-Encoding': 'gzip, deflate, br',
358
+ 'Accept-Language': LanguageLocaleMap[this.language],
359
+ 'Cookie': cookies,
360
+ 'Priority': 'u=3, i',
361
+ 'Referer': ApiUrlsHome.Dashboard,
362
+ 'Sec-Fetch-Dest': 'empty',
363
+ 'Sec-Fetch-Mode': 'cors',
364
+ 'Sec-Fetch-Site': 'same-origin',
365
+ 'x-csrf': '1'
366
+ };
322
367
 
323
368
  accountInfo.State = true;
324
369
  accountInfo.Info = 'Connect to MELCloud Home Success';
325
- accountInfo.ContextKey = contextKey;
370
+ accountInfo.Headers = this.headers;
326
371
  await this.functions.saveData(this.accountFile, accountInfo);
327
372
 
328
373
  return accountInfo;
@@ -340,18 +385,12 @@ class MelCloud extends EventEmitter {
340
385
 
341
386
  async send(accountInfo) {
342
387
  try {
343
- const axiosInstance = axios.create({
388
+ await axios(ApiUrlsHome.UpdateApplicationOptions, {
344
389
  method: 'POST',
345
390
  baseURL: ApiUrlsHome.BaseURL,
346
391
  timeout: 15000,
347
- headers: {
348
- 'X-MitsContextKey': accountInfo.ContextKey,
349
- 'content-type': 'application/json'
350
- }
392
+ headers: accountInfo.Headers
351
393
  });
352
-
353
- const payload = { data: accountInfo.LoginData };
354
- await axiosInstance(ApiUrlsHome.UpdateApplicationOptions, payload);
355
394
  await this.functions.saveData(this.accountFile, accountInfo);
356
395
  return true;
357
396
  } catch (error) {