homebridge-melcloud-control 4.3.5-beta.3 → 4.3.5-beta.4

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,7 +179,7 @@ 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, devicesList.WebSocket, melcloud);
183
183
  break;
184
184
  case 1: //ATW
185
185
  configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, devicesList.WebSocket);
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.3",
4
+ "version": "4.3.5-beta.4",
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, webSocket, melcloud) {
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.melcloud = melcloud;
20
21
  this.webSocket = webSocket;
21
22
  this.account = account;
22
23
  this.accountType = account.type;
@@ -1331,7 +1332,7 @@ class DeviceAta extends EventEmitter {
1331
1332
  async start() {
1332
1333
  try {
1333
1334
  //melcloud device
1334
- this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket)
1335
+ this.melCloudAta = new MelCloudAta(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket, this.melcloud )
1335
1336
  .on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
1336
1337
  if (this.logDeviceInfo && this.displayDeviceInfo) {
1337
1338
  this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
package/src/deviceatw.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, HeatPump } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceAtw extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, webSocket) {
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.webSocket = webSocket;
20
21
  this.account = account;
21
22
  this.accountType = account.type;
22
23
  this.accountName = account.name;
@@ -1577,7 +1578,7 @@ class DeviceAtw extends EventEmitter {
1577
1578
  async start() {
1578
1579
  try {
1579
1580
  //melcloud device
1580
- this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile)
1581
+ this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket)
1581
1582
  .on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, supportsHotWaterTank, supportsZone2) => {
1582
1583
  if (this.logDeviceInfo && this.displayDeviceInfo) {
1583
1584
  this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
package/src/deviceerv.js CHANGED
@@ -7,7 +7,7 @@ import { TemperatureDisplayUnits, Ventilation } from './constants.js';
7
7
  let Accessory, Characteristic, Service, Categories, AccessoryUUID;
8
8
 
9
9
  class DeviceErv extends EventEmitter {
10
- constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile) {
10
+ constructor(api, account, device, devicesFile, defaultTempsFile, accountInfo, accountFile, webSocket) {
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.webSocket = webSocket;
20
21
  this.account = account;
21
22
  this.accountType = account.type;
22
23
  this.accountName = account.name;
@@ -1130,7 +1131,7 @@ class DeviceErv extends EventEmitter {
1130
1131
  async start() {
1131
1132
  try {
1132
1133
  //melcloud device
1133
- this.melCloudErv = new MelCloudErv(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile)
1134
+ this.melCloudErv = new MelCloudErv(this.account, this.device, this.devicesFile, this.defaultTempsFile, this.accountFile, this.webSocket)
1134
1135
  .on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
1135
1136
  if (this.logDeviceInfo && this.displayDeviceInfo) {
1136
1137
  this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
package/src/melcloud.js CHANGED
@@ -114,6 +114,8 @@ class MelCloud extends EventEmitter {
114
114
  await this.functions.saveData(this.devicesFile, devicesList);
115
115
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
116
116
 
117
+ this.emit('devicesList', devicesList);
118
+
117
119
  return devicesList;
118
120
  } catch (error) {
119
121
  throw new Error(`Check devices list error: ${error.message}`);
@@ -1,4 +1,3 @@
1
- import WebSocket from 'ws';
2
1
  import axios from 'axios';
3
2
  import EventEmitter from 'events';
4
3
  import ImpulseGenerator from './impulsegenerator.js';
@@ -6,7 +5,7 @@ import Functions from './functions.js';
6
5
  import { ApiUrls, ApiUrlsHome, AirConditioner } from './constants.js';
7
6
 
8
7
  class MelCloudAta extends EventEmitter {
9
- constructor(account, device, devicesFile, defaultTempsFile, accountFile, webSocket) {
8
+ constructor(account, device, devicesFile, defaultTempsFile, accountFile, webSocket, melcloud) {
10
9
  super();
11
10
  this.accountType = account.type;
12
11
  this.logSuccess = account.log?.success;
@@ -28,16 +27,90 @@ class MelCloudAta extends EventEmitter {
28
27
  //set default values
29
28
  this.deviceData = {};
30
29
  this.headers = {};
31
- this.socket = null;
32
- this.connecting = false;
33
- this.socketConnected = false;
34
- this.heartbeat = null;
30
+ this.firstRun = true;
31
+
32
+ let deviceData = null;
33
+ melcloud.on('devicesList', async (devicesData) => {
34
+ this.headers = devicesData.Headers;
35
+ deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
36
+ if (!deviceData) return;
37
+ deviceData.Scenes = devicesData.Scenes ?? [];
38
+
39
+ //update state
40
+ await this.updateState(deviceData);
41
+ });
42
+
43
+ //web cocket message
44
+ if (this.accountType === 'melcloudhome') {
45
+ try {
46
+ webSocket.on('message', async (message) => {
47
+ const parsedMessage = JSON.parse(message);
48
+ const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
49
+ if (parsedMessage.message === 'Forbidden') return;
50
+
51
+ const messageData = parsedMessage?.[0]?.Data;
52
+ if (!messageData || !deviceData) return;
53
+
54
+ let updateState = false;
55
+ const unitId = messageData?.id;
56
+ switch (unitId) {
57
+ case this.deviceId:
58
+ if (!this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
59
+ const messageType = parsedMessage[0].messageType;
60
+ const settings = this.functions.parseArrayNameValue(messageData.settings);
61
+ switch (messageType) {
62
+ case 'unitStateChanged':
63
+
64
+ //update values
65
+ for (const [key, value] of Object.entries(settings)) {
66
+ if (!this.functions.isValidValue(value)) continue;
67
+
68
+ //update holiday mode
69
+ if (key === 'HolidayMode') {
70
+ deviceData.HolidayMode.Enabled = value;
71
+ continue;
72
+ }
73
+
74
+ //update device settings
75
+ if (key in deviceData.Device) {
76
+ deviceData.Device[key] = value;
77
+ }
78
+ }
79
+ updateState = true;
80
+ break;
81
+ case 'unitHolidayModeTriggered':
82
+ deviceData.Device.Power = settings.Power;
83
+ deviceData.HolidayMode.Enabled = settings.HolidayMode;
84
+ deviceData.HolidayMode.Active = messageData.active;
85
+ updateState = true;
86
+ break;
87
+ case 'unitWifiSignalChanged':
88
+ deviceData.Rssi = messageData.rssi;
89
+ updateState = true;
90
+ break;
91
+ default:
92
+ if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
93
+ return;
94
+ }
95
+ break;
96
+ default:
97
+ if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
98
+ return;
99
+ }
100
+
101
+ //update state
102
+ if (updateState) await this.updateState(deviceData);
103
+ });
104
+ } catch (error) {
105
+ if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
106
+ }
107
+ }
35
108
 
36
109
  //lock flag
37
110
  this.locks = false;
38
111
  this.impulseGenerator = new ImpulseGenerator()
39
112
  .on('checkState', () => this.handleWithLock(async () => {
40
- await this.checkState();
113
+ //await this.checkState();
41
114
  }))
42
115
  .on('state', (state) => {
43
116
  this.emit(state ? 'success' : 'warn', `Impulse generator ${state ? 'started' : 'stopped'}`);
@@ -57,20 +130,6 @@ class MelCloudAta extends EventEmitter {
57
130
  }
58
131
  }
59
132
 
60
- cleanupSocket() {
61
- if (this.heartbeat) {
62
- clearInterval(this.heartbeat);
63
- this.heartbeat = null;
64
- }
65
-
66
- if (this.socket) {
67
- try { this.socket.close(); } catch { }
68
- this.socket = null;
69
- }
70
-
71
- this.socketConnected = false;
72
- }
73
-
74
133
  async updateState(deviceData) {
75
134
  try {
76
135
  if (this.accountType === 'melcloudhome') {
@@ -155,9 +214,9 @@ class MelCloudAta extends EventEmitter {
155
214
  if (!deviceData) return;
156
215
  deviceData.Scenes = devicesData.Scenes ?? [];
157
216
 
158
- //web cocket connection
159
- if (this.accountType === 'melcloudhome' && !this.connecting) {
160
- this.connecting = true;
217
+ //web cocket message
218
+ if (this.accountType === 'melcloudhome' && !this.firstRun) {
219
+ this.firstRun = false;
161
220
 
162
221
  try {
163
222
  this.webSocket.on('message', async (message) => {
@@ -206,12 +265,12 @@ class MelCloudAta extends EventEmitter {
206
265
  updateState = true;
207
266
  break;
208
267
  default:
209
- if (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
268
+ if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
210
269
  return;
211
270
  }
212
271
  break;
213
272
  default:
214
- if (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
273
+ if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
215
274
  return;
216
275
  }
217
276
 
@@ -6,7 +6,7 @@ import Functions from './functions.js';
6
6
  import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
7
7
 
8
8
  class MelCloudAtw 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;
@@ -27,10 +27,7 @@ class MelCloudAtw extends EventEmitter {
27
27
  //set default values
28
28
  this.deviceData = {};
29
29
  this.headers = {};
30
- this.socket = null;
31
- this.connecting = false;
32
- this.socketConnected = false;
33
- this.heartbeat = null;
30
+ this.firstRun = true;
34
31
 
35
32
  //lock flag
36
33
  this.locks = false;
@@ -148,80 +145,53 @@ class MelCloudAtw extends EventEmitter {
148
145
  if (!deviceData) return;
149
146
  deviceData.Scenes = devicesData.Scenes ?? [];
150
147
 
151
- //web cocket connection
152
- if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
153
- this.connecting = true;
148
+ ///web cocket message
149
+ if (this.accountType === 'melcloudhome' && !this.firstRun) {
150
+ this.firstRun = false;
154
151
 
155
- const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
156
152
  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();
153
+ this.webSocket.on('message', async (message) => {
154
+ const parsedMessage = JSON.parse(message);
155
+ const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
156
+ if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
157
+ if (parsedMessage.message === 'Forbidden') return;
158
+
159
+ const messageData = parsedMessage?.[0]?.Data;
160
+ if (!messageData) return;
161
+
162
+ let updateState = false;
163
+ const unitId = messageData?.id;
164
+ switch (unitId) {
165
+ case this.deviceId:
166
+ const messageType = parsedMessage[0].messageType;
167
+ switch (messageType) {
168
+ case 'unitStateChanged':
169
+ const settings = Object.fromEntries(
170
+ messageData.settings.map(({ name, value }) => {
171
+ let parsedValue = this.functions.convertValue(value);
172
+ return [name, parsedValue];
173
+ })
174
+ );
175
+ Object.assign(deviceData.Device, settings);
176
+ updateState = true;
177
+ break;
178
+ case 'unitWifiSignalChanged':
179
+ deviceData.Rssi = messageData.rssi;
180
+ updateState = true;
181
+ break;
182
+ default:
183
+ if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
184
+ return;
177
185
  }
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
- });
186
+ break;
187
+ default:
188
+ if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
189
+ return;
190
+ }
191
+
192
+ //update state
193
+ if (updateState) await this.updateState(deviceData);
194
+ });
225
195
  } catch (error) {
226
196
  if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
227
197
  this.cleanupSocket();
@@ -6,7 +6,7 @@ import Functions from './functions.js';
6
6
  import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
7
7
 
8
8
  class MelCloudErv 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;
@@ -27,6 +27,7 @@ class MelCloudErv extends EventEmitter {
27
27
  //set default values
28
28
  this.deviceData = {};
29
29
  this.headers = {};
30
+ this.firstRun = true;
30
31
 
31
32
  //lock flags
32
33
  this.locks = false;
@@ -266,6 +266,8 @@ class MelCloudHome extends EventEmitter {
266
266
  await this.functions.saveData(this.devicesFile, devicesList);
267
267
  if (this.logDebug) this.emit('debug', `${devicesCount} devices saved`);
268
268
 
269
+ this.emit('devicesList', devicesList);
270
+
269
271
  return devicesList;
270
272
  } catch (error) {
271
273
  throw new Error(`Check devices list error: ${error.message}`);