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.
- package/CHANGELOG.md +11 -0
- package/README.md +36 -21
- package/config.schema.json +433 -25
- package/homebridge-ui/public/index.html +86 -16
- package/index.js +3 -3
- package/package.json +2 -2
- package/src/constants.js +48 -5
- package/src/deviceata.js +274 -155
- package/src/deviceatw.js +334 -218
- package/src/deviceerv.js +250 -126
- package/src/melcloud.js +24 -31
- package/src/melcloudata.js +40 -21
- package/src/melcloudatw.js +46 -29
- package/src/melclouderv.js +37 -21
- package/src/melcloudhome.js +76 -37
package/src/melcloudhome.js
CHANGED
|
@@ -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.
|
|
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(
|
|
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
|
|
65
|
+
async checkScenesList() {
|
|
65
66
|
try {
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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: '',
|
|
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
|
|
349
|
+
const cookies = [
|
|
317
350
|
'__Secure-monitorandcontrol=chunks-2',
|
|
318
351
|
`__Secure-monitorandcontrolC1=${c1}`,
|
|
319
352
|
`__Secure-monitorandcontrolC2=${c2}`
|
|
320
353
|
].join('; ');
|
|
321
|
-
|
|
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.
|
|
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
|
-
|
|
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) {
|