homebridge-melcloud-control 4.3.3-beta.0 → 4.3.3-beta.2
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/package.json +1 -1
- package/src/constants.js +2 -2
- package/src/melcloudata.js +8 -2
- package/src/melcloudatw.js +124 -10
- package/src/melclouderv.js +1 -0
- package/src/melcloudhome.js +3 -3
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.3-beta.
|
|
4
|
+
"version": "4.3.3-beta.2",
|
|
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/constants.js
CHANGED
|
@@ -107,8 +107,8 @@ export const HeatPump = {
|
|
|
107
107
|
ForceDhwMapEnumToString: { 0: "Normal", 1: "Heat Now" },
|
|
108
108
|
HolidayMapStringToEnum: { "Normal": 0, "Holiday": 1 },
|
|
109
109
|
HolidayMapEnumToString: { 0: "Normal", 1: "Holiday" },
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
OperationModeZoneMapStringToEnum: { "Heat Thermostat": 0, "Heat Flow": 1, "Heat Curve": 2, "Cool Thermostat": 3, "Cool Flow": 4, "Flor Dry Up": 5, "Idle": 6 },
|
|
111
|
+
OperationModeZoneMapEnumToString: { 0: "Heat Thermostat", 1: "Heat Flow", 2: "Heat Curve", 3: "Cool Thermostat", 4: "Cool Flow", 5: "Flor Dry Up", 6: "Idle" },
|
|
112
112
|
EffectiveFlags: {
|
|
113
113
|
Power: 1,
|
|
114
114
|
OperationMode: 2,
|
package/src/melcloudata.js
CHANGED
|
@@ -145,7 +145,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
145
145
|
|
|
146
146
|
async checkState() {
|
|
147
147
|
try {
|
|
148
|
-
|
|
149
148
|
//read device info from file
|
|
150
149
|
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
151
150
|
if (!devicesData) return;
|
|
@@ -369,7 +368,14 @@ class MelCloudAta extends EventEmitter {
|
|
|
369
368
|
path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
|
|
370
369
|
headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
371
370
|
updateState = false;
|
|
372
|
-
|
|
371
|
+
this.socket.send(JSON.stringify([{
|
|
372
|
+
messageType: 'setUnitSettings',
|
|
373
|
+
Data: {
|
|
374
|
+
id: deviceData.DeviceID,
|
|
375
|
+
settings: payload
|
|
376
|
+
}
|
|
377
|
+
}]));
|
|
378
|
+
return;
|
|
373
379
|
}
|
|
374
380
|
|
|
375
381
|
//sens payload
|
package/src/melcloudatw.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
1
2
|
import axios from 'axios';
|
|
2
3
|
import EventEmitter from 'events';
|
|
3
4
|
import ImpulseGenerator from './impulsegenerator.js';
|
|
@@ -26,8 +27,12 @@ class MelCloudAtw extends EventEmitter {
|
|
|
26
27
|
//set default values
|
|
27
28
|
this.deviceData = {};
|
|
28
29
|
this.headers = {};
|
|
30
|
+
this.socket = null;
|
|
31
|
+
this.connecting = false;
|
|
32
|
+
this.socketConnected = false;
|
|
33
|
+
this.heartbeat = null;
|
|
29
34
|
|
|
30
|
-
//lock
|
|
35
|
+
//lock flag
|
|
31
36
|
this.locks = false;
|
|
32
37
|
this.impulseGenerator = new ImpulseGenerator()
|
|
33
38
|
.on('checkState', () => this.handleWithLock(async () => {
|
|
@@ -51,20 +56,29 @@ class MelCloudAtw extends EventEmitter {
|
|
|
51
56
|
}
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
cleanupSocket() {
|
|
60
|
+
if (this.heartbeat) {
|
|
61
|
+
clearInterval(this.heartbeat);
|
|
62
|
+
this.heartbeat = null;
|
|
63
|
+
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
if (this.socket) {
|
|
66
|
+
try { this.socket.close(); } catch { }
|
|
67
|
+
this.socket = null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.socketConnected = false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async updateState(deviceData) {
|
|
74
|
+
try {
|
|
62
75
|
if (this.accountType === 'melcloudhome') {
|
|
63
|
-
deviceData.
|
|
76
|
+
deviceData.Device.OperationMode = HeatPump.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
|
|
77
|
+
deviceData.Device.OperationModeZone1 = HeatPump.OperationModeZoneMapStringToEnum[deviceData.Device.OperationModeZone1] ?? deviceData.Device.OperationModeZone1;
|
|
78
|
+
deviceData.Device.OperationModeZone2 = HeatPump.OperationModeZoneMapStringToEnum[deviceData.Device.OperationModeZone2] ?? deviceData.Device.OperationModeZone2;
|
|
64
79
|
}
|
|
65
80
|
if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
|
|
66
81
|
|
|
67
|
-
//device
|
|
68
82
|
//device
|
|
69
83
|
const serialNumber = deviceData.SerialNumber || '4.0.0';
|
|
70
84
|
const firmwareAppVersion = deviceData.Device?.FirmwareAppVersion || '4.0.0';
|
|
@@ -123,6 +137,105 @@ class MelCloudAtw extends EventEmitter {
|
|
|
123
137
|
};
|
|
124
138
|
};
|
|
125
139
|
|
|
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
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
149
|
+
|
|
150
|
+
//web cocket connection
|
|
151
|
+
if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
|
|
152
|
+
this.connecting = true;
|
|
153
|
+
|
|
154
|
+
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
155
|
+
try {
|
|
156
|
+
const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
|
|
157
|
+
.on('error', (error) => {
|
|
158
|
+
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
159
|
+
socket.close();
|
|
160
|
+
})
|
|
161
|
+
.on('close', () => {
|
|
162
|
+
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
163
|
+
this.cleanupSocket();
|
|
164
|
+
})
|
|
165
|
+
.on('open', () => {
|
|
166
|
+
this.socket = socket;
|
|
167
|
+
this.socketConnected = true;
|
|
168
|
+
this.connecting = false;
|
|
169
|
+
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
170
|
+
|
|
171
|
+
// heartbeat
|
|
172
|
+
this.heartbeat = setInterval(() => {
|
|
173
|
+
if (socket.readyState === socket.OPEN) {
|
|
174
|
+
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
175
|
+
socket.ping();
|
|
176
|
+
}
|
|
177
|
+
}, 30000);
|
|
178
|
+
})
|
|
179
|
+
.on('pong', () => {
|
|
180
|
+
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
181
|
+
})
|
|
182
|
+
.on('message', async (message) => {
|
|
183
|
+
const parsedMessage = JSON.parse(message);
|
|
184
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
185
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
186
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
187
|
+
|
|
188
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
189
|
+
if (!messageData) return;
|
|
190
|
+
|
|
191
|
+
let updateState = false;
|
|
192
|
+
const unitId = messageData?.id;
|
|
193
|
+
switch (unitId) {
|
|
194
|
+
case this.deviceId:
|
|
195
|
+
const messageType = parsedMessage[0].messageType;
|
|
196
|
+
switch (messageType) {
|
|
197
|
+
case 'unitStateChanged':
|
|
198
|
+
const settings = Object.fromEntries(
|
|
199
|
+
messageData.settings.map(({ name, value }) => {
|
|
200
|
+
let parsedValue = this.functions.convertValue(value);
|
|
201
|
+
return [name, parsedValue];
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
Object.assign(deviceData.Device, settings);
|
|
205
|
+
updateState = true;
|
|
206
|
+
break;
|
|
207
|
+
case 'unitWifiSignalChanged':
|
|
208
|
+
deviceData.Rssi = messageData.rssi;
|
|
209
|
+
updateState = true;
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
if (this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//update state
|
|
222
|
+
if (updateState) await this.updateState(deviceData);
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
226
|
+
this.cleanupSocket();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
//update state
|
|
231
|
+
await this.updateState(deviceData);
|
|
232
|
+
|
|
233
|
+
return true;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
throw new Error(`Check state error: ${error.message}`);
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
|
|
126
239
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
127
240
|
try {
|
|
128
241
|
|
|
@@ -248,6 +361,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
248
361
|
headers: headers,
|
|
249
362
|
data: payload
|
|
250
363
|
});
|
|
364
|
+
|
|
251
365
|
this.updateData(deviceData, updateState);
|
|
252
366
|
return true;
|
|
253
367
|
default:
|
package/src/melclouderv.js
CHANGED
package/src/melcloudhome.js
CHANGED
|
@@ -267,11 +267,11 @@ class MelCloudHome extends EventEmitter {
|
|
|
267
267
|
'--no-zygote'
|
|
268
268
|
]
|
|
269
269
|
});
|
|
270
|
-
browser.on('disconnected', () => this.emit('debug', 'Browser disconnected'));
|
|
270
|
+
browser.on('disconnected', () => this.logDebug && this.emit('debug', 'Browser disconnected'));
|
|
271
271
|
|
|
272
272
|
const page = await browser.newPage();
|
|
273
|
-
page.on('error', error => this.emit('error', `Page crashed: ${error.message}`));
|
|
274
|
-
page.on('pageerror', error => this.emit('error', `Browser error: ${error.message}`));
|
|
273
|
+
page.on('error', error => this.logError && this.emit('error', `Page crashed: ${error.message}`));
|
|
274
|
+
page.on('pageerror', error => this.logError && this.emit('error', `Browser error: ${error.message}`));
|
|
275
275
|
page.setDefaultTimeout(GLOBAL_TIMEOUT);
|
|
276
276
|
page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
|
|
277
277
|
|