homebridge-melcloud-control 4.3.5-beta.1 → 4.3.5-beta.10

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 CHANGED
@@ -85,22 +85,22 @@ class MelCloudPlatform {
85
85
  .on('start', async () => {
86
86
  try {
87
87
  //melcloud account
88
- let configureAccount;
88
+ let melcloud;
89
89
  let timmers = []
90
90
  switch (account.type) {
91
91
  case 'melcloud':
92
92
  timmers = [{ name: 'checkDevicesList', sampling: refreshInterval }];
93
- configureAccount = new MelCloud(account, accountFile, buildingsFile, devicesFile, true);
93
+ melcloud = new MelCloud(account, accountFile, buildingsFile, devicesFile, true);
94
94
  break;
95
95
  case 'melcloudhome':
96
96
  timmers = [{ name: 'connect', sampling: 3300000 }, { name: 'checkDevicesList', sampling: 3000 }];
97
- configureAccount = new MelCloudHome(account, accountFile, buildingsFile, devicesFile, true);
97
+ melcloud = new MelCloudHome(account, accountFile, buildingsFile, devicesFile, true);
98
98
  break;
99
99
  default:
100
100
  if (logLevel.warn) log.warn(`Unknown account type: ${account.type}.`);
101
101
  return;
102
102
  }
103
- configureAccount.on('success', (msg) => log.success(`${accountName}, ${msg}`))
103
+ melcloud.on('success', (msg) => log.success(`${accountName}, ${msg}`))
104
104
  .on('info', (msg) => log.info(`${accountName}, ${msg}`))
105
105
  .on('debug', (msg) => log.info(`${accountName}, debug: ${msg}`))
106
106
  .on('warn', (msg) => log.warn(`${accountName}, ${msg}`))
@@ -109,7 +109,7 @@ class MelCloudPlatform {
109
109
  //connect
110
110
  let accountInfo;
111
111
  try {
112
- accountInfo = await configureAccount.connect();
112
+ accountInfo = await melcloud.connect();
113
113
  if (!accountInfo.State) {
114
114
  if (logLevel.warn) log.warn(`${accountName}, ${accountInfo.Info}`);
115
115
  return;
@@ -123,7 +123,7 @@ class MelCloudPlatform {
123
123
  //check devices list
124
124
  let devicesList;
125
125
  try {
126
- devicesList = await configureAccount.checkDevicesList();
126
+ devicesList = await melcloud.checkDevicesList();
127
127
  if (!devicesList.State) {
128
128
  if (logLevel.warn) log.warn(`${accountName}, ${devicesList.Info}`);
129
129
  return;
@@ -179,15 +179,15 @@ class MelCloudPlatform {
179
179
  let configuredDevice;
180
180
  switch (deviceType) {
181
181
  case 0: //ATA
182
- configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile), devicesList.WebSocket;
182
+ configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
183
183
  break;
184
184
  case 1: //ATW
185
- configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, devicesList.WebSocket);
185
+ configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
186
186
  break;
187
187
  case 2:
188
188
  break;
189
189
  case 3: //ERV
190
- configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, devicesList.WebSocket);
190
+ configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud);
191
191
  break;
192
192
  default:
193
193
  if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeString}, ${deviceName}, unknown device: ${deviceType}.`);
@@ -205,14 +205,11 @@ class MelCloudPlatform {
205
205
  if (accessory) {
206
206
  api.publishExternalAccessories(PluginName, [accessory]);
207
207
  if (logLevel.success) log.success(`${accountName}, ${deviceTypeString}, ${deviceName}, Published as external accessory.`);
208
-
209
- //start impulse generators for device
210
- await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
211
208
  }
212
209
  }
213
210
 
214
211
  //start account impulse generator
215
- await configureAccount.impulseGenerator.state(true, timmers, false);
212
+ await melcloud.impulseGenerator.state(true, timmers, false);
216
213
 
217
214
  //stop start impulse generator
218
215
  await impulseGenerator.state(false);
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.1",
4
+ "version": "4.3.5-beta.10",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/deviceata.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, AirConditioner } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAta extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, webSocket) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -17,7 +17,7 @@ class DeviceAta extends EventEmitter {
17
17
  AccessoryUUID = api.hap.uuid;
18
18
 
19
19
  //account config
20
- this.webSocket = webSocket;
20
+ this.melcloud = melcloud;
21
21
  this.account = account;
22
22
  this.accountType = account.type;
23
23
  this.accountName = account.name;
@@ -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.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket)
1324
+ this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, 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,7 +1878,7 @@ 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.updateState();
1892
1882
 
1893
1883
  //prepare accessory
1894
1884
  const accessory = await this.prepareAccessory();
package/src/deviceatw.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, HeatPump } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAtw extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -17,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;
@@ -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.devicesFile, this.defaultTempsFile, this.accountFile)
1570
+ this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.devicesFile, 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,7 +2307,7 @@ 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.melCloudAtw.checkState();
2310
+ await this.melCloudAtw.updateState();
2321
2311
 
2322
2312
  //prepare accessory
2323
2313
  const accessory = await this.prepareAccessory();
package/src/deviceerv.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, Ventilation } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceErv extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, melcloud) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -17,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;
@@ -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.devicesFile, this.defaultTempsFile, this.accountFile)
1124
+ this.melCloudErv = new MelCloudErv(this.account, this.device, this.devicesFile, 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,7 +1581,7 @@ 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.melCloudErv.checkState();
1584
+ await this.melCloudErv.updateState();
1594
1585
 
1595
1586
  //prepare accessory
1596
1587
  const accessory = await this.prepareAccessory();
package/src/melcloud.js CHANGED
@@ -110,6 +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
+ this.emit('devicesList', devicesList);
113
114
 
114
115
  await this.functions.saveData(this.devicesFile, devicesList);
115
116
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
@@ -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, devicesFile, defaultTempsFile, accountFile, webSocket) {
7
+ constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
10
8
  super();
11
9
  this.accountType = account.type;
12
10
  this.logSuccess = account.log?.success;
@@ -19,7 +17,6 @@ class MelCloudAta extends EventEmitter {
19
17
  this.devicesFile = devicesFile;
20
18
  this.defaultTempsFile = defaultTempsFile;
21
19
  this.accountFile = accountFile;
22
- this.webSocket = webSocket;
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))
@@ -28,47 +25,85 @@ class MelCloudAta extends EventEmitter {
28
25
  //set default values
29
26
  this.deviceData = {};
30
27
  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
- }
28
+ this.firtRun = true;
46
29
 
47
- async handleWithLock(fn) {
48
- if (this.locks) return;
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 ?? [];
49
36
 
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
- }
37
+ //web cocket message
38
+ if (this.accountType === 'melcloudhome' && this.firtRun) {
39
+ this.firtRun = false;
40
+ try {
41
+ devicesData.webSocket.on('message', async (message) => {
42
+ const parsedMessage = JSON.parse(message);
43
+ const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
44
+ if (parsedMessage.message === 'Forbidden') return;
59
45
 
60
- cleanupSocket() {
61
- if (this.heartbeat) {
62
- clearInterval(this.heartbeat);
63
- this.heartbeat = null;
64
- }
46
+ const messageData = parsedMessage?.[0]?.Data;
47
+ if (!messageData || !deviceData) return;
65
48
 
66
- if (this.socket) {
67
- try { this.socket.close(); } catch { }
68
- this.socket = null;
69
- }
49
+ let updateState = false;
50
+ const unitId = messageData?.id;
51
+ switch (unitId) {
52
+ case this.deviceId:
53
+ if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
54
+ const messageType = parsedMessage[0].messageType;
55
+ const settings = this.functions.parseArrayNameValue(messageData.settings);
56
+ switch (messageType) {
57
+ case 'unitStateChanged':
58
+
59
+ //update values
60
+ for (const [key, value] of Object.entries(settings)) {
61
+ if (!this.functions.isValidValue(value)) continue;
62
+
63
+ //update holiday mode
64
+ if (key === 'HolidayMode') {
65
+ deviceData.HolidayMode.Enabled = value;
66
+ continue;
67
+ }
68
+
69
+ //update device settings
70
+ if (key in deviceData.Device) {
71
+ deviceData.Device[key] = value;
72
+ }
73
+ }
74
+ updateState = true;
75
+ break;
76
+ case 'unitHolidayModeTriggered':
77
+ deviceData.Device.Power = settings.Power;
78
+ deviceData.HolidayMode.Enabled = settings.HolidayMode;
79
+ deviceData.HolidayMode.Active = messageData.active;
80
+ updateState = true;
81
+ break;
82
+ case 'unitWifiSignalChanged':
83
+ deviceData.Rssi = messageData.rssi;
84
+ updateState = true;
85
+ break;
86
+ default:
87
+ if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
88
+ return;
89
+ }
90
+ break;
91
+ default:
92
+ if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
93
+ return;
94
+ }
95
+
96
+ //update state
97
+ if (updateState) await this.updateState(deviceData);
98
+ });
99
+ } catch (error) {
100
+ if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
101
+ }
102
+ }
70
103
 
71
- this.socketConnected = false;
104
+ //update state
105
+ await this.updateState(deviceData);
106
+ });
72
107
  }
73
108
 
74
109
  async updateState(deviceData) {
@@ -144,95 +179,6 @@ class MelCloudAta extends EventEmitter {
144
179
  };
145
180
  };
146
181
 
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
182
  async send(accountType, displayType, deviceData, flag, flagData) {
237
183
  try {
238
184
  let method = null
@@ -284,7 +230,7 @@ class MelCloudAta extends EventEmitter {
284
230
  headers: headers,
285
231
  data: payload
286
232
  });
287
- this.updateData(deviceData, updateState);
233
+
288
234
  return true;
289
235
  case "melcloudhome":
290
236
  switch (flag) {
@@ -375,7 +321,6 @@ class MelCloudAta extends EventEmitter {
375
321
  data: payload
376
322
  });
377
323
 
378
- this.updateData(deviceData, updateState);
379
324
  return true;
380
325
  default:
381
326
  return;
@@ -385,14 +330,5 @@ class MelCloudAta extends EventEmitter {
385
330
  throw new Error(`Send data error: ${error.message}`);
386
331
  }
387
332
  }
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
333
  };
398
334
  export default MelCloudAta;
@@ -1,12 +1,10 @@
1
- import WebSocket from 'ws';
2
1
  import axios from 'axios';
3
2
  import EventEmitter from 'events';
4
- import ImpulseGenerator from './impulsegenerator.js';
5
3
  import Functions from './functions.js';
6
4
  import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
7
5
 
8
6
  class MelCloudAtw extends EventEmitter {
9
- constructor(account, device, devicesFile, defaultTempsFile, accountFile) {
7
+ constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
10
8
  super();
11
9
  this.accountType = account.type;
12
10
  this.logSuccess = account.log?.success;
@@ -27,47 +25,68 @@ 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
28
 
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
-
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
- }
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
- cleanupSocket() {
60
- if (this.heartbeat) {
61
- clearInterval(this.heartbeat);
62
- this.heartbeat = null;
63
- }
36
+ //update state
37
+ await this.updateState(deviceData);
38
+ });
39
+
40
+ ///web cocket message
41
+ if (this.accountType === 'melcloudhome') {
42
+ try {
43
+ melcloud.webSocket.on('message', async (message) => {
44
+ const parsedMessage = JSON.parse(message);
45
+ const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
46
+ if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
47
+ if (parsedMessage.message === 'Forbidden') return;
48
+
49
+ const messageData = parsedMessage?.[0]?.Data;
50
+ if (!messageData || !deviceData) return;
51
+
52
+ let updateState = false;
53
+ const unitId = messageData?.id;
54
+ switch (unitId) {
55
+ case this.deviceId:
56
+ const messageType = parsedMessage[0].messageType;
57
+ switch (messageType) {
58
+ case 'unitStateChanged':
59
+ const settings = Object.fromEntries(
60
+ messageData.settings.map(({ name, value }) => {
61
+ let parsedValue = this.functions.convertValue(value);
62
+ return [name, parsedValue];
63
+ })
64
+ );
65
+ Object.assign(deviceData.Device, settings);
66
+ updateState = true;
67
+ break;
68
+ case 'unitWifiSignalChanged':
69
+ deviceData.Rssi = messageData.rssi;
70
+ updateState = true;
71
+ break;
72
+ default:
73
+ if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
74
+ return;
75
+ }
76
+ break;
77
+ default:
78
+ if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
79
+ return;
80
+ }
64
81
 
65
- if (this.socket) {
66
- try { this.socket.close(); } catch { }
67
- this.socket = null;
82
+ //update state
83
+ if (updateState) await this.updateState(deviceData);
84
+ });
85
+ } catch (error) {
86
+ if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
87
+ this.cleanupSocket();
88
+ }
68
89
  }
69
-
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,7 @@ class MelCloudAtw extends EventEmitter {
303
222
  headers: headers,
304
223
  data: payload
305
224
  });
306
- this.updateData(deviceData);
225
+
307
226
  return true;
308
227
  case "melcloudhome":
309
228
  switch (flag) {
@@ -363,7 +282,6 @@ class MelCloudAtw extends EventEmitter {
363
282
  data: payload
364
283
  });
365
284
 
366
- this.updateData(deviceData, updateState);
367
285
  return true;
368
286
  default:
369
287
  return;
@@ -373,14 +291,5 @@ class MelCloudAtw extends EventEmitter {
373
291
  throw new Error(`Send data error: ${error.message}`);
374
292
  }
375
293
  }
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
294
  };
386
295
  export default MelCloudAtw;
@@ -1,12 +1,10 @@
1
- import WebSocket from 'ws';
2
1
  import axios from 'axios';
3
2
  import EventEmitter from 'events';
4
- import ImpulseGenerator from './impulsegenerator.js';
5
3
  import Functions from './functions.js';
6
4
  import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
7
5
 
8
6
  class MelCloudErv extends EventEmitter {
9
- constructor(account, device, devicesFile, defaultTempsFile, accountFile) {
7
+ constructor(account, device, devicesFile, defaultTempsFile, accountFile, melcloud) {
10
8
  super();
11
9
  this.accountType = account.type;
12
10
  this.logSuccess = account.log?.success;
@@ -28,39 +26,20 @@ class MelCloudErv extends EventEmitter {
28
26
  this.deviceData = {};
29
27
  this.headers = {};
30
28
 
31
- //lock flags
32
- this.locks = false;
33
- this.impulseGenerator = new ImpulseGenerator()
34
- .on('checkState', () => this.handleWithLock(async () => {
35
- await this.checkState();
36
- }))
37
- .on('state', (state) => {
38
- this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
39
- });
40
- }
41
-
42
- async handleWithLock(fn) {
43
- if (this.locks) return;
29
+ let deviceData = null;
30
+ melcloud.on('devicesList', async (devicesData) => {
31
+ this.headers = devicesData.Headers;
32
+ deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
33
+ if (!deviceData) return;
34
+ deviceData.Scenes = devicesData.Scenes ?? [];
44
35
 
45
- this.locks = true;
46
- try {
47
- await fn();
48
- } catch (error) {
49
- this.emit('error', `Inpulse generator error: ${error}`);
50
- } finally {
51
- this.locks = false;
52
- }
36
+ //update state
37
+ await this.updateState(deviceData);
38
+ }); this.firstRun = true;
53
39
  }
54
40
 
55
- async checkState() {
41
+ async updateState(deviceData) {
56
42
  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
43
  if (this.accountType === 'melcloudhome') {
65
44
  deviceData.Scenes = devicesData.Scenes ?? [];
66
45
 
@@ -194,7 +173,7 @@ class MelCloudErv extends EventEmitter {
194
173
  headers: headers,
195
174
  data: payload
196
175
  });
197
- this.updateData(deviceData);
176
+
198
177
  return true;
199
178
  case "melcloudhome":
200
179
  switch (flag) {
@@ -257,7 +236,7 @@ class MelCloudErv extends EventEmitter {
257
236
  headers: headers,
258
237
  data: payload
259
238
  });
260
- this.updateData(deviceData, updateState);
239
+
261
240
  return true;
262
241
  default:
263
242
  return;
@@ -267,14 +246,5 @@ class MelCloudErv extends EventEmitter {
267
246
  throw new Error(`Send data error: ${error.message}`);
268
247
  }
269
248
  }
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
249
  };
280
250
  export default MelCloudErv;
@@ -225,31 +225,35 @@ class MelCloudHome extends EventEmitter {
225
225
 
226
226
  try {
227
227
  const url = `${ApiUrlsHome.WebSocketURL}${this.webSocketOptions.Hash}`;
228
- const socket = new WebSocket(url, { headers: this.webSocketOptions.Headers })
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`);
235
235
  this.cleanupSocket();
236
236
  })
237
237
  .on('open', () => {
238
- this.socket = socket;
239
238
  this.socketConnected = true;
240
239
  this.connecting = false;
241
240
  if (this.logSuccess) this.emit('success', `Socket Connect Success`);
242
241
 
243
242
  // heartbeat
244
243
  this.heartbeat = setInterval(() => {
245
- if (socket.readyState === socket.OPEN) {
246
- if (!this.logDebug) this.emit('debug', `Socket send heartbeat`);
247
- socket.ping();
244
+ if (this.socket.readyState === this.socket.OPEN) {
245
+ if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
246
+ this.socket.ping();
248
247
  }
249
248
  }, 30000);
250
249
  })
251
250
  .on('pong', () => {
252
- if (!this.logDebug) this.emit('debug', `Socket received heartbeat`);
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}`);
253
257
  });
254
258
  } catch (error) {
255
259
  if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
@@ -263,6 +267,7 @@ class MelCloudHome extends EventEmitter {
263
267
  devicesList.Scenes = scenes;
264
268
  devicesList.Headers = this.headers;
265
269
  devicesList.WebSocket = this.socket;
270
+ this.emit('devicesList', devicesList);
266
271
 
267
272
  await this.functions.saveData(this.devicesFile, devicesList);
268
273
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);