homebridge-melcloud-control 4.4.1-beta.3 → 4.4.1-beta.30
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 +10 -0
- package/README.md +34 -34
- package/config.schema.json +54 -54
- package/index.js +4 -4
- package/package.json +2 -2
- package/src/constants.js +62 -50
- package/src/deviceata.js +2 -6
- package/src/deviceatw.js +2 -6
- package/src/deviceerv.js +2 -6
- package/src/functions.js +105 -111
- package/src/melcloud.js +4 -4
- package/src/melcloudata.js +9 -9
- package/src/melcloudatw.js +8 -8
- package/src/melclouderv.js +7 -7
- package/src/melcloudhome.js +34 -32
package/src/melcloudata.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, AirConditioner } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudAta extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -204,7 +204,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
204
204
|
case 'account':
|
|
205
205
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
206
206
|
payload = { data: flagData.LoginData };
|
|
207
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
207
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
208
208
|
await this.functions.saveData(this.accountFile, flagData);
|
|
209
209
|
break;
|
|
210
210
|
default:
|
|
@@ -230,7 +230,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
230
230
|
HideDryModeControl: deviceData.HideDryModeControl,
|
|
231
231
|
HasPendingCommand: true
|
|
232
232
|
};
|
|
233
|
-
path = ApiUrls.
|
|
233
|
+
path = ApiUrls.Post.Ata;
|
|
234
234
|
update = true;
|
|
235
235
|
break;
|
|
236
236
|
}
|
|
@@ -254,7 +254,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
254
254
|
units: { ATA: [deviceData.DeviceID] }
|
|
255
255
|
};
|
|
256
256
|
method = 'POST';
|
|
257
|
-
path =
|
|
257
|
+
path = ApiUrls.Home.Post.ProtectionFrost;
|
|
258
258
|
update = true;
|
|
259
259
|
break;
|
|
260
260
|
case 'overheatprotection':
|
|
@@ -265,7 +265,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
265
265
|
units: { ATA: [deviceData.DeviceID] }
|
|
266
266
|
};
|
|
267
267
|
method = 'POST';
|
|
268
|
-
path =
|
|
268
|
+
path = ApiUrls.Home.Post.ProtectionOverheat;
|
|
269
269
|
update = true;
|
|
270
270
|
break;
|
|
271
271
|
case 'holidaymode':
|
|
@@ -276,17 +276,17 @@ class MelCloudAta extends EventEmitter {
|
|
|
276
276
|
units: { ATA: [deviceData.DeviceID] }
|
|
277
277
|
};
|
|
278
278
|
method = 'POST';
|
|
279
|
-
path =
|
|
279
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
280
280
|
break;
|
|
281
281
|
case 'schedule':
|
|
282
282
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
283
283
|
method = 'PUT';
|
|
284
|
-
path =
|
|
284
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
285
285
|
update = true;
|
|
286
286
|
break;
|
|
287
287
|
case 'scene':
|
|
288
288
|
method = 'PUT';
|
|
289
|
-
path =
|
|
289
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
290
290
|
break;
|
|
291
291
|
default:
|
|
292
292
|
if (displayType === 1 && deviceData.Device.OperationMode === 8) {
|
|
@@ -312,7 +312,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
312
312
|
inStandbyMode: null
|
|
313
313
|
};
|
|
314
314
|
method = 'PUT';
|
|
315
|
-
path =
|
|
315
|
+
path = ApiUrls.Home.Put.Ata.replace('deviceid', deviceData.DeviceID);
|
|
316
316
|
break;
|
|
317
317
|
}
|
|
318
318
|
|
package/src/melcloudatw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, HeatPump } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudAtw extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -204,7 +204,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
204
204
|
case 'account':
|
|
205
205
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
206
206
|
payload = { data: flagData.LoginData };
|
|
207
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
207
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
208
208
|
await this.functions.saveData(this.accountFile, flagData);
|
|
209
209
|
break;
|
|
210
210
|
default:
|
|
@@ -231,7 +231,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
231
231
|
ProhibitHotWater: deviceData.Device.ProhibitHotWater,
|
|
232
232
|
HasPendingCommand: true
|
|
233
233
|
}
|
|
234
|
-
path = ApiUrls.
|
|
234
|
+
path = ApiUrls.Post.Atw;
|
|
235
235
|
update = true;
|
|
236
236
|
break;
|
|
237
237
|
}
|
|
@@ -255,7 +255,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
255
255
|
units: { ATA: [deviceData.DeviceID] }
|
|
256
256
|
};
|
|
257
257
|
method = 'POST';
|
|
258
|
-
path =
|
|
258
|
+
path = ApiUrls.Home.Post.ProtectionFrost;
|
|
259
259
|
update = true;
|
|
260
260
|
break;
|
|
261
261
|
case 'holidaymode':
|
|
@@ -266,17 +266,17 @@ class MelCloudAtw extends EventEmitter {
|
|
|
266
266
|
units: { ATW: [deviceData.DeviceID] }
|
|
267
267
|
};
|
|
268
268
|
method = 'POST';
|
|
269
|
-
path =
|
|
269
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
270
270
|
break;
|
|
271
271
|
case 'schedule':
|
|
272
272
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
273
273
|
method = 'PUT';
|
|
274
|
-
path =
|
|
274
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
275
275
|
update = true;
|
|
276
276
|
break;
|
|
277
277
|
case 'scene':
|
|
278
278
|
method = 'PUT';
|
|
279
|
-
path =
|
|
279
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
280
280
|
break;
|
|
281
281
|
default:
|
|
282
282
|
payload = {
|
|
@@ -295,7 +295,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
295
295
|
ecoHotWater: deviceData.Device.EcoHotWater,
|
|
296
296
|
};
|
|
297
297
|
method = 'PUT';
|
|
298
|
-
path =
|
|
298
|
+
path = ApiUrls.Home.Put.Atw.replace('deviceid', deviceData.DeviceID);
|
|
299
299
|
break
|
|
300
300
|
}
|
|
301
301
|
|
package/src/melclouderv.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, Ventilation } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudErv extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -189,7 +189,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
189
189
|
case 'account':
|
|
190
190
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
191
191
|
payload = { data: flagData.LoginData };
|
|
192
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
192
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
193
193
|
await this.functions.saveData(this.accountFile, flagData);
|
|
194
194
|
break;
|
|
195
195
|
default:
|
|
@@ -231,7 +231,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
231
231
|
NightPurgeMode: deviceData.Device.NightPurgeMode,
|
|
232
232
|
HasPendingCommand: true
|
|
233
233
|
}
|
|
234
|
-
path = ApiUrls.
|
|
234
|
+
path = ApiUrls.Post.Erv;
|
|
235
235
|
update = true;
|
|
236
236
|
break;
|
|
237
237
|
}
|
|
@@ -255,17 +255,17 @@ class MelCloudErv extends EventEmitter {
|
|
|
255
255
|
units: { ERV: [deviceData.DeviceID] }
|
|
256
256
|
};
|
|
257
257
|
method = 'POST';
|
|
258
|
-
path =
|
|
258
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
259
259
|
break;
|
|
260
260
|
case 'schedule':
|
|
261
261
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
262
262
|
method = 'PUT';
|
|
263
|
-
path =
|
|
263
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
264
264
|
update = true;
|
|
265
265
|
break;
|
|
266
266
|
case 'scene':
|
|
267
267
|
method = 'PUT';
|
|
268
|
-
path =
|
|
268
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
269
269
|
break;
|
|
270
270
|
default:
|
|
271
271
|
if (displayType === 1 && deviceData.Device.VentilationMode === 2) {
|
|
@@ -288,7 +288,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
288
288
|
ventilationMode: Ventilation.VentilationModeMapEnumToString[deviceData.Device.VentilationMode],
|
|
289
289
|
};
|
|
290
290
|
method = 'PUT';
|
|
291
|
-
path =
|
|
291
|
+
path = ApiUrls.Home.Put.Erv.replace('deviceid', deviceData.DeviceID);
|
|
292
292
|
break
|
|
293
293
|
}
|
|
294
294
|
|
package/src/melcloudhome.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import axios from 'axios';
|
|
3
2
|
import WebSocket from 'ws';
|
|
4
3
|
import { exec } from 'child_process';
|
|
@@ -7,7 +6,7 @@ import EventEmitter from 'events';
|
|
|
7
6
|
import puppeteer from 'puppeteer';
|
|
8
7
|
import ImpulseGenerator from './impulsegenerator.js';
|
|
9
8
|
import Functions from './functions.js';
|
|
10
|
-
import {
|
|
9
|
+
import { ApiUrls, LanguageLocaleMap } from './constants.js';
|
|
11
10
|
const execPromise = promisify(exec);
|
|
12
11
|
|
|
13
12
|
class MelCloudHome extends EventEmitter {
|
|
@@ -17,7 +16,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
17
16
|
this.user = account.user;
|
|
18
17
|
this.passwd = account.passwd;
|
|
19
18
|
this.language = account.language;
|
|
20
|
-
this.logSuccess = account.log?.success;
|
|
21
19
|
this.logWarn = account.log?.warn;
|
|
22
20
|
this.logError = account.log?.error;
|
|
23
21
|
this.logDebug = account.log?.debug;
|
|
@@ -79,7 +77,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
79
77
|
async checkScenesList() {
|
|
80
78
|
try {
|
|
81
79
|
if (this.logDebug) this.emit('debug', `Scanning for scenes`);
|
|
82
|
-
const listScenesData = await this.client(
|
|
80
|
+
const listScenesData = await this.client(ApiUrls.Home.Get.Scenes, { method: 'GET', });
|
|
83
81
|
|
|
84
82
|
const scenesList = listScenesData.data;
|
|
85
83
|
if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
|
|
@@ -111,7 +109,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
111
109
|
try {
|
|
112
110
|
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
113
111
|
if (this.logDebug) this.emit('debug', `Scanning for devices`);
|
|
114
|
-
const listDevicesData = await this.client(
|
|
112
|
+
const listDevicesData = await this.client(ApiUrls.Home.Get.ListDevices, { method: 'GET' });
|
|
115
113
|
|
|
116
114
|
const userContext = listDevicesData.data;
|
|
117
115
|
const buildings = userContext.buildings ?? [];
|
|
@@ -225,32 +223,36 @@ class MelCloudHome extends EventEmitter {
|
|
|
225
223
|
try {
|
|
226
224
|
const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
|
|
227
225
|
|
|
228
|
-
// Get Chromium path
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
226
|
+
// Get Chromium path from resolver
|
|
227
|
+
const chromiumInfo = await this.functions.ensureChromiumInstalled();
|
|
228
|
+
let chromiumPath = chromiumInfo.path;
|
|
229
|
+
const arch = chromiumInfo.arch;
|
|
230
|
+
const system = chromiumInfo.system;
|
|
231
|
+
|
|
232
|
+
// If path is found, use it
|
|
233
|
+
if (chromiumPath) {
|
|
234
|
+
if (!this.logDebug) this.emit('debug', `Using Chromium for ${system} (${arch}) at ${chromiumPath}`);
|
|
235
|
+
} else {
|
|
236
|
+
if (arch === 'arm') {
|
|
237
|
+
accountInfo.Info = `No Chromium found for ${system} (${arch}). Please install it manually and try again.`;
|
|
238
|
+
return accountInfo;
|
|
239
|
+
} else {
|
|
240
|
+
try {
|
|
241
|
+
chromiumPath = puppeteer.executablePath();
|
|
242
|
+
if (!this.logDebug) this.emit('debug', `Using Puppeteer Chromium for ${system} (${arch}) at ${chromiumPath}`);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
accountInfo.Info = `No Puppeteer Chromium for ${system} (${arch}), error: ${error.message}`;
|
|
240
245
|
return accountInfo;
|
|
241
246
|
}
|
|
242
|
-
} catch (error) {
|
|
243
|
-
accountInfo.Info = `Failed to get Puppeteer Chromium path: ${error.message}`;
|
|
244
|
-
return accountInfo;
|
|
245
247
|
}
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
// Verify Chromium executable
|
|
249
251
|
try {
|
|
250
252
|
const { stdout } = await execPromise(`"${chromiumPath}" --version`);
|
|
251
|
-
if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
|
|
253
|
+
if (!this.logDebug) this.emit('debug', `Chromium for ${system} (${arch}) detected: ${stdout.trim()}`);
|
|
252
254
|
} catch (error) {
|
|
253
|
-
accountInfo.Info = `Chromium found at ${chromiumPath}, but
|
|
255
|
+
accountInfo.Info = `Chromium for ${system} (${arch}) found at ${chromiumPath}, but execute error: ${error.message}. Please install it manually and try again.`;
|
|
254
256
|
return accountInfo;
|
|
255
257
|
}
|
|
256
258
|
|
|
@@ -282,22 +284,22 @@ class MelCloudHome extends EventEmitter {
|
|
|
282
284
|
await client.send('Network.enable')
|
|
283
285
|
client.on('Network.webSocketCreated', ({ url }) => {
|
|
284
286
|
try {
|
|
285
|
-
if (url.startsWith(`${
|
|
287
|
+
if (url.startsWith(`${ApiUrls.Home.WebSocket}`)) {
|
|
286
288
|
const params = new URL(url).searchParams;
|
|
287
289
|
const hash = params.get('hash');
|
|
288
290
|
if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
|
|
289
291
|
|
|
290
|
-
//
|
|
292
|
+
// Web socket connection
|
|
291
293
|
if (!this.connecting && !this.socketConnected) {
|
|
292
294
|
this.connecting = true;
|
|
293
295
|
|
|
294
296
|
try {
|
|
295
297
|
const headers = {
|
|
296
|
-
'Origin':
|
|
298
|
+
'Origin': ApiUrls.Home.Base,
|
|
297
299
|
'Pragma': 'no-cache',
|
|
298
300
|
'Cache-Control': 'no-cache'
|
|
299
301
|
};
|
|
300
|
-
const webSocket = new WebSocket(`${
|
|
302
|
+
const webSocket = new WebSocket(`${ApiUrls.Home.WebSocket}${hash}`, { headers: headers })
|
|
301
303
|
.on('error', (error) => {
|
|
302
304
|
if (this.logError) this.emit('error', `Web socket error: ${error}`);
|
|
303
305
|
try {
|
|
@@ -326,7 +328,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
326
328
|
})
|
|
327
329
|
.on('message', (message) => {
|
|
328
330
|
const parsedMessage = JSON.parse(message);
|
|
329
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
331
|
+
if (!this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
330
332
|
if (parsedMessage.message === 'Forbidden') return;
|
|
331
333
|
|
|
332
334
|
this.emit('webSocket', parsedMessage);
|
|
@@ -343,9 +345,9 @@ class MelCloudHome extends EventEmitter {
|
|
|
343
345
|
});
|
|
344
346
|
|
|
345
347
|
try {
|
|
346
|
-
await page.goto(
|
|
348
|
+
await page.goto(ApiUrls.Home.Base, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: GLOBAL_TIMEOUT });
|
|
347
349
|
} catch (error) {
|
|
348
|
-
accountInfo.Info = `Navigation to ${
|
|
350
|
+
accountInfo.Info = `Navigation to ${ApiUrls.Home.Base} failed: ${error.message}`;
|
|
349
351
|
return accountInfo;
|
|
350
352
|
}
|
|
351
353
|
|
|
@@ -407,7 +409,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
407
409
|
'Accept-Language': LanguageLocaleMap[this.language],
|
|
408
410
|
'Cookie': cookies,
|
|
409
411
|
'Priority': 'u=3, i',
|
|
410
|
-
'Referer':
|
|
412
|
+
'Referer': ApiUrls.Home.Dashboard,
|
|
411
413
|
'Sec-Fetch-Dest': 'empty',
|
|
412
414
|
'Sec-Fetch-Mode': 'cors',
|
|
413
415
|
'Sec-Fetch-Site': 'same-origin',
|
|
@@ -416,10 +418,10 @@ class MelCloudHome extends EventEmitter {
|
|
|
416
418
|
};
|
|
417
419
|
|
|
418
420
|
this.client = axios.create({
|
|
419
|
-
baseURL:
|
|
421
|
+
baseURL: ApiUrls.Home.Base,
|
|
420
422
|
timeout: 30000,
|
|
421
423
|
headers: headers
|
|
422
|
-
})
|
|
424
|
+
});
|
|
423
425
|
this.emit('client', this.client);
|
|
424
426
|
|
|
425
427
|
accountInfo.State = true;
|