homebridge-melcloud-control 4.3.3-beta.0 → 4.3.3-beta.1
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/melcloudatw.js +123 -9
- 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.1",
|
|
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/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,6 +27,10 @@ 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
35
|
//lock flags
|
|
31
36
|
this.locks = false;
|
|
@@ -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
|
|