homebridge-melcloud-control 4.3.5-beta.2 → 4.3.5-beta.20
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 +4 -17
- package/src/deviceatw.js +4 -17
- package/src/deviceerv.js +4 -16
- package/src/melcloud.js +4 -6
- package/src/melcloudata.js +68 -142
- package/src/melcloudatw.js +62 -152
- package/src/melclouderv.js +69 -45
- package/src/melcloudhome.js +17 -12
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.20",
|
|
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) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,7 +17,7 @@ class DeviceAta 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;
|
|
@@ -49,7 +49,6 @@ class DeviceAta extends EventEmitter {
|
|
|
49
49
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
50
50
|
|
|
51
51
|
//files
|
|
52
|
-
this.devicesFile = devicesFile;
|
|
53
52
|
this.defaultTempsFile = defaultTempsFile;
|
|
54
53
|
this.accountInfo = accountInfo;
|
|
55
54
|
this.accountFile = accountFile;
|
|
@@ -108,16 +107,6 @@ class DeviceAta extends EventEmitter {
|
|
|
108
107
|
this.accessory = {};
|
|
109
108
|
};
|
|
110
109
|
|
|
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
110
|
async externalIntegrations() {
|
|
122
111
|
//RESTFul server
|
|
123
112
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1331,7 +1320,7 @@ class DeviceAta extends EventEmitter {
|
|
|
1331
1320
|
async start() {
|
|
1332
1321
|
try {
|
|
1333
1322
|
//melcloud device
|
|
1334
|
-
this.melCloudAta = new MelCloudAta(this.account, this.device, this.
|
|
1323
|
+
this.melCloudAta = new MelCloudAta(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1335
1324
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1336
1325
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1337
1326
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -1887,10 +1876,8 @@ class DeviceAta extends EventEmitter {
|
|
|
1887
1876
|
//start external integrations
|
|
1888
1877
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
1889
1878
|
|
|
1890
|
-
//check state
|
|
1891
|
-
await this.melCloudAta.checkState();
|
|
1892
|
-
|
|
1893
1879
|
//prepare accessory
|
|
1880
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
1894
1881
|
const accessory = await this.prepareAccessory();
|
|
1895
1882
|
return accessory;
|
|
1896
1883
|
} 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) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,6 +17,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
+
this.melcloud = melcloud;
|
|
20
21
|
this.account = account;
|
|
21
22
|
this.accountType = account.type;
|
|
22
23
|
this.accountName = account.name;
|
|
@@ -52,7 +53,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
52
53
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
53
54
|
|
|
54
55
|
//files
|
|
55
|
-
this.devicesFile = devicesFile;
|
|
56
56
|
this.defaultTempsFile = defaultTempsFile;
|
|
57
57
|
this.accountInfo = accountInfo;
|
|
58
58
|
this.accountFile = accountFile;
|
|
@@ -111,17 +111,6 @@ class DeviceAtw extends EventEmitter {
|
|
|
111
111
|
this.accessory = {};
|
|
112
112
|
};
|
|
113
113
|
|
|
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
114
|
async externalIntegrations() {
|
|
126
115
|
//RESTFul server
|
|
127
116
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1577,7 +1566,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1577
1566
|
async start() {
|
|
1578
1567
|
try {
|
|
1579
1568
|
//melcloud device
|
|
1580
|
-
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.
|
|
1569
|
+
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1581
1570
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, supportsHotWaterTank, supportsZone2) => {
|
|
1582
1571
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1583
1572
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -2316,10 +2305,8 @@ class DeviceAtw extends EventEmitter {
|
|
|
2316
2305
|
//start external integrations
|
|
2317
2306
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
2318
2307
|
|
|
2319
|
-
//check state
|
|
2320
|
-
await this.melCloudAtw.checkState();
|
|
2321
|
-
|
|
2322
2308
|
//prepare accessory
|
|
2309
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
2323
2310
|
const accessory = await this.prepareAccessory();
|
|
2324
2311
|
return accessory;
|
|
2325
2312
|
} 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) {
|
|
11
11
|
super();
|
|
12
12
|
|
|
13
13
|
Accessory = api.platformAccessory;
|
|
@@ -17,6 +17,7 @@ class DeviceErv extends EventEmitter {
|
|
|
17
17
|
AccessoryUUID = api.hap.uuid;
|
|
18
18
|
|
|
19
19
|
//account config
|
|
20
|
+
this.melcloud = melcloud;
|
|
20
21
|
this.account = account;
|
|
21
22
|
this.accountType = account.type;
|
|
22
23
|
this.accountName = account.name;
|
|
@@ -44,7 +45,6 @@ class DeviceErv extends EventEmitter {
|
|
|
44
45
|
this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
|
|
45
46
|
|
|
46
47
|
//files
|
|
47
|
-
this.devicesFile = devicesFile;
|
|
48
48
|
this.defaultTempsFile = defaultTempsFile;
|
|
49
49
|
this.accountInfo = accountInfo;
|
|
50
50
|
this.accountFile = accountFile;
|
|
@@ -103,16 +103,6 @@ class DeviceErv extends EventEmitter {
|
|
|
103
103
|
this.accessory = {};
|
|
104
104
|
};
|
|
105
105
|
|
|
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
106
|
async externalIntegrations() {
|
|
117
107
|
//RESTFul server
|
|
118
108
|
const restFulEnabled = this.restFul.enable || false;
|
|
@@ -1130,7 +1120,7 @@ class DeviceErv extends EventEmitter {
|
|
|
1130
1120
|
async start() {
|
|
1131
1121
|
try {
|
|
1132
1122
|
//melcloud device
|
|
1133
|
-
this.melCloudErv = new MelCloudErv(this.account, this.device, this.
|
|
1123
|
+
this.melCloudErv = new MelCloudErv(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1134
1124
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1135
1125
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1136
1126
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -1589,10 +1579,8 @@ class DeviceErv extends EventEmitter {
|
|
|
1589
1579
|
//start external integrations
|
|
1590
1580
|
if (this.restFul.enable || this.mqtt.enable) await this.externalIntegrations();
|
|
1591
1581
|
|
|
1592
|
-
//check state
|
|
1593
|
-
await this.melCloudErv.checkState();
|
|
1594
|
-
|
|
1595
1582
|
//prepare accessory
|
|
1583
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
1596
1584
|
const accessory = await this.prepareAccessory();
|
|
1597
1585
|
return accessory;
|
|
1598
1586
|
} 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, melcloudDevicesList) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,10 +14,8 @@ 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
|
-
this.webSocket = webSocket;
|
|
23
19
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
24
20
|
.on('warn', warn => this.emit('warn', warn))
|
|
25
21
|
.on('error', error => this.emit('error', error))
|
|
@@ -28,47 +24,75 @@ class MelCloudAta extends EventEmitter {
|
|
|
28
24
|
//set default values
|
|
29
25
|
this.deviceData = {};
|
|
30
26
|
this.headers = {};
|
|
31
|
-
this.socket = null;
|
|
32
|
-
this.connecting = false;
|
|
33
|
-
this.socketConnected = false;
|
|
34
|
-
this.heartbeat = null;
|
|
35
27
|
|
|
36
|
-
|
|
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;
|
|
49
|
-
|
|
50
|
-
this.locks = true;
|
|
51
|
-
try {
|
|
52
|
-
await fn();
|
|
53
|
-
} catch (error) {
|
|
54
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
55
|
-
} finally {
|
|
56
|
-
this.locks = false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
28
|
+
if (!this.logDebug) this.emit('debug', `MELCloud: ${melcloud}`);
|
|
59
29
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
30
|
+
let deviceData = null;
|
|
31
|
+
melcloud.on('devicesList', async (devicesData) => {
|
|
32
|
+
this.headers = devicesData.Headers;
|
|
33
|
+
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
34
|
+
if (!deviceData) return;
|
|
35
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
65
36
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
37
|
+
//update state
|
|
38
|
+
await this.updateState(deviceData);
|
|
39
|
+
}).on('webSocket', async (parsedMessage) => {
|
|
40
|
+
try {
|
|
41
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
42
|
+
if (!messageData || !deviceData) return;
|
|
43
|
+
|
|
44
|
+
let updateState = false;
|
|
45
|
+
const unitId = messageData?.id;
|
|
46
|
+
switch (unitId) {
|
|
47
|
+
case this.deviceId:
|
|
48
|
+
const messageType = parsedMessage[0].messageType;
|
|
49
|
+
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
50
|
+
switch (messageType) {
|
|
51
|
+
case 'unitStateChanged':
|
|
52
|
+
|
|
53
|
+
//update values
|
|
54
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
55
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
56
|
+
|
|
57
|
+
//update holiday mode
|
|
58
|
+
if (key === 'HolidayMode') {
|
|
59
|
+
deviceData.HolidayMode.Enabled = value;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//update device settings
|
|
64
|
+
if (key in deviceData.Device) {
|
|
65
|
+
deviceData.Device[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
updateState = true;
|
|
69
|
+
break;
|
|
70
|
+
case 'unitHolidayModeTriggered':
|
|
71
|
+
deviceData.Device.Power = settings.Power;
|
|
72
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
73
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
74
|
+
updateState = true;
|
|
75
|
+
break;
|
|
76
|
+
case 'unitWifiSignalChanged':
|
|
77
|
+
deviceData.Rssi = messageData.rssi;
|
|
78
|
+
updateState = true;
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
70
89
|
|
|
71
|
-
|
|
90
|
+
//update state
|
|
91
|
+
if (updateState) await this.updateState(deviceData);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
72
96
|
}
|
|
73
97
|
|
|
74
98
|
async updateState(deviceData) {
|
|
@@ -144,95 +168,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
144
168
|
};
|
|
145
169
|
};
|
|
146
170
|
|
|
147
|
-
async checkState() {
|
|
148
|
-
try {
|
|
149
|
-
//read device info from file
|
|
150
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
151
|
-
if (!devicesData) return;
|
|
152
|
-
|
|
153
|
-
this.headers = devicesData.Headers;
|
|
154
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
155
|
-
if (!deviceData) return;
|
|
156
|
-
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
|
-
await this.updateState(deviceData);
|
|
229
|
-
|
|
230
|
-
return true;
|
|
231
|
-
} catch (error) {
|
|
232
|
-
throw new Error(`Check state error: ${error.message}`);
|
|
233
|
-
};
|
|
234
|
-
};
|
|
235
|
-
|
|
236
171
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
237
172
|
try {
|
|
238
173
|
let method = null
|
|
@@ -284,7 +219,8 @@ class MelCloudAta extends EventEmitter {
|
|
|
284
219
|
headers: headers,
|
|
285
220
|
data: payload
|
|
286
221
|
});
|
|
287
|
-
|
|
222
|
+
|
|
223
|
+
this.emit('deviceState', deviceData);
|
|
288
224
|
return true;
|
|
289
225
|
case "melcloudhome":
|
|
290
226
|
switch (flag) {
|
|
@@ -375,7 +311,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
375
311
|
data: payload
|
|
376
312
|
});
|
|
377
313
|
|
|
378
|
-
this.updateData(deviceData, updateState);
|
|
379
314
|
return true;
|
|
380
315
|
default:
|
|
381
316
|
return;
|
|
@@ -385,14 +320,5 @@ class MelCloudAta extends EventEmitter {
|
|
|
385
320
|
throw new Error(`Send data error: ${error.message}`);
|
|
386
321
|
}
|
|
387
322
|
}
|
|
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
323
|
};
|
|
398
324
|
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, melcloudDevicesList) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,7 +14,6 @@ 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;
|
|
22
19
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
@@ -27,47 +24,69 @@ class MelCloudAtw extends EventEmitter {
|
|
|
27
24
|
//set default values
|
|
28
25
|
this.deviceData = {};
|
|
29
26
|
this.headers = {};
|
|
30
|
-
this.socket = null;
|
|
31
|
-
this.connecting = false;
|
|
32
|
-
this.socketConnected = false;
|
|
33
|
-
this.heartbeat = null;
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.on('state', (state) => {
|
|
42
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async handleWithLock(fn) {
|
|
47
|
-
if (this.locks) return;
|
|
48
|
-
|
|
49
|
-
this.locks = true;
|
|
50
|
-
try {
|
|
51
|
-
await fn();
|
|
52
|
-
} catch (error) {
|
|
53
|
-
this.emit('error', `Inpulse generator error: ${error}`);
|
|
54
|
-
} finally {
|
|
55
|
-
this.locks = false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
28
|
+
let deviceData = null;
|
|
29
|
+
melcloud.on('devicesList', async (devicesData) => {
|
|
30
|
+
this.headers = devicesData.Headers;
|
|
31
|
+
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
32
|
+
if (!deviceData) return;
|
|
33
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
58
34
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
//update state
|
|
36
|
+
await this.updateState(deviceData);
|
|
37
|
+
}).on('message', async (message) => {
|
|
38
|
+
try {
|
|
39
|
+
const parsedMessage = JSON.parse(message);
|
|
40
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
41
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
42
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
43
|
+
|
|
44
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
45
|
+
if (!messageData || !deviceData) return;
|
|
46
|
+
|
|
47
|
+
let updateState = false;
|
|
48
|
+
const unitId = messageData?.id;
|
|
49
|
+
switch (unitId) {
|
|
50
|
+
case this.deviceId:
|
|
51
|
+
const messageType = parsedMessage[0].messageType;
|
|
52
|
+
switch (messageType) {
|
|
53
|
+
case 'unitStateChanged':
|
|
54
|
+
const settings = Object.fromEntries(
|
|
55
|
+
messageData.settings.map(({ name, value }) => {
|
|
56
|
+
let parsedValue = this.functions.convertValue(value);
|
|
57
|
+
return [name, parsedValue];
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
Object.assign(deviceData.Device, settings);
|
|
61
|
+
updateState = true;
|
|
62
|
+
break;
|
|
63
|
+
case 'unitHolidayModeTriggered':
|
|
64
|
+
deviceData.Device.Power = settings.Power;
|
|
65
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
66
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
67
|
+
updateState = true;
|
|
68
|
+
break;
|
|
69
|
+
case 'unitWifiSignalChanged':
|
|
70
|
+
deviceData.Rssi = messageData.rssi;
|
|
71
|
+
updateState = true;
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
default:
|
|
79
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
//update state
|
|
84
|
+
if (updateState) await this.updateState(deviceData);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
69
89
|
|
|
70
|
-
this.socketConnected = false;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
async updateState(deviceData) {
|
|
@@ -137,106 +156,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
137
156
|
};
|
|
138
157
|
};
|
|
139
158
|
|
|
140
|
-
async checkState() {
|
|
141
|
-
try {
|
|
142
|
-
//read device info from file
|
|
143
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
144
|
-
if (!devicesData) return;
|
|
145
|
-
|
|
146
|
-
this.headers = devicesData.Headers;
|
|
147
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
148
|
-
if (!deviceData) return;
|
|
149
|
-
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
|
-
await this.updateState(deviceData);
|
|
233
|
-
|
|
234
|
-
return true;
|
|
235
|
-
} catch (error) {
|
|
236
|
-
throw new Error(`Check state error: ${error.message}`);
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
|
|
240
159
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
241
160
|
try {
|
|
242
161
|
|
|
@@ -303,7 +222,8 @@ class MelCloudAtw extends EventEmitter {
|
|
|
303
222
|
headers: headers,
|
|
304
223
|
data: payload
|
|
305
224
|
});
|
|
306
|
-
|
|
225
|
+
|
|
226
|
+
this.emit('deviceState', deviceData);
|
|
307
227
|
return true;
|
|
308
228
|
case "melcloudhome":
|
|
309
229
|
switch (flag) {
|
|
@@ -363,7 +283,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
363
283
|
data: payload
|
|
364
284
|
});
|
|
365
285
|
|
|
366
|
-
this.updateData(deviceData, updateState);
|
|
367
286
|
return true;
|
|
368
287
|
default:
|
|
369
288
|
return;
|
|
@@ -373,14 +292,5 @@ class MelCloudAtw extends EventEmitter {
|
|
|
373
292
|
throw new Error(`Send data error: ${error.message}`);
|
|
374
293
|
}
|
|
375
294
|
}
|
|
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
295
|
};
|
|
386
296
|
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, melcloudDevicesList) {
|
|
10
8
|
super();
|
|
11
9
|
this.accountType = account.type;
|
|
12
10
|
this.logSuccess = account.log?.success;
|
|
@@ -16,7 +14,6 @@ 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;
|
|
22
19
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
@@ -28,42 +25,77 @@ class MelCloudErv extends EventEmitter {
|
|
|
28
25
|
this.deviceData = {};
|
|
29
26
|
this.headers = {};
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.on('state', (state) => {
|
|
38
|
-
this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
28
|
+
let deviceData = null;
|
|
29
|
+
melcloud.on('devicesList', async (devicesData) => {
|
|
30
|
+
this.headers = devicesData.Headers;
|
|
31
|
+
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
32
|
+
if (!deviceData) return;
|
|
33
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
//update state
|
|
36
|
+
await this.updateState(deviceData);
|
|
37
|
+
}).on('webSocket', async (parsedMessage) => {
|
|
38
|
+
try {
|
|
39
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
40
|
+
if (!messageData || !deviceData) return;
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
let updateState = false;
|
|
43
|
+
const unitId = messageData?.id;
|
|
44
|
+
switch (unitId) {
|
|
45
|
+
case this.deviceId:
|
|
46
|
+
const messageType = parsedMessage[0].messageType;
|
|
47
|
+
const settings = this.functions.parseArrayNameValue(messageData.settings);
|
|
48
|
+
switch (messageType) {
|
|
49
|
+
case 'unitStateChanged':
|
|
50
|
+
|
|
51
|
+
//update values
|
|
52
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
53
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
54
|
+
|
|
55
|
+
//update holiday mode
|
|
56
|
+
if (key === 'HolidayMode') {
|
|
57
|
+
deviceData.HolidayMode.Enabled = value;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//update device settings
|
|
62
|
+
if (key in deviceData.Device) {
|
|
63
|
+
deviceData.Device[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
updateState = true;
|
|
67
|
+
break;
|
|
68
|
+
case 'unitHolidayModeTriggered':
|
|
69
|
+
deviceData.Device.Power = settings.Power;
|
|
70
|
+
deviceData.HolidayMode.Enabled = settings.HolidayMode;
|
|
71
|
+
deviceData.HolidayMode.Active = messageData.active;
|
|
72
|
+
updateState = true;
|
|
73
|
+
break;
|
|
74
|
+
case 'unitWifiSignalChanged':
|
|
75
|
+
deviceData.Rssi = messageData.rssi;
|
|
76
|
+
updateState = true;
|
|
77
|
+
break;
|
|
78
|
+
default:
|
|
79
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//update state
|
|
89
|
+
if (updateState) await this.updateState(deviceData);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (this.logError) this.emit('error', `Web socket process message error: ${error}`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
53
94
|
}
|
|
54
95
|
|
|
55
|
-
async
|
|
96
|
+
async updateState(deviceData) {
|
|
56
97
|
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
98
|
if (this.accountType === 'melcloudhome') {
|
|
65
|
-
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
66
|
-
|
|
67
99
|
//read default temps
|
|
68
100
|
const temps = await this.functions.readData(this.defaultTempsFile, true);
|
|
69
101
|
deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
|
|
@@ -194,7 +226,8 @@ class MelCloudErv extends EventEmitter {
|
|
|
194
226
|
headers: headers,
|
|
195
227
|
data: payload
|
|
196
228
|
});
|
|
197
|
-
|
|
229
|
+
|
|
230
|
+
this.emit('deviceState', deviceData);
|
|
198
231
|
return true;
|
|
199
232
|
case "melcloudhome":
|
|
200
233
|
switch (flag) {
|
|
@@ -257,7 +290,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
257
290
|
headers: headers,
|
|
258
291
|
data: payload
|
|
259
292
|
});
|
|
260
|
-
|
|
293
|
+
|
|
261
294
|
return true;
|
|
262
295
|
default:
|
|
263
296
|
return;
|
|
@@ -267,14 +300,5 @@ class MelCloudErv extends EventEmitter {
|
|
|
267
300
|
throw new Error(`Send data error: ${error.message}`);
|
|
268
301
|
}
|
|
269
302
|
}
|
|
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
303
|
};
|
|
280
304
|
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;
|
|
@@ -228,7 +228,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
228
228
|
this.socket = new WebSocket(url, { headers: this.webSocketOptions.Headers })
|
|
229
229
|
.on('error', (error) => {
|
|
230
230
|
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
231
|
-
socket.close();
|
|
231
|
+
this.socket.close();
|
|
232
232
|
})
|
|
233
233
|
.on('close', () => {
|
|
234
234
|
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
@@ -241,14 +241,22 @@ class MelCloudHome extends EventEmitter {
|
|
|
241
241
|
|
|
242
242
|
// heartbeat
|
|
243
243
|
this.heartbeat = setInterval(() => {
|
|
244
|
-
if (socket.readyState === socket.OPEN) {
|
|
245
|
-
if (
|
|
246
|
-
socket.ping();
|
|
244
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
245
|
+
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
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;
|