homebridge-melcloud-control 4.9.2-beta.1 → 4.9.2-beta.3
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 -0
- package/package.json +1 -1
- package/src/melcloudhome.js +30 -58
package/index.js
CHANGED
|
@@ -112,6 +112,7 @@ class MelCloudPlatform {
|
|
|
112
112
|
}
|
|
113
113
|
if (logLevel.debug) log.info(melCloudDevicesData.Status);
|
|
114
114
|
|
|
115
|
+
|
|
115
116
|
//filter configured devices
|
|
116
117
|
const devicesIds = (melCloudDevicesData.Devices ?? []).map(d => String(d.DeviceID));
|
|
117
118
|
const ataDevices = (account.ataDevices || []).filter(d => (d.displayType ?? 0) > 0 && devicesIds.includes(d.id));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.9.2-beta.
|
|
4
|
+
"version": "4.9.2-beta.3",
|
|
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/melcloudhome.js
CHANGED
|
@@ -40,6 +40,15 @@ class MelCloudHome extends EventEmitter {
|
|
|
40
40
|
// Flaga zapobiegająca wielokrotnemu dodaniu interceptorów
|
|
41
41
|
this.interceptorsAttached = false;
|
|
42
42
|
|
|
43
|
+
// WebSocket state
|
|
44
|
+
this.socket = null;
|
|
45
|
+
this.socketConnected = false;
|
|
46
|
+
this.connecting = false;
|
|
47
|
+
this.heartbeat = null;
|
|
48
|
+
this.reconnectTimer = null;
|
|
49
|
+
this.reconnectDelay = 5_000; // ms, rośnie wykładniczo do reconnectDelayMax
|
|
50
|
+
this.reconnectDelayMax = 300_000; // 5 minut
|
|
51
|
+
|
|
43
52
|
if (pluginStart) {
|
|
44
53
|
this.impulseGenerator = new ImpulseGenerator()
|
|
45
54
|
.on('checkDevicesList', async () => {
|
|
@@ -53,44 +62,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
53
62
|
|
|
54
63
|
// ── WebSocket ─────────────────────────────────────────────────────────────
|
|
55
64
|
|
|
56
|
-
// Pobiera hash do URL WebSocket.
|
|
57
|
-
// Sprawdza kolejno: /api/configuration → /api/user/context → access token jako fallback.
|
|
58
|
-
// Gdy znajdziesz właściwe źródło, uproszcz tę metodę.
|
|
59
|
-
async fetchWsHash() {
|
|
60
|
-
// Próba 1: /api/configuration
|
|
61
|
-
try {
|
|
62
|
-
const resp = await this.client.get(ApiUrls.Home.Get.Config);
|
|
63
|
-
const hash = resp.data?.wsHash ?? resp.data?.webSocketHash ?? resp.data?.hash ?? null;
|
|
64
|
-
if (hash) {
|
|
65
|
-
if (!this.logDebug) this.emit('debug', `WS hash from configuration: ${hash}`);
|
|
66
|
-
return hash;
|
|
67
|
-
}
|
|
68
|
-
if (!this.logDebug) this.emit('debug', `Configuration response (no hash found): ${JSON.stringify(resp.data)}`);
|
|
69
|
-
} catch (err) {
|
|
70
|
-
if (!this.logDebug) this.emit('debug', `fetchWsHash: configuration failed: ${err.message}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Próba 2: /api/user/context (buildingsList może zawierać hash)
|
|
74
|
-
try {
|
|
75
|
-
const resp = await this.client.get(ApiUrls.Home.Get.Context);
|
|
76
|
-
const hash = resp.data?.wsHash ?? resp.data?.webSocketHash ?? resp.data?.hash ?? null;
|
|
77
|
-
if (hash) {
|
|
78
|
-
if (!this.logDebug) this.emit('debug', `WS hash from context: ${hash}`);
|
|
79
|
-
return hash;
|
|
80
|
-
}
|
|
81
|
-
} catch (err) {
|
|
82
|
-
if (!this.logDebug) this.emit('debug', `fetchWsHash: context failed: ${err.message}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Fallback: access token (JWT) — niektóre implementacje używają go bezpośrednio
|
|
86
|
-
if (this.accessToken) {
|
|
87
|
-
if (!this.logDebug) this.emit('debug', 'fetchWsHash: falling back to access token as hash');
|
|
88
|
-
return this.accessToken;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
throw new Error('Unable to obtain WebSocket hash — update fetchWsHash() when source is known');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
65
|
cleanupSocket() {
|
|
95
66
|
if (this.heartbeat) {
|
|
96
67
|
clearInterval(this.heartbeat);
|
|
@@ -108,11 +79,12 @@ class MelCloudHome extends EventEmitter {
|
|
|
108
79
|
|
|
109
80
|
let hash;
|
|
110
81
|
try {
|
|
111
|
-
|
|
82
|
+
const resp = await this.client.get(ApiUrls.Home.Get.Context);
|
|
83
|
+
hash = resp.data.id ?? null;
|
|
112
84
|
} catch (err) {
|
|
113
|
-
if (
|
|
85
|
+
if (this.logError) this.emit('error', `connectSocket: cannot get WS hash: ${err.message}`);
|
|
114
86
|
this.connecting = false;
|
|
115
|
-
this.
|
|
87
|
+
this.scheduleReconnect();
|
|
116
88
|
return;
|
|
117
89
|
}
|
|
118
90
|
|
|
@@ -123,7 +95,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
123
95
|
'Cache-Control': 'no-cache',
|
|
124
96
|
};
|
|
125
97
|
|
|
126
|
-
if (
|
|
98
|
+
if (this.logDebug) this.emit('debug', `Connecting WebSocket: ${url.slice(0, 60)}...`);
|
|
127
99
|
|
|
128
100
|
try {
|
|
129
101
|
const ws = new WebSocket(url, { headers });
|
|
@@ -137,16 +109,16 @@ class MelCloudHome extends EventEmitter {
|
|
|
137
109
|
ws.on('close', () => {
|
|
138
110
|
if (this.logDebug) this.emit('debug', 'Web socket closed');
|
|
139
111
|
this.cleanupSocket();
|
|
140
|
-
this.
|
|
112
|
+
this.scheduleReconnect();
|
|
141
113
|
});
|
|
142
114
|
|
|
143
115
|
ws.on('open', () => {
|
|
144
116
|
this.socketConnected = true;
|
|
145
117
|
this.connecting = false;
|
|
146
|
-
this.
|
|
147
|
-
if (this.
|
|
148
|
-
clearTimeout(this.
|
|
149
|
-
this.
|
|
118
|
+
this.reconnectDelay = 5_000; // reset backoff po udanym połączeniu
|
|
119
|
+
if (this.reconnectTimer) {
|
|
120
|
+
clearTimeout(this.reconnectTimer);
|
|
121
|
+
this.reconnectTimer = null;
|
|
150
122
|
}
|
|
151
123
|
if (this.logDebug) this.emit('debug', 'Web Socket Connected');
|
|
152
124
|
|
|
@@ -177,26 +149,25 @@ class MelCloudHome extends EventEmitter {
|
|
|
177
149
|
if (this.logError) this.emit('error', `Web socket message parse error: ${err.message}`);
|
|
178
150
|
}
|
|
179
151
|
});
|
|
180
|
-
|
|
181
152
|
} catch (error) {
|
|
182
153
|
if (this.logError) this.emit('error', `Web socket connection failed: ${error.message}`);
|
|
183
154
|
this.cleanupSocket();
|
|
184
|
-
this.
|
|
155
|
+
this.scheduleReconnect();
|
|
185
156
|
}
|
|
186
157
|
}
|
|
187
158
|
|
|
188
159
|
// Wykładniczy backoff: 5s → 10s → 20s → ... → max 5 minut
|
|
189
|
-
|
|
190
|
-
if (this.
|
|
160
|
+
scheduleReconnect() {
|
|
161
|
+
if (this.reconnectTimer) return; // już zaplanowany
|
|
191
162
|
|
|
192
|
-
if (this.logDebug) this.emit('debug', `Web socket reconnecting in ${this.
|
|
163
|
+
if (this.logDebug) this.emit('debug', `Web socket reconnecting in ${this.reconnectDelay / 1000}s...`);
|
|
193
164
|
|
|
194
|
-
this.
|
|
195
|
-
this.
|
|
165
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
166
|
+
this.reconnectTimer = null;
|
|
196
167
|
await this.connectSocket();
|
|
197
|
-
}, this.
|
|
168
|
+
}, this.reconnectDelay);
|
|
198
169
|
|
|
199
|
-
this.
|
|
170
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.reconnectDelayMax);
|
|
200
171
|
}
|
|
201
172
|
|
|
202
173
|
// ── Utils ─────────────────────────────────────────────────────────────────
|
|
@@ -458,12 +429,13 @@ class MelCloudHome extends EventEmitter {
|
|
|
458
429
|
this.emit('client', this.client);
|
|
459
430
|
}
|
|
460
431
|
|
|
461
|
-
connectInfo.State = exchangeRes;
|
|
462
|
-
connectInfo.Status = exchangeRes ? 'Connect Success' : 'Connect Failed at token exchange';
|
|
463
432
|
this.connectSocket().catch(err => {
|
|
464
433
|
if (this.logError) this.emit('error', `Initial WebSocket connect failed: ${err.message}`);
|
|
465
434
|
});
|
|
466
435
|
|
|
436
|
+
connectInfo.State = exchangeRes;
|
|
437
|
+
connectInfo.Status = `Connect Success${this.socketConnected ? ', Web Socket Connected' : ''}`;
|
|
438
|
+
|
|
467
439
|
return connectInfo;
|
|
468
440
|
}
|
|
469
441
|
|