homebridge-melcloud-control 4.3.0-beta.24 → 4.3.0-beta.26
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 +1 -1
- package/package.json +1 -1
- package/src/constants.js +3 -1
- package/src/melcloudata.js +92 -63
package/index.js
CHANGED
|
@@ -194,7 +194,7 @@ class MelCloudPlatform {
|
|
|
194
194
|
|
|
195
195
|
//start impulse generators\
|
|
196
196
|
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
|
|
197
|
-
const timmers = accountType === 'melcloudhome' ? [] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
197
|
+
const timmers = accountType === 'melcloudhome' ? [{ name: 'connect', sampling: 3600000 }, { name: 'checkDevicesList', sampling: 3000 }] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
198
198
|
await melCloud.impulseGenerator.state(true, timmers, false);
|
|
199
199
|
|
|
200
200
|
//stop impulse generator
|
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.0-beta.
|
|
4
|
+
"version": "4.3.0-beta.26",
|
|
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
|
@@ -12,6 +12,7 @@ export const ApiUrls = {
|
|
|
12
12
|
SetAta: "/Device/SetAta",
|
|
13
13
|
SetAtw: "/Device/SetAtw",
|
|
14
14
|
SetErv: "/Device/SetErv",
|
|
15
|
+
GetRefreshUnit: "/Device/RequestRefresh?id=deviceid",
|
|
15
16
|
UpdateApplicationOptions: "/User/UpdateApplicationOptions",
|
|
16
17
|
HolidayModeUpdate: "/HolidayMode/Update",
|
|
17
18
|
EnergyCostReport: "/EnergyCost/Report",
|
|
@@ -42,7 +43,8 @@ export const ApiUrlsHome = {
|
|
|
42
43
|
PutDeviceSettings: "https://melcloudhome.com/dashboard",
|
|
43
44
|
PutScheduleEnabled: "https://melcloudhome.com/ata/deviceid/schedule",
|
|
44
45
|
},
|
|
45
|
-
Origin: "https://melcloudhome.com"
|
|
46
|
+
Origin: "https://melcloudhome.com",
|
|
47
|
+
WebSocketURL: "wss://ws.melcloudhome.com/?hash="
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
export const DeviceType = [
|
package/src/melcloudata.js
CHANGED
|
@@ -53,15 +53,90 @@ class MelCloudAta extends EventEmitter {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
cleanupSocket
|
|
56
|
+
cleanupSocket() {
|
|
57
57
|
if (this.heartbeat) {
|
|
58
58
|
clearInterval(this.heartbeat);
|
|
59
59
|
this.heartbeat = null;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
|
|
62
|
+
if (this.socket) {
|
|
63
|
+
try { this.socket.close(); } catch { }
|
|
64
|
+
this.socket = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
this.socketConnected = false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
reconnect() {
|
|
71
|
+
this.cleanupSocket();
|
|
72
|
+
|
|
73
|
+
const delay = Math.min(this.reconnectDelay, 30000); // max 30s
|
|
74
|
+
if (this.logDebug) this.emit('debug', `Reconnect in ${delay}ms`);
|
|
75
|
+
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
this.reconnectDelay *= 2; // exponential backoff
|
|
78
|
+
this.reconnectDelay = Math.max(1000, this.reconnectDelay);
|
|
79
|
+
this.connectSocket(deviceData);
|
|
80
|
+
}, delay);
|
|
63
81
|
};
|
|
64
82
|
|
|
83
|
+
async connectSocket(deviceData) {
|
|
84
|
+
if (this.connecting || this.socketConnected) return;
|
|
85
|
+
|
|
86
|
+
this.connecting = true;
|
|
87
|
+
try {
|
|
88
|
+
const socket = new WebSocket(`ApiUrlsHome.WebSocketURL${deviceData.WsHeaders.hash}`, { headers: deviceData.WsHeaders.headers })
|
|
89
|
+
.on('error', (err) => {
|
|
90
|
+
if (this.logDebug) this.emit('debug', `Socket error: ${err}`);
|
|
91
|
+
})
|
|
92
|
+
.on('close', () => {
|
|
93
|
+
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
94
|
+
this.reconnect();
|
|
95
|
+
})
|
|
96
|
+
.on('open', () => {
|
|
97
|
+
this.socket = socket;
|
|
98
|
+
this.socketConnected = true;
|
|
99
|
+
this.connecting = false;
|
|
100
|
+
this.reconnectDelay = 1000;
|
|
101
|
+
this.emit('success', `Socket Connect Success`);
|
|
102
|
+
|
|
103
|
+
// heartbeat
|
|
104
|
+
this.heartbeat = setInterval(() => {
|
|
105
|
+
if (socket.readyState === socket.OPEN) {
|
|
106
|
+
if (!this.logDebug) this.emit('warn', `Socket send heartbeat`);
|
|
107
|
+
socket.ping();
|
|
108
|
+
}
|
|
109
|
+
}, 30000);
|
|
110
|
+
})
|
|
111
|
+
.on('pong', () => {
|
|
112
|
+
if (!this.logDebug) this.emit('warn', `Socket received heartbeat`);
|
|
113
|
+
})
|
|
114
|
+
.on('message', (message) => {
|
|
115
|
+
const parsed = JSON.parse(message);
|
|
116
|
+
const msgData = parsed[0].Data;
|
|
117
|
+
const unitId = msgData.id;
|
|
118
|
+
|
|
119
|
+
if (!this.logDebug) {
|
|
120
|
+
const stringifyMessage = JSON.stringify(parsed, null, 2);
|
|
121
|
+
this.emit('warn', `Incoming message: ${stringifyMessage}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (unitId === this.deviceId) {
|
|
125
|
+
const type = parsed[0].messageType;
|
|
126
|
+
|
|
127
|
+
const settings = Object.fromEntries(msgData.settings.map(s => [s.name, s.value]));
|
|
128
|
+
Object.assign(deviceData.Device, settings);
|
|
129
|
+
|
|
130
|
+
this.emit('deviceState', deviceData);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
} catch (e) {
|
|
135
|
+
this.emit('debug', `Socket connection failed: ${e}`);
|
|
136
|
+
reconnect();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
65
140
|
async checkState() {
|
|
66
141
|
try {
|
|
67
142
|
|
|
@@ -77,72 +152,14 @@ class MelCloudAta extends EventEmitter {
|
|
|
77
152
|
deviceData.Device.ActualFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.ActualFanSpeed] ?? deviceData.Device.ActualFanSpeed;
|
|
78
153
|
deviceData.Device.SetFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.SetFanSpeed] ?? deviceData.Device.SetFanSpeed;
|
|
79
154
|
deviceData.Device.VaneVerticalDirection = AirConditioner.VaneVerticalDirectionMapStringToEnum[deviceData.Device.VaneVerticalDirection] ?? deviceData.Device.VaneVerticalDirection;
|
|
80
|
-
deviceData.Device.VaneHorizontalDirection = AirConditioner.VaneHorizontalDirectionMapStringToEnum[deviceData.Device.VaneHorizontalDirection] ?? deviceData.Device.VaneHorizontalDirection
|
|
81
|
-
|
|
82
|
-
if (!this.socketConnected) {
|
|
83
|
-
const socket = new WebSocket(`wss://ws.melcloudhome.com/?hash=${devicesData.WsHeaders.hash}`, { headers: devicesData.WsHeaders.headers })
|
|
84
|
-
.on('error', (error) => {
|
|
85
|
-
if (this.logDebug) this.emit('debug', `Socket error: ${error}`);
|
|
86
|
-
socket.close();
|
|
87
|
-
})
|
|
88
|
-
.on('close', () => {
|
|
89
|
-
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
90
|
-
this.cleanupSocket();
|
|
91
|
-
})
|
|
92
|
-
.on('open', () => {
|
|
93
|
-
// connect to device success
|
|
94
|
-
this.socket = socket;
|
|
95
|
-
this.socketConnected = true;
|
|
96
|
-
this.emit('success', `Socket Connect Success`);
|
|
97
|
-
|
|
98
|
-
// start heartbeat
|
|
99
|
-
this.heartbeat = setInterval(() => {
|
|
100
|
-
if (socket.readyState === socket.OPEN) {
|
|
101
|
-
if (!this.logDebug) this.emit('warn', `Socket send heartbeat`);
|
|
102
|
-
socket.ping();
|
|
103
|
-
}
|
|
104
|
-
}, 30000);
|
|
105
|
-
})
|
|
106
|
-
.on('pong', () => {
|
|
107
|
-
if (!this.logDebug) this.emit('warn', `Socket received heartbeat`);
|
|
108
|
-
})
|
|
109
|
-
.on('message', (message) => {
|
|
110
|
-
const parsedMessage = JSON.parse(message);
|
|
111
|
-
const messageData = parsedMessage[0].Data;
|
|
112
|
-
const unitId = messageData.id;
|
|
113
|
-
const unitType = messageData.unitType;
|
|
114
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
115
|
-
if (!this.logDebug) this.emit('warn', `Incoming message:', ${stringifyMessage}`);
|
|
116
|
-
|
|
117
|
-
switch (unitId) {
|
|
118
|
-
case this.deviceId:
|
|
119
|
-
const messageType = parsedMessage[0].messageType;
|
|
120
|
-
switch (messageType) {
|
|
121
|
-
case 'unitStateChanged':
|
|
122
|
-
const settings = Object.fromEntries(messageData.settings.map(s => [s.name, s.value]));
|
|
123
|
-
Object.assign(deviceData.Device, settings);
|
|
124
|
-
|
|
125
|
-
//emit state
|
|
126
|
-
this.emit('deviceState', deviceData);
|
|
127
|
-
break;
|
|
128
|
-
case 'unitWiFiChanged':
|
|
129
|
-
const state = Object.fromEntries(messageData.settings.map(s => [s.name, s.value]));
|
|
130
|
-
Object.assign(deviceData.Device, state);
|
|
131
|
-
|
|
132
|
-
//emit state
|
|
133
|
-
this.emit('deviceState', deviceData);
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|
|
155
|
+
deviceData.Device.VaneHorizontalDirection = AirConditioner.VaneHorizontalDirectionMapStringToEnum[deviceData.Device.VaneHorizontalDirection] ?? deviceData.Device.VaneHorizontalDirection
|
|
140
156
|
|
|
141
157
|
//read default temps
|
|
142
158
|
const temps = await this.functions.readData(this.defaultTempsFile, true);
|
|
143
159
|
deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
|
|
144
160
|
deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
|
|
145
161
|
|
|
162
|
+
await this.connectSocket(deviceData);
|
|
146
163
|
}
|
|
147
164
|
if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
|
|
148
165
|
|
|
@@ -300,6 +317,11 @@ class MelCloudAta extends EventEmitter {
|
|
|
300
317
|
this.headers.Referer = ApiUrlsHome.Referers.GetPutScenes;
|
|
301
318
|
break;
|
|
302
319
|
default:
|
|
320
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
321
|
+
if (this.logDebug) this.emit('debug', `Socket not open, cannot send`);
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
|
|
303
325
|
if (displayType === 1 && deviceData.Device.OperationMode === 8) {
|
|
304
326
|
deviceData.Device.SetTemperature = (deviceData.Device.DefaultCoolingSetTemperature + deviceData.Device.DefaultHeatingSetTemperature) / 2;
|
|
305
327
|
|
|
@@ -325,7 +347,14 @@ class MelCloudAta extends EventEmitter {
|
|
|
325
347
|
method = 'PUT';
|
|
326
348
|
path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
|
|
327
349
|
this.headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
328
|
-
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
this.socket.send(JSON.stringify(payload));
|
|
353
|
+
return true;
|
|
354
|
+
} catch (err) {
|
|
355
|
+
if (this.logDebug) this.emit('debug', `Socket send error: ${err}`);
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
329
358
|
}
|
|
330
359
|
|
|
331
360
|
this.headers['Content-Type'] = 'application/json; charset=utf-8';
|