homebridge-melcloud-control 4.3.5-beta.6 → 4.3.5-beta.7
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 -6
- package/package.json +1 -1
- package/src/deviceata.js +2 -13
- package/src/deviceatw.js +3 -14
- package/src/deviceerv.js +3 -13
- package/src/melcloud.js +1 -2
- package/src/melcloudata.js +4 -128
- package/src/melcloudatw.js +60 -121
- package/src/melclouderv.js +13 -44
- package/src/melcloudhome.js +8 -4
package/index.js
CHANGED
|
@@ -179,15 +179,15 @@ class MelCloudPlatform {
|
|
|
179
179
|
let configuredDevice;
|
|
180
180
|
switch (deviceType) {
|
|
181
181
|
case 0: //ATA
|
|
182
|
-
configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
182
|
+
configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
|
|
183
183
|
break;
|
|
184
184
|
case 1: //ATW
|
|
185
|
-
configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
185
|
+
configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
|
|
186
186
|
break;
|
|
187
187
|
case 2:
|
|
188
188
|
break;
|
|
189
189
|
case 3: //ERV
|
|
190
|
-
configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
190
|
+
configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
|
|
191
191
|
break;
|
|
192
192
|
default:
|
|
193
193
|
if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeString}, ${deviceName}, unknown device: ${deviceType}.`);
|
|
@@ -205,9 +205,6 @@ class MelCloudPlatform {
|
|
|
205
205
|
if (accessory) {
|
|
206
206
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
207
207
|
if (logLevel.success) log.success(`${accountName}, ${deviceTypeString}, ${deviceName}, Published as external accessory.`);
|
|
208
|
-
|
|
209
|
-
//start impulse generators for device
|
|
210
|
-
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
|
|
211
208
|
}
|
|
212
209
|
}
|
|
213
210
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.3.5-beta.
|
|
4
|
+
"version": "4.3.5-beta.7",
|
|
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/deviceata.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, AirConditioner } from './constants.js';
|
|
|
7
7
|
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
8
8
|
|
|
9
9
|
class DeviceAta extends EventEmitter {
|
|
10
|
-
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
10
|
+
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -18,7 +18,6 @@ class DeviceAta extends EventEmitter {
|
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
20
|
this.melcloud = melcloud;
|
|
21
|
-
this.webSocket = webSocket;
|
|
22
21
|
this.account = account;
|
|
23
22
|
this.accountType = account.type;
|
|
24
23
|
this.accountName = account.name;
|
|
@@ -109,16 +108,6 @@ class DeviceAta extends EventEmitter {
|
|
|
109
108
|
this.accessory = {};
|
|
110
109
|
};
|
|
111
110
|
|
|
112
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
113
|
-
try {
|
|
114
|
-
//start impulse generator
|
|
115
|
-
await this.melCloudAta.impulseGenerator.state(state, timers)
|
|
116
|
-
return true;
|
|
117
|
-
} catch (error) {
|
|
118
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
111
|
async externalIntegrations() {
|
|
123
112
|
//RESTFul server
|
|
124
113
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1332,7 +1321,7 @@ class DeviceAta extends EventEmitter {
|
|
|
1332
1321
|
async start() {
|
|
1333
1322
|
try {
|
|
1334
1323
|
//melcloud device
|
|
1335
|
-
this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.
|
|
1324
|
+
this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1336
1325
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1337
1326
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1338
1327
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
package/src/deviceatw.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, HeatPump } from './constants.js';
|
|
|
7
7
|
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
8
8
|
|
|
9
9
|
class DeviceAtw extends EventEmitter {
|
|
10
|
-
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
10
|
+
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,7 +17,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
-
this.
|
|
20
|
+
this.melcloud = melcloud;
|
|
21
21
|
this.account = account;
|
|
22
22
|
this.accountType = account.type;
|
|
23
23
|
this.accountName = account.name;
|
|
@@ -112,17 +112,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
112
112
|
this.accessory = {};
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
116
|
-
try {
|
|
117
|
-
//start impulse generator
|
|
118
|
-
await this.melCloudAtw.impulseGenerator.state(state, timers)
|
|
119
|
-
return true;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
115
|
async externalIntegrations() {
|
|
127
116
|
//RESTFul server
|
|
128
117
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1578,7 +1567,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1578
1567
|
async start() {
|
|
1579
1568
|
try {
|
|
1580
1569
|
//melcloud device
|
|
1581
|
-
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.
|
|
1570
|
+
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1582
1571
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, supportsHotWaterTank, supportsZone2) => {
|
|
1583
1572
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1584
1573
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
package/src/deviceerv.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, Ventilation } from './constants.js';
|
|
|
7
7
|
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
|
|
8
8
|
|
|
9
9
|
class DeviceErv extends EventEmitter {
|
|
10
|
-
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile,
|
|
10
|
+
constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,7 +17,7 @@ class DeviceErv extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
-
this.
|
|
20
|
+
this.melcloud = melcloud;
|
|
21
21
|
this.account = account;
|
|
22
22
|
this.accountType = account.type;
|
|
23
23
|
this.accountName = account.name;
|
|
@@ -104,16 +104,6 @@ class DeviceErv extends EventEmitter {
|
|
|
104
104
|
this.accessory = {};
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
108
|
-
try {
|
|
109
|
-
//start impulse generator
|
|
110
|
-
await this.melCloudErv.impulseGenerator.state(state, timers)
|
|
111
|
-
return true;
|
|
112
|
-
} catch (error) {
|
|
113
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
107
|
async externalIntegrations() {
|
|
118
108
|
//RESTFul server
|
|
119
109
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1131,7 +1121,7 @@ class DeviceErv extends EventEmitter {
|
|
|
1131
1121
|
async start() {
|
|
1132
1122
|
try {
|
|
1133
1123
|
//melcloud device
|
|
1134
|
-
this.melCloudErv = new MelCloudErv(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.
|
|
1124
|
+
this.melCloudErv = new MelCloudErv(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1135
1125
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1136
1126
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1137
1127
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
package/src/melcloud.js
CHANGED
|
@@ -110,12 +110,11 @@ class MelCloud extends EventEmitter {
|
|
|
110
110
|
devicesList.Info = `Found ${devicesCount} devices`;
|
|
111
111
|
devicesList.Devices = devices;
|
|
112
112
|
devicesList.Headers = this.headers;
|
|
113
|
+
this.emit('devicesList', devicesList);
|
|
113
114
|
|
|
114
115
|
await this.functions.saveData(this.devicesFile, devicesList);
|
|
115
116
|
if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
|
|
116
117
|
|
|
117
|
-
this.emit('devicesList', devicesList);
|
|
118
|
-
|
|
119
118
|
return devicesList;
|
|
120
119
|
} catch (error) {
|
|
121
120
|
throw new Error(`Check devices list error: ${error.message}`);
|
package/src/melcloudata.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import EventEmitter from 'events';
|
|
3
|
-
import ImpulseGenerator from './impulsegenerator.js';
|
|
4
3
|
import Functions from './functions.js';
|
|
5
4
|
import { ApiUrls, ApiUrlsHome, AirConditioner } from './constants.js';
|
|
6
5
|
|
|
7
6
|
class MelCloudAta extends EventEmitter {
|
|
8
|
-
constructor(account, device, devicesFile, defaultTempsFile, accountFile,
|
|
7
|
+
constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
|
|
9
8
|
super();
|
|
10
9
|
this.accountType = account.type;
|
|
11
10
|
this.logSuccess = account.log?.success;
|
|
@@ -18,7 +17,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
18
17
|
this.devicesFile = devicesFile;
|
|
19
18
|
this.defaultTempsFile = defaultTempsFile;
|
|
20
19
|
this.accountFile = accountFile;
|
|
21
|
-
this.webSocket = webSocket;
|
|
22
20
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
23
21
|
.on('warn', warn => this.emit('warn', warn))
|
|
24
22
|
.on('error', error => this.emit('error', error))
|
|
@@ -42,7 +40,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
42
40
|
//web cocket message
|
|
43
41
|
if (this.accountType === 'melcloudhome') {
|
|
44
42
|
try {
|
|
45
|
-
webSocket.on('message', async (message) => {
|
|
43
|
+
melcloud.webSocket.on('message', async (message) => {
|
|
46
44
|
const parsedMessage = JSON.parse(message);
|
|
47
45
|
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
48
46
|
if (parsedMessage.message === 'Forbidden') return;
|
|
@@ -104,29 +102,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
104
102
|
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
|
-
|
|
108
|
-
//lock flag
|
|
109
|
-
this.locks = false;
|
|
110
|
-
this.impulseGenerator = new ImpulseGenerator()
|
|
111
|
-
.on('checkState', () => this.handleWithLock(async () => {
|
|
112
|
-
//await this.checkState();
|
|
113
|
-
}))
|
|
114
|
-
.on('state', (state) => {
|
|
115
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async handleWithLock(fn) {
|
|
120
|
-
if (this.locks) return;
|
|
121
|
-
|
|
122
|
-
this.locks = true;
|
|
123
|
-
try {
|
|
124
|
-
await fn();
|
|
125
|
-
} catch (error) {
|
|
126
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
127
|
-
} finally {
|
|
128
|
-
this.locks = false;
|
|
129
|
-
}
|
|
130
105
|
}
|
|
131
106
|
|
|
132
107
|
async updateState(deviceData) {
|
|
@@ -144,7 +119,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
144
119
|
deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
|
|
145
120
|
|
|
146
121
|
}
|
|
147
|
-
if (
|
|
122
|
+
if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
|
|
148
123
|
|
|
149
124
|
//device
|
|
150
125
|
const serialNumber = deviceData.SerialNumber || '4.0.0';
|
|
@@ -202,95 +177,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
202
177
|
};
|
|
203
178
|
};
|
|
204
179
|
|
|
205
|
-
async checkState() {
|
|
206
|
-
try {
|
|
207
|
-
//read device info from file
|
|
208
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
209
|
-
if (!devicesData) return;
|
|
210
|
-
|
|
211
|
-
this.headers = devicesData.Headers;
|
|
212
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
213
|
-
if (!deviceData) return;
|
|
214
|
-
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
215
|
-
|
|
216
|
-
//web cocket message
|
|
217
|
-
if (this.accountType === 'melcloudhome' && !this.firstRun) {
|
|
218
|
-
this.firstRun = false;
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
this.webSocket.on('message', async (message) => {
|
|
222
|
-
const parsedMessage = JSON.parse(message);
|
|
223
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
224
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
225
|
-
|
|
226
|
-
const messageData = parsedMessage?.[0]?.Data;
|
|
227
|
-
if (!messageData) return;
|
|
228
|
-
|
|
229
|
-
let updateState = false;
|
|
230
|
-
const unitId = messageData?.id;
|
|
231
|
-
switch (unitId) {
|
|
232
|
-
case this.deviceId:
|
|
233
|
-
if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
234
|
-
const messageType = parsedMessage[0].messageType;
|
|
235
|
-
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
236
|
-
switch (messageType) {
|
|
237
|
-
case 'unitStateChanged':
|
|
238
|
-
|
|
239
|
-
//update values
|
|
240
|
-
for (const [key, value] of Object.entries(settings)) {
|
|
241
|
-
if (!this.functions.isValidValue(value)) continue;
|
|
242
|
-
|
|
243
|
-
//update holiday mode
|
|
244
|
-
if (key === 'HolidayMode') {
|
|
245
|
-
deviceData.HolidayMode.Enabled = value;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
//update device settings
|
|
250
|
-
if (key in deviceData.Device) {
|
|
251
|
-
deviceData.Device[key] = value;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
updateState = true;
|
|
255
|
-
break;
|
|
256
|
-
case 'unitHolidayModeTriggered':
|
|
257
|
-
deviceData.Device.Power = settings.Power;
|
|
258
|
-
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
259
|
-
deviceData.HolidayMode.Active = messageData.active;
|
|
260
|
-
updateState = true;
|
|
261
|
-
break;
|
|
262
|
-
case 'unitWifiSignalChanged':
|
|
263
|
-
deviceData.Rssi = messageData.rssi;
|
|
264
|
-
updateState = true;
|
|
265
|
-
break;
|
|
266
|
-
default:
|
|
267
|
-
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
break;
|
|
271
|
-
default:
|
|
272
|
-
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
//update state
|
|
277
|
-
if (updateState) await this.updateState(deviceData);
|
|
278
|
-
});
|
|
279
|
-
} catch (error) {
|
|
280
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
281
|
-
this.cleanupSocket();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
//update state
|
|
286
|
-
await this.updateState(deviceData);
|
|
287
|
-
|
|
288
|
-
return true;
|
|
289
|
-
} catch (error) {
|
|
290
|
-
throw new Error(`Check state error: ${error.message}`);
|
|
291
|
-
};
|
|
292
|
-
};
|
|
293
|
-
|
|
294
180
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
295
181
|
try {
|
|
296
182
|
let method = null
|
|
@@ -342,7 +228,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
342
228
|
headers: headers,
|
|
343
229
|
data: payload
|
|
344
230
|
});
|
|
345
|
-
|
|
231
|
+
|
|
346
232
|
return true;
|
|
347
233
|
case "melcloudhome":
|
|
348
234
|
switch (flag) {
|
|
@@ -433,7 +319,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
433
319
|
data: payload
|
|
434
320
|
});
|
|
435
321
|
|
|
436
|
-
this.updateData(deviceData, updateState);
|
|
437
322
|
return true;
|
|
438
323
|
default:
|
|
439
324
|
return;
|
|
@@ -443,14 +328,5 @@ class MelCloudAta extends EventEmitter {
|
|
|
443
328
|
throw new Error(`Send data error: ${error.message}`);
|
|
444
329
|
}
|
|
445
330
|
}
|
|
446
|
-
|
|
447
|
-
updateData(deviceData, updateState = true) {
|
|
448
|
-
this.locks = true;
|
|
449
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
450
|
-
|
|
451
|
-
setTimeout(() => {
|
|
452
|
-
this.locks = false
|
|
453
|
-
}, 5000);
|
|
454
|
-
}
|
|
455
331
|
};
|
|
456
332
|
export default MelCloudAta;
|
package/src/melcloudatw.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import WebSocket from 'ws';
|
|
2
1
|
import axios from 'axios';
|
|
3
2
|
import EventEmitter from 'events';
|
|
4
|
-
import ImpulseGenerator from './impulsegenerator.js';
|
|
5
3
|
import Functions from './functions.js';
|
|
6
4
|
import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
|
|
7
5
|
|
|
8
6
|
class MelCloudAtw extends EventEmitter {
|
|
9
|
-
constructor(account, device, devicesFile, defaultTempsFile, accountFile,
|
|
7
|
+
constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -27,44 +25,68 @@ class MelCloudAtw extends EventEmitter {
|
|
|
27
25
|
//set default values
|
|
28
26
|
this.deviceData = {};
|
|
29
27
|
this.headers = {};
|
|
30
|
-
this.firstRun = true;
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.on('state', (state) => {
|
|
39
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async handleWithLock(fn) {
|
|
44
|
-
if (this.locks) return;
|
|
45
|
-
|
|
46
|
-
this.locks = true;
|
|
47
|
-
try {
|
|
48
|
-
await fn();
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
51
|
-
} finally {
|
|
52
|
-
this.locks = false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
29
|
+
let deviceData = null;
|
|
30
|
+
melcloud.on('devicesList', async (devicesData) => {
|
|
31
|
+
this.headers = devicesData.Headers;
|
|
32
|
+
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
33
|
+
if (!deviceData) return;
|
|
34
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
55
35
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
36
|
+
//update state
|
|
37
|
+
await this.updateState(deviceData);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
///web cocket message
|
|
41
|
+
if (this.accountType === 'melcloudhome') {
|
|
42
|
+
try {
|
|
43
|
+
melcloud.webSocket.on('message', async (message) => {
|
|
44
|
+
const parsedMessage = JSON.parse(message);
|
|
45
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
46
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
47
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
48
|
+
|
|
49
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
50
|
+
if (!messageData || !deviceData) return;
|
|
51
|
+
|
|
52
|
+
let updateState = false;
|
|
53
|
+
const unitId = messageData?.id;
|
|
54
|
+
switch (unitId) {
|
|
55
|
+
case this.deviceId:
|
|
56
|
+
const messageType = parsedMessage[0].messageType;
|
|
57
|
+
switch (messageType) {
|
|
58
|
+
case 'unitStateChanged':
|
|
59
|
+
const settings = Object.fromEntries(
|
|
60
|
+
messageData.settings.map(({ name, value }) => {
|
|
61
|
+
let parsedValue = this.functions.convertValue(value);
|
|
62
|
+
return [name, parsedValue];
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
Object.assign(deviceData.Device, settings);
|
|
66
|
+
updateState = true;
|
|
67
|
+
break;
|
|
68
|
+
case 'unitWifiSignalChanged':
|
|
69
|
+
deviceData.Rssi = messageData.rssi;
|
|
70
|
+
updateState = true;
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
//update state
|
|
83
|
+
if (updateState) await this.updateState(deviceData);
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
87
|
+
this.cleanupSocket();
|
|
88
|
+
}
|
|
65
89
|
}
|
|
66
|
-
|
|
67
|
-
this.socketConnected = false;
|
|
68
90
|
}
|
|
69
91
|
|
|
70
92
|
async updateState(deviceData) {
|
|
@@ -134,79 +156,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
134
156
|
};
|
|
135
157
|
};
|
|
136
158
|
|
|
137
|
-
async checkState() {
|
|
138
|
-
try {
|
|
139
|
-
//read device info from file
|
|
140
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
141
|
-
if (!devicesData) return;
|
|
142
|
-
|
|
143
|
-
this.headers = devicesData.Headers;
|
|
144
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
145
|
-
if (!deviceData) return;
|
|
146
|
-
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
147
|
-
|
|
148
|
-
///web cocket message
|
|
149
|
-
if (this.accountType === 'melcloudhome' && !this.firstRun) {
|
|
150
|
-
this.firstRun = false;
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
this.webSocket.on('message', async (message) => {
|
|
154
|
-
const parsedMessage = JSON.parse(message);
|
|
155
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
156
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
157
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
158
|
-
|
|
159
|
-
const messageData = parsedMessage?.[0]?.Data;
|
|
160
|
-
if (!messageData) return;
|
|
161
|
-
|
|
162
|
-
let updateState = false;
|
|
163
|
-
const unitId = messageData?.id;
|
|
164
|
-
switch (unitId) {
|
|
165
|
-
case this.deviceId:
|
|
166
|
-
const messageType = parsedMessage[0].messageType;
|
|
167
|
-
switch (messageType) {
|
|
168
|
-
case 'unitStateChanged':
|
|
169
|
-
const settings = Object.fromEntries(
|
|
170
|
-
messageData.settings.map(({ name, value }) => {
|
|
171
|
-
let parsedValue = this.functions.convertValue(value);
|
|
172
|
-
return [name, parsedValue];
|
|
173
|
-
})
|
|
174
|
-
);
|
|
175
|
-
Object.assign(deviceData.Device, settings);
|
|
176
|
-
updateState = true;
|
|
177
|
-
break;
|
|
178
|
-
case 'unitWifiSignalChanged':
|
|
179
|
-
deviceData.Rssi = messageData.rssi;
|
|
180
|
-
updateState = true;
|
|
181
|
-
break;
|
|
182
|
-
default:
|
|
183
|
-
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
break;
|
|
187
|
-
default:
|
|
188
|
-
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
//update state
|
|
193
|
-
if (updateState) await this.updateState(deviceData);
|
|
194
|
-
});
|
|
195
|
-
} catch (error) {
|
|
196
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
197
|
-
this.cleanupSocket();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
//update state
|
|
202
|
-
await this.updateState(deviceData);
|
|
203
|
-
|
|
204
|
-
return true;
|
|
205
|
-
} catch (error) {
|
|
206
|
-
throw new Error(`Check state error: ${error.message}`);
|
|
207
|
-
};
|
|
208
|
-
};
|
|
209
|
-
|
|
210
159
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
211
160
|
try {
|
|
212
161
|
|
|
@@ -273,7 +222,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
273
222
|
headers: headers,
|
|
274
223
|
data: payload
|
|
275
224
|
});
|
|
276
|
-
|
|
225
|
+
|
|
277
226
|
return true;
|
|
278
227
|
case "melcloudhome":
|
|
279
228
|
switch (flag) {
|
|
@@ -333,7 +282,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
333
282
|
data: payload
|
|
334
283
|
});
|
|
335
284
|
|
|
336
|
-
this.updateData(deviceData, updateState);
|
|
337
285
|
return true;
|
|
338
286
|
default:
|
|
339
287
|
return;
|
|
@@ -343,14 +291,5 @@ class MelCloudAtw extends EventEmitter {
|
|
|
343
291
|
throw new Error(`Send data error: ${error.message}`);
|
|
344
292
|
}
|
|
345
293
|
}
|
|
346
|
-
|
|
347
|
-
updateData(deviceData, updateState = true) {
|
|
348
|
-
this.locks = true;
|
|
349
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
350
|
-
|
|
351
|
-
setTimeout(() => {
|
|
352
|
-
this.locks = false
|
|
353
|
-
}, 5000);
|
|
354
|
-
}
|
|
355
294
|
};
|
|
356
295
|
export default MelCloudAtw;
|
package/src/melclouderv.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import WebSocket from 'ws';
|
|
2
1
|
import axios from 'axios';
|
|
3
2
|
import EventEmitter from 'events';
|
|
4
|
-
import ImpulseGenerator from './impulsegenerator.js';
|
|
5
3
|
import Functions from './functions.js';
|
|
6
4
|
import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
|
|
7
5
|
|
|
8
6
|
class MelCloudErv extends EventEmitter {
|
|
9
|
-
constructor(account, device, devicesFile, defaultTempsFile, accountFile,
|
|
7
|
+
constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -27,41 +25,21 @@ class MelCloudErv extends EventEmitter {
|
|
|
27
25
|
//set default values
|
|
28
26
|
this.deviceData = {};
|
|
29
27
|
this.headers = {};
|
|
30
|
-
this.firstRun = true;
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.on('state', (state) => {
|
|
39
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async handleWithLock(fn) {
|
|
44
|
-
if (this.locks) return;
|
|
29
|
+
let deviceData = null;
|
|
30
|
+
melcloud.on('devicesList', async (devicesData) => {
|
|
31
|
+
this.headers = devicesData.Headers;
|
|
32
|
+
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
33
|
+
if (!deviceData) return;
|
|
34
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
51
|
-
} finally {
|
|
52
|
-
this.locks = false;
|
|
53
|
-
}
|
|
36
|
+
//update state
|
|
37
|
+
await this.updateState(deviceData);
|
|
38
|
+
}); this.firstRun = true;
|
|
54
39
|
}
|
|
55
40
|
|
|
56
|
-
async
|
|
41
|
+
async updateState(deviceData) {
|
|
57
42
|
try {
|
|
58
|
-
//read device info from file
|
|
59
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
60
|
-
if (!devicesData) return;
|
|
61
|
-
|
|
62
|
-
this.headers = devicesData.Headers;
|
|
63
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
64
|
-
if (!deviceData) return;
|
|
65
43
|
if (this.accountType === 'melcloudhome') {
|
|
66
44
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
67
45
|
|
|
@@ -195,7 +173,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
195
173
|
headers: headers,
|
|
196
174
|
data: payload
|
|
197
175
|
});
|
|
198
|
-
|
|
176
|
+
|
|
199
177
|
return true;
|
|
200
178
|
case "melcloudhome":
|
|
201
179
|
switch (flag) {
|
|
@@ -258,7 +236,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
258
236
|
headers: headers,
|
|
259
237
|
data: payload
|
|
260
238
|
});
|
|
261
|
-
|
|
239
|
+
|
|
262
240
|
return true;
|
|
263
241
|
default:
|
|
264
242
|
return;
|
|
@@ -268,14 +246,5 @@ class MelCloudErv extends EventEmitter {
|
|
|
268
246
|
throw new Error(`Send data error: ${error.message}`);
|
|
269
247
|
}
|
|
270
248
|
}
|
|
271
|
-
|
|
272
|
-
updateData(deviceData, updateState = true) {
|
|
273
|
-
this.locks = true;
|
|
274
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
275
|
-
|
|
276
|
-
setTimeout(() => {
|
|
277
|
-
this.locks = false
|
|
278
|
-
}, 5000);
|
|
279
|
-
}
|
|
280
249
|
};
|
|
281
250
|
export default MelCloudErv;
|
package/src/melcloudhome.js
CHANGED
|
@@ -242,13 +242,18 @@ class MelCloudHome extends EventEmitter {
|
|
|
242
242
|
// heartbeat
|
|
243
243
|
this.heartbeat = setInterval(() => {
|
|
244
244
|
if (this.socket.readyState === this.socket.OPEN) {
|
|
245
|
-
if (
|
|
245
|
+
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
246
246
|
this.socket.ping();
|
|
247
247
|
}
|
|
248
248
|
}, 30000);
|
|
249
249
|
})
|
|
250
250
|
.on('pong', () => {
|
|
251
|
-
if (
|
|
251
|
+
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
252
|
+
})
|
|
253
|
+
.on('message', (message) => {
|
|
254
|
+
const parsedMessage = JSON.parse(message);
|
|
255
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
256
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
252
257
|
});
|
|
253
258
|
} catch (error) {
|
|
254
259
|
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
@@ -262,12 +267,11 @@ class MelCloudHome extends EventEmitter {
|
|
|
262
267
|
devicesList.Scenes = scenes;
|
|
263
268
|
devicesList.Headers = this.headers;
|
|
264
269
|
devicesList.WebSocket = this.socket;
|
|
270
|
+
this.emit('devicesList', devicesList);
|
|
265
271
|
|
|
266
272
|
await this.functions.saveData(this.devicesFile, devicesList);
|
|
267
273
|
if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
|
|
268
274
|
|
|
269
|
-
this.emit('devicesList', devicesList);
|
|
270
|
-
|
|
271
275
|
return devicesList;
|
|
272
276
|
} catch (error) {
|
|
273
277
|
throw new Error(`Check devices list error: ${error.message}`);
|