homebridge-melcloud-control 4.3.4 → 4.3.5-beta.0

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
@@ -179,15 +179,15 @@ class MelCloudPlatform {
179
179
  let configuredDevice;
180
180
  switch (deviceType) {
181
181
  case 0: //ATA
182
- configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile);
182
+ configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile), devicesList.WebSocket;
183
183
  break;
184
184
  case 1: //ATW
185
- configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile);
185
+ configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, devicesList.WebSocket);
186
186
  break;
187
187
  case 2:
188
188
  break;
189
189
  case 3: //ERV
190
- configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile);
190
+ configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, devicesList.WebSocket);
191
191
  break;
192
192
  default:
193
193
  if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeString}, ${deviceName}, unknown device: ${deviceType}.`);
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.4",
4
+ "version": "4.3.5-beta.0",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/deviceata.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, AirConditioner } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAta extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, webSocket) {
11
11
  super();
12
12
 
13
13
  Accessory = api.platformAccessory;
@@ -17,6 +17,7 @@ class DeviceAta extends EventEmitter {
17
17
  AccessoryUUID = api.hap.uuid;
18
18
 
19
19
  //account config
20
+ this.webSocket = webSocket;
20
21
  this.account = account;
21
22
  this.accountType = account.type;
22
23
  this.accountName = account.name;
@@ -1330,7 +1331,7 @@ class DeviceAta extends EventEmitter {
1330
1331
  async start() {
1331
1332
  try {
1332
1333
  //melcloud device
1333
- this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile)
1334
+ this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket)
1334
1335
  .on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
1335
1336
  if (this.logDeviceInfo && this.displayDeviceInfo) {
1336
1337
  this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
@@ -6,7 +6,7 @@ import Functions from './functions.js';
6
6
  import { ApiUrls, ApiUrlsHome, AirConditioner } from './constants.js';
7
7
 
8
8
  class MelCloudAta extends EventEmitter {
9
- constructor(account, device, devicesFile, defaultTempsFile, accountFile) {
9
+ constructor(account, device, devicesFile, defaultTempsFile, accountFile, webSocket) {
10
10
  super();
11
11
  this.accountType = account.type;
12
12
  this.logSuccess = account.log?.success;
@@ -19,6 +19,7 @@ class MelCloudAta extends EventEmitter {
19
19
  this.devicesFile = devicesFile;
20
20
  this.defaultTempsFile = defaultTempsFile;
21
21
  this.accountFile = accountFile;
22
+ this.webSocket = webSocket;
22
23
  this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
23
24
  .on('warn', warn => this.emit('warn', warn))
24
25
  .on('error', error => this.emit('error', error))
@@ -155,95 +156,68 @@ class MelCloudAta extends EventEmitter {
155
156
  deviceData.Scenes = devicesData.Scenes ?? [];
156
157
 
157
158
  //web cocket connection
158
- if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
159
+ if (this.accountType === 'melcloudhome' && !this.connecting) {
159
160
  this.connecting = true;
160
161
 
161
162
  try {
162
- const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
163
- const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
164
- .on('error', (error) => {
165
- if (this.logError) this.emit('error', `Socket error: ${error}`);
166
- socket.close();
167
- })
168
- .on('close', () => {
169
- if (this.logDebug) this.emit('debug', `Socket closed`);
170
- this.cleanupSocket();
171
- })
172
- .on('open', () => {
173
- this.socket = socket;
174
- this.socketConnected = true;
175
- this.connecting = false;
176
- if (this.logSuccess) this.emit('success', `Socket Connect Success`);
177
-
178
- // heartbeat
179
- this.heartbeat = setInterval(() => {
180
- if (socket.readyState === socket.OPEN) {
181
- if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
182
- socket.ping();
183
- }
184
- }, 30000);
185
- })
186
- .on('pong', () => {
187
- if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
188
- })
189
- .on('message', async (message) => {
190
- const parsedMessage = JSON.parse(message);
191
- const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
192
- if (parsedMessage.message === 'Forbidden') return;
193
-
194
- const messageData = parsedMessage?.[0]?.Data;
195
- if (!messageData) return;
196
-
197
- let updateState = false;
198
- const unitId = messageData?.id;
199
- switch (unitId) {
200
- case this.deviceId:
201
- if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
202
- const messageType = parsedMessage[0].messageType;
203
- const settings = this.functions.parseArrayNameValue(messageData.settings);
204
- switch (messageType) {
205
- case 'unitStateChanged':
206
-
207
- //update values
208
- for (const [key, value] of Object.entries(settings)) {
209
- if (!this.functions.isValidValue(value)) continue;
210
-
211
- //update holiday mode
212
- if (key === 'HolidayMode') {
213
- deviceData.HolidayMode.Enabled = value;
214
- continue;
215
- }
216
-
217
- //update device settings
218
- if (key in deviceData.Device) {
219
- deviceData.Device[key] = value;
220
- }
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;
221
189
  }
222
- updateState = true;
223
- break;
224
- case 'unitHolidayModeTriggered':
225
- deviceData.Device.Power = settings.Power;
226
- deviceData.HolidayMode.Enabled = settings.HolidayMode;
227
- deviceData.HolidayMode.Active = messageData.active;
228
- updateState = true;
229
- break;
230
- case 'unitWifiSignalChanged':
231
- deviceData.Rssi = messageData.rssi;
232
- updateState = true;
233
- break;
234
- default:
235
- if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
236
- return;
237
- }
238
- break;
239
- default:
240
- if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
241
- return;
242
- }
243
190
 
244
- //update state
245
- if (updateState) await this.updateState(deviceData);
246
- });
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
+ });
247
221
  } catch (error) {
248
222
  if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
249
223
  this.cleanupSocket();
@@ -25,6 +25,10 @@ class MelCloudHome extends EventEmitter {
25
25
  this.devicesFile = devicesFile;
26
26
  this.headers = {};
27
27
  this.webSocketOptions = {};
28
+ this.socket = null;
29
+ this.connecting = false;
30
+ this.socketConnected = false;
31
+ this.heartbeat = null;
28
32
 
29
33
  this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
30
34
  .on('warn', warn => this.emit('warn', warn))
@@ -63,6 +67,20 @@ class MelCloudHome extends EventEmitter {
63
67
  }
64
68
  }
65
69
 
70
+ cleanupSocket() {
71
+ if (this.heartbeat) {
72
+ clearInterval(this.heartbeat);
73
+ this.heartbeat = null;
74
+ }
75
+
76
+ if (this.socket) {
77
+ try { this.socket.close(); } catch { }
78
+ this.socket = null;
79
+ }
80
+
81
+ this.socketConnected = false;
82
+ }
83
+
66
84
  async checkScenesList() {
67
85
  try {
68
86
  if (this.logDebug) this.emit('debug', `Scanning for scenes`);
@@ -200,12 +218,50 @@ class MelCloudHome extends EventEmitter {
200
218
  if (this.logDebug) this.emit('debug', `Get scenes error: ${error} `);
201
219
  }
202
220
 
221
+ //web cocket connection
222
+ if (!this.connecting && !this.socketConnected) {
223
+ this.connecting = true;
224
+
225
+ try {
226
+ const url = `${ApiUrlsHome.WebSocketURL}${this.webSocketOptions.Hash}`;
227
+ const socket = new WebSocket(url, { headers: this.webSocketOptions.Headers })
228
+ .on('error', (error) => {
229
+ if (this.logError) this.emit('error', `Socket error: ${error}`);
230
+ socket.close();
231
+ })
232
+ .on('close', () => {
233
+ if (this.logDebug) this.emit('debug', `Socket closed`);
234
+ this.cleanupSocket();
235
+ })
236
+ .on('open', () => {
237
+ this.socket = socket;
238
+ this.socketConnected = true;
239
+ this.connecting = false;
240
+ if (this.logSuccess) this.emit('success', `Socket Connect Success`);
241
+
242
+ // heartbeat
243
+ this.heartbeat = setInterval(() => {
244
+ if (socket.readyState === socket.OPEN) {
245
+ if (!this.logDebug) this.emit('debug', `Socket send heartbeat`);
246
+ socket.ping();
247
+ }
248
+ }, 30000);
249
+ })
250
+ .on('pong', () => {
251
+ if (!this.logDebug) this.emit('debug', `Socket received heartbeat`);
252
+ });
253
+ } catch (error) {
254
+ if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
255
+ this.cleanupSocket();
256
+ }
257
+ }
258
+
203
259
  devicesList.State = true;
204
260
  devicesList.Info = `Found ${devicesCount} devices and ${scenes.length} scenes`;
205
261
  devicesList.Devices = devices;
206
262
  devicesList.Scenes = scenes;
207
263
  devicesList.Headers = this.headers;
208
- devicesList.WebSocketOptions = this.webSocketOptions;
264
+ devicesList.WebSocket = this.socket;
209
265
 
210
266
  await this.functions.saveData(this.devicesFile, devicesList);
211
267
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
@@ -379,7 +435,6 @@ class MelCloudHome extends EventEmitter {
379
435
  }
380
436
  };
381
437
 
382
-
383
438
  accountInfo.State = true;
384
439
  accountInfo.Info = 'Connect to MELCloud Home Success';
385
440
  await this.functions.saveData(this.accountFile, accountInfo);