homebridge-melcloud-control 4.3.5-beta.3 → 4.3.5-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/index.js +18 -23
- package/package.json +1 -1
- package/src/deviceata.js +6 -15
- package/src/deviceatw.js +6 -15
- package/src/deviceerv.js +6 -14
- package/src/melcloud.js +4 -6
- package/src/melcloudata.js +72 -134
- package/src/melcloudatw.js +66 -142
- package/src/melclouderv.js +83 -45
- package/src/melcloudhome.js +14 -9
package/index.js
CHANGED
|
@@ -74,7 +74,6 @@ class MelCloudPlatform {
|
|
|
74
74
|
//define directory and file paths
|
|
75
75
|
const accountFile = `${prefDir}/${accountName}_Account`;
|
|
76
76
|
const buildingsFile = `${prefDir}/${accountName}_Buildings`;
|
|
77
|
-
const devicesFile = `${prefDir}/${accountName}_Devices`;
|
|
78
77
|
|
|
79
78
|
//set account refresh interval
|
|
80
79
|
const refreshInterval = (account.refreshInterval ?? 120) * 1000
|
|
@@ -85,22 +84,22 @@ class MelCloudPlatform {
|
|
|
85
84
|
.on('start', async () => {
|
|
86
85
|
try {
|
|
87
86
|
//melcloud account
|
|
88
|
-
let
|
|
87
|
+
let melcloud;
|
|
89
88
|
let timmers = []
|
|
90
89
|
switch (account.type) {
|
|
91
90
|
case 'melcloud':
|
|
92
91
|
timmers = [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
93
|
-
|
|
92
|
+
melcloud = new MelCloud(account, accountFile, buildingsFile, true);
|
|
94
93
|
break;
|
|
95
94
|
case 'melcloudhome':
|
|
96
95
|
timmers = [{ name: 'connect', sampling: 3300000 }, { name: 'checkDevicesList', sampling: 3000 }];
|
|
97
|
-
|
|
96
|
+
melcloud = new MelCloudHome(account, accountFile, buildingsFile, true);
|
|
98
97
|
break;
|
|
99
98
|
default:
|
|
100
99
|
if (logLevel.warn) log.warn(`Unknown account type: ${account.type}.`);
|
|
101
100
|
return;
|
|
102
101
|
}
|
|
103
|
-
|
|
102
|
+
melcloud.on('success', (msg) => log.success(`${accountName}, ${msg}`))
|
|
104
103
|
.on('info', (msg) => log.info(`${accountName}, ${msg}`))
|
|
105
104
|
.on('debug', (msg) => log.info(`${accountName}, debug: ${msg}`))
|
|
106
105
|
.on('warn', (msg) => log.warn(`${accountName}, ${msg}`))
|
|
@@ -109,7 +108,7 @@ class MelCloudPlatform {
|
|
|
109
108
|
//connect
|
|
110
109
|
let accountInfo;
|
|
111
110
|
try {
|
|
112
|
-
accountInfo = await
|
|
111
|
+
accountInfo = await melcloud.connect();
|
|
113
112
|
if (!accountInfo.State) {
|
|
114
113
|
if (logLevel.warn) log.warn(`${accountName}, ${accountInfo.Info}`);
|
|
115
114
|
return;
|
|
@@ -118,21 +117,24 @@ class MelCloudPlatform {
|
|
|
118
117
|
if (logLevel.error) log.error(`${accountName}, Connect error: ${error.message ?? error}`);
|
|
119
118
|
return;
|
|
120
119
|
}
|
|
121
|
-
if (logLevel.success) log.success(accountInfo.Info);
|
|
120
|
+
if (logLevel.success) log.success(`${accountName}, ${accountInfo.Info}`);
|
|
122
121
|
|
|
123
122
|
//check devices list
|
|
124
|
-
let
|
|
123
|
+
let melcloudDevicesList;
|
|
125
124
|
try {
|
|
126
|
-
|
|
127
|
-
if (!
|
|
128
|
-
if (logLevel.warn) log.warn(`${accountName}, ${
|
|
125
|
+
melcloudDevicesList = await melcloud.checkDevicesList();
|
|
126
|
+
if (!melcloudDevicesList.State) {
|
|
127
|
+
if (logLevel.warn) log.warn(`${accountName}, ${melcloudDevicesList.Info}`);
|
|
129
128
|
return;
|
|
130
129
|
}
|
|
131
130
|
} catch (error) {
|
|
132
131
|
if (logLevel.error) log.error(`${accountName}, Check devices list error: ${error.message ?? error}`);
|
|
133
132
|
return;
|
|
134
133
|
}
|
|
135
|
-
if (logLevel.debug) log.info(
|
|
134
|
+
if (logLevel.debug) log.info(melcloudDevicesList.Info);
|
|
135
|
+
|
|
136
|
+
//start account impulse generator
|
|
137
|
+
await melcloud.impulseGenerator.state(true, timmers, false);
|
|
136
138
|
|
|
137
139
|
//configured devices
|
|
138
140
|
const ataDevices = (account.ataDevices || []).filter(device => device.id != null && String(device.id) !== '0');
|
|
@@ -144,14 +146,13 @@ class MelCloudPlatform {
|
|
|
144
146
|
for (const [index, device] of devices.entries()) {
|
|
145
147
|
//chack device from config exist on melcloud
|
|
146
148
|
const displayType = device.displayType > 0;
|
|
147
|
-
const deviceExistInMelCloud =
|
|
149
|
+
const deviceExistInMelCloud = melcloudDevicesList.Devices.some(dev => dev.DeviceID === device.id);
|
|
148
150
|
if (!deviceExistInMelCloud || !displayType) continue;
|
|
149
151
|
|
|
150
152
|
device.id = String(device.id);
|
|
151
153
|
const deviceName = device.name;
|
|
152
154
|
const deviceType = device.type;
|
|
153
155
|
const deviceTypeString = device.typeString;
|
|
154
|
-
const deviceRefreshInterval = (device.refreshInterval ?? 5) * 1000;
|
|
155
156
|
const defaultTempsFile = `${prefDir}/${accountName}_${device.id}_Temps`;
|
|
156
157
|
|
|
157
158
|
// set rest ful port
|
|
@@ -179,15 +180,15 @@ class MelCloudPlatform {
|
|
|
179
180
|
let configuredDevice;
|
|
180
181
|
switch (deviceType) {
|
|
181
182
|
case 0: //ATA
|
|
182
|
-
configuredDevice = new DeviceAta(api, account, device,
|
|
183
|
+
configuredDevice = new DeviceAta(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList);
|
|
183
184
|
break;
|
|
184
185
|
case 1: //ATW
|
|
185
|
-
configuredDevice = new DeviceAtw(api, account, device,
|
|
186
|
+
configuredDevice = new DeviceAtw(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList);
|
|
186
187
|
break;
|
|
187
188
|
case 2:
|
|
188
189
|
break;
|
|
189
190
|
case 3: //ERV
|
|
190
|
-
configuredDevice = new DeviceErv(api, account, device,
|
|
191
|
+
configuredDevice = new DeviceErv(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList);
|
|
191
192
|
break;
|
|
192
193
|
default:
|
|
193
194
|
if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeString}, ${deviceName}, unknown device: ${deviceType}.`);
|
|
@@ -205,15 +206,9 @@ class MelCloudPlatform {
|
|
|
205
206
|
if (accessory) {
|
|
206
207
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
207
208
|
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
209
|
}
|
|
212
210
|
}
|
|
213
211
|
|
|
214
|
-
//start account impulse generator
|
|
215
|
-
await configureAccount.impulseGenerator.state(true, timmers, false);
|
|
216
|
-
|
|
217
212
|
//stop start impulse generator
|
|
218
213
|
await impulseGenerator.state(false);
|
|
219
214
|
} catch (error) {
|
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.30",
|
|
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,
|
|
10
|
+
constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,7 +17,8 @@ class DeviceAta extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
-
this.
|
|
20
|
+
this.melcloud = melcloud;
|
|
21
|
+
this.melcloudDevicesList = melcloudDevicesList;
|
|
21
22
|
this.account = account;
|
|
22
23
|
this.accountType = account.type;
|
|
23
24
|
this.accountName = account.name;
|
|
@@ -49,7 +50,6 @@ class DeviceAta extends EventEmitter {
|
|
|
49
50
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
50
51
|
|
|
51
52
|
//files
|
|
52
|
-
this.devicesFile = devicesFile;
|
|
53
53
|
this.defaultTempsFile = defaultTempsFile;
|
|
54
54
|
this.accountInfo = accountInfo;
|
|
55
55
|
this.accountFile = accountFile;
|
|
@@ -108,16 +108,6 @@ class DeviceAta extends EventEmitter {
|
|
|
108
108
|
this.accessory = {};
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
112
|
-
try {
|
|
113
|
-
//start impulse generator
|
|
114
|
-
await this.melCloudAta.impulseGenerator.state(state, timers)
|
|
115
|
-
return true;
|
|
116
|
-
} catch (error) {
|
|
117
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
111
|
async externalIntegrations() {
|
|
122
112
|
//RESTFul server
|
|
123
113
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1331,7 +1321,7 @@ class DeviceAta extends EventEmitter {
|
|
|
1331
1321
|
async start() {
|
|
1332
1322
|
try {
|
|
1333
1323
|
//melcloud device
|
|
1334
|
-
this.melCloudAta = new MelCloudAta(this.account, this.device, this.
|
|
1324
|
+
this.melCloudAta = new MelCloudAta(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1335
1325
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1336
1326
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1337
1327
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -1888,9 +1878,10 @@ class DeviceAta extends EventEmitter {
|
|
|
1888
1878
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
1889
1879
|
|
|
1890
1880
|
//check state
|
|
1891
|
-
await this.melCloudAta.checkState();
|
|
1881
|
+
await this.melCloudAta.checkState(this.melcloudDevicesList);
|
|
1892
1882
|
|
|
1893
1883
|
//prepare accessory
|
|
1884
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
1894
1885
|
const accessory = await this.prepareAccessory();
|
|
1895
1886
|
return accessory;
|
|
1896
1887
|
} catch (error) {
|
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,
|
|
10
|
+
constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,6 +17,8 @@ class DeviceAtw extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
+
this.melcloud = melcloud;
|
|
21
|
+
this.melcloudDevicesList = melcloudDevicesList;
|
|
20
22
|
this.account = account;
|
|
21
23
|
this.accountType = account.type;
|
|
22
24
|
this.accountName = account.name;
|
|
@@ -52,7 +54,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
52
54
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
53
55
|
|
|
54
56
|
//files
|
|
55
|
-
this.devicesFile = devicesFile;
|
|
56
57
|
this.defaultTempsFile = defaultTempsFile;
|
|
57
58
|
this.accountInfo = accountInfo;
|
|
58
59
|
this.accountFile = accountFile;
|
|
@@ -111,17 +112,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
111
112
|
this.accessory = {};
|
|
112
113
|
};
|
|
113
114
|
|
|
114
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
115
|
-
try {
|
|
116
|
-
//start impulse generator
|
|
117
|
-
await this.melCloudAtw.impulseGenerator.state(state, timers)
|
|
118
|
-
return true;
|
|
119
|
-
} catch (error) {
|
|
120
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
115
|
async externalIntegrations() {
|
|
126
116
|
//RESTFul server
|
|
127
117
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1577,7 +1567,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1577
1567
|
async start() {
|
|
1578
1568
|
try {
|
|
1579
1569
|
//melcloud device
|
|
1580
|
-
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.
|
|
1570
|
+
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1581
1571
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, supportsHotWaterTank, supportsZone2) => {
|
|
1582
1572
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1583
1573
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -2317,9 +2307,10 @@ class DeviceAtw extends EventEmitter {
|
|
|
2317
2307
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
2318
2308
|
|
|
2319
2309
|
//check state
|
|
2320
|
-
await this.
|
|
2310
|
+
await this.melCloudAta.checkState(this.melcloudDevicesList);
|
|
2321
2311
|
|
|
2322
2312
|
//prepare accessory
|
|
2313
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
2323
2314
|
const accessory = await this.prepareAccessory();
|
|
2324
2315
|
return accessory;
|
|
2325
2316
|
} catch (error) {
|
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,
|
|
10
|
+
constructor(api, account, device, defaultTempsFile, accountInfo, accountFile, melcloud, melcloudDevicesList) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,6 +17,8 @@ class DeviceErv extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
+
this.melcloud = melcloud;
|
|
21
|
+
this.melcloudDevicesList = melcloudDevicesList;
|
|
20
22
|
this.account = account;
|
|
21
23
|
this.accountType = account.type;
|
|
22
24
|
this.accountName = account.name;
|
|
@@ -44,7 +46,6 @@ class DeviceErv extends EventEmitter {
|
|
|
44
46
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
45
47
|
|
|
46
48
|
//files
|
|
47
|
-
this.devicesFile = devicesFile;
|
|
48
49
|
this.defaultTempsFile = defaultTempsFile;
|
|
49
50
|
this.accountInfo = accountInfo;
|
|
50
51
|
this.accountFile = accountFile;
|
|
@@ -103,16 +104,6 @@ class DeviceErv extends EventEmitter {
|
|
|
103
104
|
this.accessory = {};
|
|
104
105
|
};
|
|
105
106
|
|
|
106
|
-
async startStopImpulseGenerator(state, timers = []) {
|
|
107
|
-
try {
|
|
108
|
-
//start impulse generator
|
|
109
|
-
await this.melCloudErv.impulseGenerator.state(state, timers)
|
|
110
|
-
return true;
|
|
111
|
-
} catch (error) {
|
|
112
|
-
throw new Error(`Impulse generator start error: ${error}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
107
|
async externalIntegrations() {
|
|
117
108
|
//RESTFul server
|
|
118
109
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1130,7 +1121,7 @@ class DeviceErv extends EventEmitter {
|
|
|
1130
1121
|
async start() {
|
|
1131
1122
|
try {
|
|
1132
1123
|
//melcloud device
|
|
1133
|
-
this.melCloudErv = new MelCloudErv(this.account, this.device, this.
|
|
1124
|
+
this.melCloudErv = new MelCloudErv(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1134
1125
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1135
1126
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1136
1127
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -1590,9 +1581,10 @@ class DeviceErv extends EventEmitter {
|
|
|
1590
1581
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
1591
1582
|
|
|
1592
1583
|
//check state
|
|
1593
|
-
await this.
|
|
1584
|
+
await this.melCloudAta.checkState(this.melcloudDevicesList);
|
|
1594
1585
|
|
|
1595
1586
|
//prepare accessory
|
|
1587
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
1596
1588
|
const accessory = await this.prepareAccessory();
|
|
1597
1589
|
return accessory;
|
|
1598
1590
|
} catch (error) {
|
package/src/melcloud.js
CHANGED
|
@@ -5,7 +5,7 @@ import Functions from './functions.js';
|
|
|
5
5
|
import { ApiUrls } from './constants.js';
|
|
6
6
|
|
|
7
7
|
class MelCloud extends EventEmitter {
|
|
8
|
-
constructor(account, accountFile, buildingsFile,
|
|
8
|
+
constructor(account, accountFile, buildingsFile, pluginStart = false) {
|
|
9
9
|
super();
|
|
10
10
|
this.accountType = account.type;
|
|
11
11
|
this.user = account.user;
|
|
@@ -14,9 +14,9 @@ class MelCloud extends EventEmitter {
|
|
|
14
14
|
this.logWarn = account.log?.warn;
|
|
15
15
|
this.logError = account.log?.error;
|
|
16
16
|
this.logDebug = account.log?.debug;
|
|
17
|
+
|
|
17
18
|
this.accountFile = accountFile;
|
|
18
19
|
this.buildingsFile = buildingsFile;
|
|
19
|
-
this.devicesFile = devicesFile;
|
|
20
20
|
this.headers = {};
|
|
21
21
|
|
|
22
22
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
@@ -110,9 +110,7 @@ class MelCloud extends EventEmitter {
|
|
|
110
110
|
devicesList.Info = `Found ${devicesCount} devices`;
|
|
111
111
|
devicesList.Devices = devices;
|
|
112
112
|
devicesList.Headers = this.headers;
|
|
113
|
-
|
|
114
|
-
await this.functions.saveData(this.devicesFile, devicesList);
|
|
115
|
-
if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
|
|
113
|
+
this.emit('devicesList', devicesList);
|
|
116
114
|
|
|
117
115
|
return devicesList;
|
|
118
116
|
} catch (error) {
|
|
@@ -173,7 +171,7 @@ class MelCloud extends EventEmitter {
|
|
|
173
171
|
});
|
|
174
172
|
|
|
175
173
|
accountInfo.State = true;
|
|
176
|
-
accountInfo.Info = 'Connect
|
|
174
|
+
accountInfo.Info = 'Connect Success';
|
|
177
175
|
accountInfo.UseFahrenheit = loginData.UseFahrenheit;
|
|
178
176
|
accountInfo.Account = account;
|
|
179
177
|
await this.functions.saveData(this.accountFile, accountInfo);
|
package/src/melcloudata.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, AirConditioner } from './constants.js';
|
|
7
5
|
|
|
8
6
|
class MelCloudAta extends EventEmitter {
|
|
9
|
-
constructor(account, device,
|
|
7
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,10 +14,9 @@ class MelCloudAta extends EventEmitter {
|
|
|
16
14
|
this.restFulEnabled = account.restFul?.enable;
|
|
17
15
|
this.mqttEnabled = account.mqtt?.enable;
|
|
18
16
|
this.deviceId = device.id;
|
|
19
|
-
this.devicesFile = devicesFile;
|
|
20
17
|
this.defaultTempsFile = defaultTempsFile;
|
|
21
18
|
this.accountFile = accountFile;
|
|
22
|
-
|
|
19
|
+
|
|
23
20
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
24
21
|
.on('warn', warn => this.emit('warn', warn))
|
|
25
22
|
.on('error', error => this.emit('error', error))
|
|
@@ -27,48 +24,74 @@ class MelCloudAta extends EventEmitter {
|
|
|
27
24
|
|
|
28
25
|
//set default values
|
|
29
26
|
this.deviceData = {};
|
|
30
|
-
this.headers = {}
|
|
31
|
-
this.socket = null;
|
|
32
|
-
this.connecting = false;
|
|
33
|
-
this.socketConnected = false;
|
|
34
|
-
this.heartbeat = null;
|
|
35
|
-
|
|
36
|
-
//lock flag
|
|
37
|
-
this.locks = false;
|
|
38
|
-
this.impulseGenerator = new ImpulseGenerator()
|
|
39
|
-
.on('checkState', () => this.handleWithLock(async () => {
|
|
40
|
-
await this.checkState();
|
|
41
|
-
}))
|
|
42
|
-
.on('state', (state) => {
|
|
43
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async handleWithLock(fn) {
|
|
48
|
-
if (this.locks) return;
|
|
27
|
+
this.headers = {}
|
|
49
28
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.locks = false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
cleanupSocket() {
|
|
61
|
-
if (this.heartbeat) {
|
|
62
|
-
clearInterval(this.heartbeat);
|
|
63
|
-
this.heartbeat = null;
|
|
64
|
-
}
|
|
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 ?? [];
|
|
65
35
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
36
|
+
//update state
|
|
37
|
+
await this.updateState(deviceData);
|
|
38
|
+
}).on('webSocket', async (parsedMessage) => {
|
|
39
|
+
try {
|
|
40
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
41
|
+
if (!messageData || !deviceData) return;
|
|
42
|
+
|
|
43
|
+
let updateState = false;
|
|
44
|
+
const unitId = messageData?.id;
|
|
45
|
+
switch (unitId) {
|
|
46
|
+
case this.deviceId:
|
|
47
|
+
const messageType = parsedMessage[0].messageType;
|
|
48
|
+
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
49
|
+
switch (messageType) {
|
|
50
|
+
case 'unitStateChanged':
|
|
51
|
+
|
|
52
|
+
//update values
|
|
53
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
54
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
55
|
+
|
|
56
|
+
//update holiday mode
|
|
57
|
+
if (key === 'HolidayMode') {
|
|
58
|
+
deviceData.HolidayMode.Enabled = value;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//update device settings
|
|
63
|
+
if (key in deviceData.Device) {
|
|
64
|
+
deviceData.Device[key] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
updateState = true;
|
|
68
|
+
break;
|
|
69
|
+
case 'unitHolidayModeTriggered':
|
|
70
|
+
deviceData.Device.Power = settings.Power;
|
|
71
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
72
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
73
|
+
updateState = true;
|
|
74
|
+
break;
|
|
75
|
+
case 'unitWifiSignalChanged':
|
|
76
|
+
deviceData.Rssi = messageData.rssi;
|
|
77
|
+
updateState = true;
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
//update state
|
|
90
|
+
if (updateState) await this.updateState(deviceData);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
72
95
|
}
|
|
73
96
|
|
|
74
97
|
async updateState(deviceData) {
|
|
@@ -144,94 +167,18 @@ class MelCloudAta extends EventEmitter {
|
|
|
144
167
|
};
|
|
145
168
|
};
|
|
146
169
|
|
|
147
|
-
async checkState() {
|
|
170
|
+
async checkState(devicesData) {
|
|
148
171
|
try {
|
|
149
|
-
//read device info from file
|
|
150
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
151
|
-
if (!devicesData) return;
|
|
152
|
-
|
|
153
172
|
this.headers = devicesData.Headers;
|
|
154
173
|
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
155
|
-
if (!deviceData) return;
|
|
156
174
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
157
|
-
|
|
158
|
-
//web cocket connection
|
|
159
|
-
if (this.accountType === 'melcloudhome' && !this.connecting) {
|
|
160
|
-
this.connecting = true;
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
this.webSocket.on('message', async (message) => {
|
|
164
|
-
const parsedMessage = JSON.parse(message);
|
|
165
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
166
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
167
|
-
|
|
168
|
-
const messageData = parsedMessage?.[0]?.Data;
|
|
169
|
-
if (!messageData) return;
|
|
170
|
-
|
|
171
|
-
let updateState = false;
|
|
172
|
-
const unitId = messageData?.id;
|
|
173
|
-
switch (unitId) {
|
|
174
|
-
case this.deviceId:
|
|
175
|
-
if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
176
|
-
const messageType = parsedMessage[0].messageType;
|
|
177
|
-
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
178
|
-
switch (messageType) {
|
|
179
|
-
case 'unitStateChanged':
|
|
180
|
-
|
|
181
|
-
//update values
|
|
182
|
-
for (const [key, value] of Object.entries(settings)) {
|
|
183
|
-
if (!this.functions.isValidValue(value)) continue;
|
|
184
|
-
|
|
185
|
-
//update holiday mode
|
|
186
|
-
if (key === 'HolidayMode') {
|
|
187
|
-
deviceData.HolidayMode.Enabled = value;
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
//update device settings
|
|
192
|
-
if (key in deviceData.Device) {
|
|
193
|
-
deviceData.Device[key] = value;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
updateState = true;
|
|
197
|
-
break;
|
|
198
|
-
case 'unitHolidayModeTriggered':
|
|
199
|
-
deviceData.Device.Power = settings.Power;
|
|
200
|
-
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
201
|
-
deviceData.HolidayMode.Active = messageData.active;
|
|
202
|
-
updateState = true;
|
|
203
|
-
break;
|
|
204
|
-
case 'unitWifiSignalChanged':
|
|
205
|
-
deviceData.Rssi = messageData.rssi;
|
|
206
|
-
updateState = true;
|
|
207
|
-
break;
|
|
208
|
-
default:
|
|
209
|
-
if (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
break;
|
|
213
|
-
default:
|
|
214
|
-
if (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
//update state
|
|
219
|
-
if (updateState) await this.updateState(deviceData);
|
|
220
|
-
});
|
|
221
|
-
} catch (error) {
|
|
222
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
223
|
-
this.cleanupSocket();
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
//update state
|
|
228
175
|
await this.updateState(deviceData);
|
|
229
176
|
|
|
230
177
|
return true;
|
|
231
178
|
} catch (error) {
|
|
232
|
-
throw new Error(`
|
|
179
|
+
throw new Error(`Chaeck state error: ${error.message}`);
|
|
233
180
|
};
|
|
234
|
-
}
|
|
181
|
+
}
|
|
235
182
|
|
|
236
183
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
237
184
|
try {
|
|
@@ -284,7 +231,8 @@ class MelCloudAta extends EventEmitter {
|
|
|
284
231
|
headers: headers,
|
|
285
232
|
data: payload
|
|
286
233
|
});
|
|
287
|
-
|
|
234
|
+
|
|
235
|
+
this.emit('deviceState', deviceData);
|
|
288
236
|
return true;
|
|
289
237
|
case "melcloudhome":
|
|
290
238
|
switch (flag) {
|
|
@@ -375,7 +323,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
375
323
|
data: payload
|
|
376
324
|
});
|
|
377
325
|
|
|
378
|
-
this.updateData(deviceData, updateState);
|
|
379
326
|
return true;
|
|
380
327
|
default:
|
|
381
328
|
return;
|
|
@@ -385,14 +332,5 @@ class MelCloudAta extends EventEmitter {
|
|
|
385
332
|
throw new Error(`Send data error: ${error.message}`);
|
|
386
333
|
}
|
|
387
334
|
}
|
|
388
|
-
|
|
389
|
-
updateData(deviceData, updateState = true) {
|
|
390
|
-
this.locks = true;
|
|
391
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
392
|
-
|
|
393
|
-
setTimeout(() => {
|
|
394
|
-
this.locks = false
|
|
395
|
-
}, 5000);
|
|
396
|
-
}
|
|
397
335
|
};
|
|
398
336
|
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,
|
|
7
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,9 +14,9 @@ class MelCloudAtw extends EventEmitter {
|
|
|
16
14
|
this.restFulEnabled = account.restFul?.enable;
|
|
17
15
|
this.mqttEnabled = account.mqtt?.enable;
|
|
18
16
|
this.deviceId = device.id;
|
|
19
|
-
this.devicesFile = devicesFile;
|
|
20
17
|
this.defaultTempsFile = defaultTempsFile;
|
|
21
18
|
this.accountFile = accountFile;
|
|
19
|
+
|
|
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))
|
|
@@ -27,47 +25,69 @@ class MelCloudAtw extends EventEmitter {
|
|
|
27
25
|
//set default values
|
|
28
26
|
this.deviceData = {};
|
|
29
27
|
this.headers = {};
|
|
30
|
-
this.socket = null;
|
|
31
|
-
this.connecting = false;
|
|
32
|
-
this.socketConnected = false;
|
|
33
|
-
this.heartbeat = null;
|
|
34
|
-
|
|
35
|
-
//lock flag
|
|
36
|
-
this.locks = false;
|
|
37
|
-
this.impulseGenerator = new ImpulseGenerator()
|
|
38
|
-
.on('checkState', () => this.handleWithLock(async () => {
|
|
39
|
-
await this.checkState();
|
|
40
|
-
}))
|
|
41
|
-
.on('state', (state) => {
|
|
42
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} catch (error) {
|
|
53
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
54
|
-
} finally {
|
|
55
|
-
this.locks = false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
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 ?? [];
|
|
58
35
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
36
|
+
//update state
|
|
37
|
+
await this.updateState(deviceData);
|
|
38
|
+
}).on('message', async (message) => {
|
|
39
|
+
try {
|
|
40
|
+
const parsedMessage = JSON.parse(message);
|
|
41
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
42
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
43
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
44
|
+
|
|
45
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
46
|
+
if (!messageData || !deviceData) return;
|
|
47
|
+
|
|
48
|
+
let updateState = false;
|
|
49
|
+
const unitId = messageData?.id;
|
|
50
|
+
switch (unitId) {
|
|
51
|
+
case this.deviceId:
|
|
52
|
+
const messageType = parsedMessage[0].messageType;
|
|
53
|
+
switch (messageType) {
|
|
54
|
+
case 'unitStateChanged':
|
|
55
|
+
const settings = Object.fromEntries(
|
|
56
|
+
messageData.settings.map(({ name, value }) => {
|
|
57
|
+
let parsedValue = this.functions.convertValue(value);
|
|
58
|
+
return [name, parsedValue];
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
Object.assign(deviceData.Device, settings);
|
|
62
|
+
updateState = true;
|
|
63
|
+
break;
|
|
64
|
+
case 'unitHolidayModeTriggered':
|
|
65
|
+
deviceData.Device.Power = settings.Power;
|
|
66
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
67
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
68
|
+
updateState = true;
|
|
69
|
+
break;
|
|
70
|
+
case 'unitWifiSignalChanged':
|
|
71
|
+
deviceData.Rssi = messageData.rssi;
|
|
72
|
+
updateState = true;
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
//update state
|
|
85
|
+
if (updateState) await this.updateState(deviceData);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
69
90
|
|
|
70
|
-
this.socketConnected = false;
|
|
71
91
|
}
|
|
72
92
|
|
|
73
93
|
async updateState(deviceData) {
|
|
@@ -137,105 +157,18 @@ class MelCloudAtw extends EventEmitter {
|
|
|
137
157
|
};
|
|
138
158
|
};
|
|
139
159
|
|
|
140
|
-
async checkState() {
|
|
160
|
+
async checkState(devicesData) {
|
|
141
161
|
try {
|
|
142
|
-
//read device info from file
|
|
143
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
144
|
-
if (!devicesData) return;
|
|
145
|
-
|
|
146
162
|
this.headers = devicesData.Headers;
|
|
147
163
|
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
148
|
-
if (!deviceData) return;
|
|
149
164
|
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
150
|
-
|
|
151
|
-
//web cocket connection
|
|
152
|
-
if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
|
|
153
|
-
this.connecting = true;
|
|
154
|
-
|
|
155
|
-
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
156
|
-
try {
|
|
157
|
-
const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
|
|
158
|
-
.on('error', (error) => {
|
|
159
|
-
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
160
|
-
socket.close();
|
|
161
|
-
})
|
|
162
|
-
.on('close', () => {
|
|
163
|
-
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
164
|
-
this.cleanupSocket();
|
|
165
|
-
})
|
|
166
|
-
.on('open', () => {
|
|
167
|
-
this.socket = socket;
|
|
168
|
-
this.socketConnected = true;
|
|
169
|
-
this.connecting = false;
|
|
170
|
-
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
171
|
-
|
|
172
|
-
// heartbeat
|
|
173
|
-
this.heartbeat = setInterval(() => {
|
|
174
|
-
if (socket.readyState === socket.OPEN) {
|
|
175
|
-
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
176
|
-
socket.ping();
|
|
177
|
-
}
|
|
178
|
-
}, 30000);
|
|
179
|
-
})
|
|
180
|
-
.on('pong', () => {
|
|
181
|
-
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
182
|
-
})
|
|
183
|
-
.on('message', async (message) => {
|
|
184
|
-
const parsedMessage = JSON.parse(message);
|
|
185
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
186
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
187
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
188
|
-
|
|
189
|
-
const messageData = parsedMessage?.[0]?.Data;
|
|
190
|
-
if (!messageData) return;
|
|
191
|
-
|
|
192
|
-
let updateState = false;
|
|
193
|
-
const unitId = messageData?.id;
|
|
194
|
-
switch (unitId) {
|
|
195
|
-
case this.deviceId:
|
|
196
|
-
const messageType = parsedMessage[0].messageType;
|
|
197
|
-
switch (messageType) {
|
|
198
|
-
case 'unitStateChanged':
|
|
199
|
-
const settings = Object.fromEntries(
|
|
200
|
-
messageData.settings.map(({ name, value }) => {
|
|
201
|
-
let parsedValue = this.functions.convertValue(value);
|
|
202
|
-
return [name, parsedValue];
|
|
203
|
-
})
|
|
204
|
-
);
|
|
205
|
-
Object.assign(deviceData.Device, settings);
|
|
206
|
-
updateState = true;
|
|
207
|
-
break;
|
|
208
|
-
case 'unitWifiSignalChanged':
|
|
209
|
-
deviceData.Rssi = messageData.rssi;
|
|
210
|
-
updateState = true;
|
|
211
|
-
break;
|
|
212
|
-
default:
|
|
213
|
-
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
break;
|
|
217
|
-
default:
|
|
218
|
-
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
//update state
|
|
223
|
-
if (updateState) await this.updateState(deviceData);
|
|
224
|
-
});
|
|
225
|
-
} catch (error) {
|
|
226
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
227
|
-
this.cleanupSocket();
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
//update state
|
|
232
165
|
await this.updateState(deviceData);
|
|
233
166
|
|
|
234
167
|
return true;
|
|
235
168
|
} catch (error) {
|
|
236
|
-
throw new Error(`
|
|
169
|
+
throw new Error(`Chaeck state error: ${error.message}`);
|
|
237
170
|
};
|
|
238
|
-
}
|
|
171
|
+
}
|
|
239
172
|
|
|
240
173
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
241
174
|
try {
|
|
@@ -303,7 +236,8 @@ class MelCloudAtw extends EventEmitter {
|
|
|
303
236
|
headers: headers,
|
|
304
237
|
data: payload
|
|
305
238
|
});
|
|
306
|
-
|
|
239
|
+
|
|
240
|
+
this.emit('deviceState', deviceData);
|
|
307
241
|
return true;
|
|
308
242
|
case "melcloudhome":
|
|
309
243
|
switch (flag) {
|
|
@@ -363,7 +297,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
363
297
|
data: payload
|
|
364
298
|
});
|
|
365
299
|
|
|
366
|
-
this.updateData(deviceData, updateState);
|
|
367
300
|
return true;
|
|
368
301
|
default:
|
|
369
302
|
return;
|
|
@@ -373,14 +306,5 @@ class MelCloudAtw extends EventEmitter {
|
|
|
373
306
|
throw new Error(`Send data error: ${error.message}`);
|
|
374
307
|
}
|
|
375
308
|
}
|
|
376
|
-
|
|
377
|
-
updateData(deviceData, updateState = true) {
|
|
378
|
-
this.locks = true;
|
|
379
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
380
|
-
|
|
381
|
-
setTimeout(() => {
|
|
382
|
-
this.locks = false
|
|
383
|
-
}, 5000);
|
|
384
|
-
}
|
|
385
309
|
};
|
|
386
310
|
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,
|
|
7
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,9 +14,9 @@ class MelCloudErv extends EventEmitter {
|
|
|
16
14
|
this.restFulEnabled = account.restFul?.enable;
|
|
17
15
|
this.mqttEnabled = account.mqtt?.enable;
|
|
18
16
|
this.deviceId = device.id;
|
|
19
|
-
this.devicesFile = devicesFile;
|
|
20
17
|
this.defaultTempsFile = defaultTempsFile;
|
|
21
18
|
this.accountFile = accountFile;
|
|
19
|
+
|
|
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))
|
|
@@ -28,42 +26,77 @@ class MelCloudErv extends EventEmitter {
|
|
|
28
26
|
this.deviceData = {};
|
|
29
27
|
this.headers = {};
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.on('state', (state) => {
|
|
38
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
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 ?? [];
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
//update state
|
|
37
|
+
await this.updateState(deviceData);
|
|
38
|
+
}).on('webSocket', async (parsedMessage) => {
|
|
39
|
+
try {
|
|
40
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
41
|
+
if (!messageData || !deviceData) return;
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
let updateState = false;
|
|
44
|
+
const unitId = messageData?.id;
|
|
45
|
+
switch (unitId) {
|
|
46
|
+
case this.deviceId:
|
|
47
|
+
const messageType = parsedMessage[0].messageType;
|
|
48
|
+
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
49
|
+
switch (messageType) {
|
|
50
|
+
case 'unitStateChanged':
|
|
51
|
+
|
|
52
|
+
//update values
|
|
53
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
54
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
55
|
+
|
|
56
|
+
//update holiday mode
|
|
57
|
+
if (key === 'HolidayMode') {
|
|
58
|
+
deviceData.HolidayMode.Enabled = value;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//update device settings
|
|
63
|
+
if (key in deviceData.Device) {
|
|
64
|
+
deviceData.Device[key] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
updateState = true;
|
|
68
|
+
break;
|
|
69
|
+
case 'unitHolidayModeTriggered':
|
|
70
|
+
deviceData.Device.Power = settings.Power;
|
|
71
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
72
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
73
|
+
updateState = true;
|
|
74
|
+
break;
|
|
75
|
+
case 'unitWifiSignalChanged':
|
|
76
|
+
deviceData.Rssi = messageData.rssi;
|
|
77
|
+
updateState = true;
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//update state
|
|
90
|
+
if (updateState) await this.updateState(deviceData);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
53
95
|
}
|
|
54
96
|
|
|
55
|
-
async
|
|
97
|
+
async updateState(deviceData) {
|
|
56
98
|
try {
|
|
57
|
-
//read device info from file
|
|
58
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
59
|
-
if (!devicesData) return;
|
|
60
|
-
|
|
61
|
-
this.headers = devicesData.Headers;
|
|
62
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
63
|
-
if (!deviceData) return;
|
|
64
99
|
if (this.accountType === 'melcloudhome') {
|
|
65
|
-
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
66
|
-
|
|
67
100
|
//read default temps
|
|
68
101
|
const temps = await this.functions.readData(this.defaultTempsFile, true);
|
|
69
102
|
deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
|
|
@@ -127,6 +160,19 @@ class MelCloudErv extends EventEmitter {
|
|
|
127
160
|
};
|
|
128
161
|
};
|
|
129
162
|
|
|
163
|
+
async checkState(devicesData) {
|
|
164
|
+
try {
|
|
165
|
+
this.headers = devicesData.Headers;
|
|
166
|
+
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
167
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
168
|
+
await this.updateState(deviceData);
|
|
169
|
+
|
|
170
|
+
return true;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
throw new Error(`Chaeck state error: ${error.message}`);
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
130
176
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
131
177
|
try {
|
|
132
178
|
let method = null
|
|
@@ -194,7 +240,8 @@ class MelCloudErv extends EventEmitter {
|
|
|
194
240
|
headers: headers,
|
|
195
241
|
data: payload
|
|
196
242
|
});
|
|
197
|
-
|
|
243
|
+
|
|
244
|
+
this.emit('deviceState', deviceData);
|
|
198
245
|
return true;
|
|
199
246
|
case "melcloudhome":
|
|
200
247
|
switch (flag) {
|
|
@@ -257,7 +304,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
257
304
|
headers: headers,
|
|
258
305
|
data: payload
|
|
259
306
|
});
|
|
260
|
-
|
|
307
|
+
|
|
261
308
|
return true;
|
|
262
309
|
default:
|
|
263
310
|
return;
|
|
@@ -267,14 +314,5 @@ class MelCloudErv extends EventEmitter {
|
|
|
267
314
|
throw new Error(`Send data error: ${error.message}`);
|
|
268
315
|
}
|
|
269
316
|
}
|
|
270
|
-
|
|
271
|
-
updateData(deviceData, updateState = true) {
|
|
272
|
-
this.locks = true;
|
|
273
|
-
if (updateState) this.emit('deviceState', deviceData);
|
|
274
|
-
|
|
275
|
-
setTimeout(() => {
|
|
276
|
-
this.locks = false
|
|
277
|
-
}, 5000);
|
|
278
|
-
}
|
|
279
317
|
};
|
|
280
318
|
export default MelCloudErv;
|
package/src/melcloudhome.js
CHANGED
|
@@ -11,7 +11,7 @@ import { ApiUrlsHome, LanguageLocaleMap } from './constants.js';
|
|
|
11
11
|
const execPromise = promisify(exec);
|
|
12
12
|
|
|
13
13
|
class MelCloudHome extends EventEmitter {
|
|
14
|
-
constructor(account, accountFile, buildingsFile,
|
|
14
|
+
constructor(account, accountFile, buildingsFile, pluginStart = false) {
|
|
15
15
|
super();
|
|
16
16
|
this.accountType = account.type;
|
|
17
17
|
this.user = account.user;
|
|
@@ -23,7 +23,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
23
23
|
this.logDebug = account.log?.debug;
|
|
24
24
|
this.accountFile = accountFile;
|
|
25
25
|
this.buildingsFile = buildingsFile;
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
this.headers = {};
|
|
28
28
|
this.webSocketOptions = {};
|
|
29
29
|
this.socket = null;
|
|
@@ -242,13 +242,21 @@ 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}`);
|
|
257
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
258
|
+
|
|
259
|
+
this.emit('webSocket', parsedMessage);
|
|
252
260
|
});
|
|
253
261
|
} catch (error) {
|
|
254
262
|
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
@@ -261,10 +269,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
261
269
|
devicesList.Devices = devices;
|
|
262
270
|
devicesList.Scenes = scenes;
|
|
263
271
|
devicesList.Headers = this.headers;
|
|
264
|
-
devicesList
|
|
265
|
-
|
|
266
|
-
await this.functions.saveData(this.devicesFile, devicesList);
|
|
267
|
-
if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
|
|
272
|
+
this.emit('devicesList', devicesList);;
|
|
268
273
|
|
|
269
274
|
return devicesList;
|
|
270
275
|
} catch (error) {
|
|
@@ -436,7 +441,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
436
441
|
};
|
|
437
442
|
|
|
438
443
|
accountInfo.State = true;
|
|
439
|
-
accountInfo.Info = 'Connect
|
|
444
|
+
accountInfo.Info = 'Connect Success';
|
|
440
445
|
await this.functions.saveData(this.accountFile, accountInfo);
|
|
441
446
|
|
|
442
447
|
return accountInfo;
|