homebridge-melcloud-control 4.10.11 → 4.10.12
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/CHANGELOG.md +7 -0
- package/homebridge-ui/public/index.html +28 -11
- package/homebridge-ui/server.js +22 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -24,6 +24,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
24
24
|
- For plugin < v4.6.0 use Homebridge UI <= v5.5.0
|
|
25
25
|
- For plugin >= v4.6.0 use Homebridge UI >= v5.13.0
|
|
26
26
|
|
|
27
|
+
## [4.10.12] - (28.05.2026)
|
|
28
|
+
|
|
29
|
+
### Changes
|
|
30
|
+
|
|
31
|
+
- fix: plugin config UI — Connect to MELCloud spinner no longer hangs indefinitely
|
|
32
|
+
- fix: server responses now delivered via HTTP file polling, bypassing unreliable Socket.IO server→client channel in Homebridge Config UI
|
|
33
|
+
|
|
27
34
|
## [4.10.9] - (28.05.2026)
|
|
28
35
|
|
|
29
36
|
### Changes
|
|
@@ -257,6 +257,31 @@
|
|
|
257
257
|
return parts.length ? `${type}: ${parts.join(', ')}` : '';
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
// Trigger the operation via homebridge.request() (Socket.IO — unreliable for response),
|
|
261
|
+
// then poll _result.json via HTTP until the server writes the result.
|
|
262
|
+
async function requestViaFile(path, body, ms = 15000) {
|
|
263
|
+
const ts = Date.now();
|
|
264
|
+
|
|
265
|
+
homebridge.request(path, body).catch(() => {}); // fire-and-forget
|
|
266
|
+
|
|
267
|
+
const deadline = ts + ms;
|
|
268
|
+
while (Date.now() < deadline) {
|
|
269
|
+
await new Promise(r => setTimeout(r, 300));
|
|
270
|
+
try {
|
|
271
|
+
const res = await fetch(`_result.json?t=${Date.now()}`);
|
|
272
|
+
if (!res.ok) continue;
|
|
273
|
+
const data = await res.json();
|
|
274
|
+
if ((data.ts ?? 0) >= ts) {
|
|
275
|
+
if (data.ok) return data.data ?? true;
|
|
276
|
+
throw new Error(data.error || 'Unknown error');
|
|
277
|
+
}
|
|
278
|
+
} catch (e) {
|
|
279
|
+
if (e.message && e.message !== 'Failed to fetch') throw e;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
throw new Error('Request timed out');
|
|
283
|
+
}
|
|
284
|
+
|
|
260
285
|
// Login & Sync Logic
|
|
261
286
|
$('logIn').addEventListener('click', async () => {
|
|
262
287
|
$('logIn').disabled = true;
|
|
@@ -268,16 +293,10 @@
|
|
|
268
293
|
updateInfo('info2', '', fontColor);
|
|
269
294
|
homebridge.showSpinner();
|
|
270
295
|
|
|
271
|
-
const statusListener = event => updateInfo('info', event.data, fontColor);
|
|
272
|
-
homebridge.addEventListener('status', statusListener);
|
|
273
|
-
|
|
274
296
|
try {
|
|
275
297
|
const account = this.account;
|
|
276
298
|
const accountTypeMelcloud = account.type === 'melcloud';
|
|
277
|
-
const
|
|
278
|
-
setTimeout(() => reject(new Error('Connection timed out — please try again')), 160_000)
|
|
279
|
-
);
|
|
280
|
-
const melCloudDevicesData = await Promise.race([homebridge.request('/connect', account), connectTimeout]);
|
|
299
|
+
const melCloudDevicesData = await requestViaFile('/connect', account, 160_000);
|
|
281
300
|
|
|
282
301
|
if (!melCloudDevicesData.State) {
|
|
283
302
|
updateInfo('info', melCloudDevicesData.Status, 'red');
|
|
@@ -457,13 +476,11 @@
|
|
|
457
476
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
458
477
|
await homebridge.savePluginConfig();
|
|
459
478
|
} catch (error) {
|
|
460
|
-
|
|
461
|
-
updateInfo('
|
|
462
|
-
updateInfo('info1', msg, 'red');
|
|
479
|
+
updateInfo('info', `Prepare config error`, "red");
|
|
480
|
+
updateInfo('info1', `Error: ${JSON.stringify(error)}`, "red");
|
|
463
481
|
} finally {
|
|
464
482
|
homebridge.hideSpinner();
|
|
465
483
|
$('logIn').disabled = false;
|
|
466
|
-
try { homebridge.removeEventListener?.('status', statusListener); } catch (_) {}
|
|
467
484
|
}
|
|
468
485
|
});
|
|
469
486
|
})();
|
package/homebridge-ui/server.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils';
|
|
2
|
+
import { promises as fsPromises } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
2
5
|
import MelCloud from '../src/melcloud.js';
|
|
3
6
|
import MelCloudHome from '../src/melcloudhome.js';
|
|
4
7
|
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
5
10
|
class PluginUiServer extends HomebridgePluginUiServer {
|
|
6
11
|
constructor() {
|
|
7
12
|
super();
|
|
@@ -13,6 +18,15 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
13
18
|
this.ready();
|
|
14
19
|
};
|
|
15
20
|
|
|
21
|
+
async writeResult(result) {
|
|
22
|
+
try {
|
|
23
|
+
await fsPromises.writeFile(
|
|
24
|
+
join(__dirname, 'public', '_result.json'),
|
|
25
|
+
JSON.stringify({ ...result, ts: Date.now() })
|
|
26
|
+
);
|
|
27
|
+
} catch (_) {}
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
withTimeout(promise, ms, label) {
|
|
17
31
|
const timer = new Promise((_, reject) =>
|
|
18
32
|
setTimeout(() => reject(new Error(`${label} timed out after ${ms / 1000}s`)), ms)
|
|
@@ -26,13 +40,19 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
26
40
|
try {
|
|
27
41
|
this.pushEvent('status', 'Connecting to account...');
|
|
28
42
|
const melCloudAccountData = await this.withTimeout(melCloudClass.connect(), 90_000, 'connect');
|
|
29
|
-
if (!melCloudAccountData.State)
|
|
43
|
+
if (!melCloudAccountData.State) {
|
|
44
|
+
await this.writeResult({ ok: true, data: melCloudAccountData });
|
|
45
|
+
return melCloudAccountData;
|
|
46
|
+
}
|
|
30
47
|
|
|
31
48
|
this.pushEvent('status', 'Loading devices...');
|
|
32
49
|
const melCloudDevicesData = await this.withTimeout(melCloudClass.checkDevicesList(), 60_000, 'checkDevicesList');
|
|
50
|
+
await this.writeResult({ ok: true, data: melCloudDevicesData });
|
|
33
51
|
return melCloudDevicesData;
|
|
34
52
|
} catch (error) {
|
|
35
|
-
|
|
53
|
+
const msg = error.message ?? String(error);
|
|
54
|
+
await this.writeResult({ ok: false, error: msg });
|
|
55
|
+
throw new Error(msg);
|
|
36
56
|
}
|
|
37
57
|
}
|
|
38
58
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.10.
|
|
4
|
+
"version": "4.10.12",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|