node-red-contrib-ccu 4.5.1 → 4.5.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/CHANGELOG.md +15 -0
- package/nodes/ccu-connection.js +65 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ Notable changes to node-red-contrib-ccu. Format follows
|
|
|
5
5
|
user-visible symptom and the cause, not the commit list (the release notes
|
|
6
6
|
append commits automatically).
|
|
7
7
|
|
|
8
|
+
## 4.5.2 (2026-10-02)
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **No HmIP-RF events for up to 10 minutes after hmipserver restarted**
|
|
13
|
+
(B-37). hmipserver keeps its subscribers over a restart: it calls
|
|
14
|
+
`listDevices` and `newDevices` on them and answers their pings, but
|
|
15
|
+
delivers no event to such a kept subscription until it is subscribed
|
|
16
|
+
afresh. The liveness ping saw its PONG and was content, so the
|
|
17
|
+
subscription came back only with the 600 s silence timeout. A
|
|
18
|
+
`listDevices` or `newDevices` call that does not follow the connection's
|
|
19
|
+
own `init` now makes it subscribe again at once, and events are back
|
|
20
|
+
within seconds. A device paired while Node-RED runs costs one such `init`
|
|
21
|
+
as well.
|
|
22
|
+
|
|
8
23
|
## 4.5.1 (2026-10-02)
|
|
9
24
|
|
|
10
25
|
### Fixed
|
package/nodes/ccu-connection.js
CHANGED
|
@@ -23,6 +23,11 @@ const binrpc = require('binrpc');
|
|
|
23
23
|
|
|
24
24
|
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
25
25
|
|
|
26
|
+
// B-37: how long after our init the process's listDevices/newDevices still belong to it -
|
|
27
|
+
// hmipserver answers the init at once and calls listDevices and newDevices afterwards. Such a
|
|
28
|
+
// call later than this is the process calling on its own: it restarted and kept the subscription.
|
|
29
|
+
const INIT_GRACE_MS = 30000;
|
|
30
|
+
|
|
26
31
|
/**
|
|
27
32
|
* check if an object is iterable
|
|
28
33
|
* @link https://stackoverflow.com/a/37837872
|
|
@@ -630,6 +635,10 @@ module.exports = function (RED) {
|
|
|
630
635
|
// and which of them are waiting for an unreachable process
|
|
631
636
|
this.initRetry = {};
|
|
632
637
|
this.ifaceWaiting = {};
|
|
638
|
+
// B-37: per interface {inFlight, at, reinit} of our own init, and how long after it the
|
|
639
|
+
// process's listDevices/newDevices still answer it (see keptSubscription())
|
|
640
|
+
this.initState = {};
|
|
641
|
+
this.initGrace = INIT_GRACE_MS;
|
|
633
642
|
this.closing = false;
|
|
634
643
|
this.serverError = {};
|
|
635
644
|
this.queueTimeout = Number.parseInt(config.queueTimeout, 10) || 5000;
|
|
@@ -2345,9 +2354,12 @@ module.exports = function (RED) {
|
|
|
2345
2354
|
this.createClient(iface, {quiet: true});
|
|
2346
2355
|
}
|
|
2347
2356
|
|
|
2357
|
+
// B-37: listDevices/newDevices while this init runs or shortly after belong to it
|
|
2358
|
+
this.initState[iface] = {inFlight: true, at: 0, reinit: false};
|
|
2348
2359
|
// a failed init is logged by the retry (InitRetry), not here
|
|
2349
2360
|
this.methodCall(iface, 'init', [initUrl, initId], {quiet: true})
|
|
2350
2361
|
.then(() => {
|
|
2362
|
+
this.initState[iface] = {inFlight: false, at: now(), reinit: false};
|
|
2351
2363
|
// the ping/re-init liveness starts after a successful init only;
|
|
2352
2364
|
// a failed one is retried by rpcInitFailed (task 11)
|
|
2353
2365
|
if (this.ifaceTypes[iface].ping && !this.closing) {
|
|
@@ -2370,10 +2382,57 @@ module.exports = function (RED) {
|
|
|
2370
2382
|
resolve(iface);
|
|
2371
2383
|
}
|
|
2372
2384
|
})
|
|
2373
|
-
.catch((error) =>
|
|
2385
|
+
.catch((error) => {
|
|
2386
|
+
this.initState[iface] = {inFlight: false, at: 0, reinit: false};
|
|
2387
|
+
reject(error);
|
|
2388
|
+
});
|
|
2374
2389
|
});
|
|
2375
2390
|
}
|
|
2376
2391
|
|
|
2392
|
+
/**
|
|
2393
|
+
* B-37: the interface process called listDevices or newDevices. Outside our own init it
|
|
2394
|
+
* restarted and kept the subscription: hmipserver restores its subscribers from its
|
|
2395
|
+
* handlers file, calls listDevices and newDevices on them and answers their pings (the
|
|
2396
|
+
* PONG arrives, so neither the liveness ping nor the 600 s watchdog fires), but delivers
|
|
2397
|
+
* no event to such a kept subscription until a fresh init (openccu-lite B-286, measured).
|
|
2398
|
+
* So init again at once. A device paired while Node-RED runs costs one init the same way.
|
|
2399
|
+
* @param iface
|
|
2400
|
+
* @param {string} method
|
|
2401
|
+
* @returns {boolean} whether a fresh init was started
|
|
2402
|
+
*/
|
|
2403
|
+
keptSubscription(iface, method) {
|
|
2404
|
+
const type = this.ifaceTypes[iface];
|
|
2405
|
+
const state = this.initState[iface];
|
|
2406
|
+
if (this.closing || !type || !type.enabled || !type.init) {
|
|
2407
|
+
return false;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
// never subscribed, an init running or already started for this restart
|
|
2411
|
+
if (!state || state.inFlight || state.reinit || !state.at) {
|
|
2412
|
+
return false;
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
if (this.initRetry[iface] && this.initRetry[iface].pending) {
|
|
2416
|
+
return false;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
if (now() - state.at < this.initGrace) {
|
|
2420
|
+
return false;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
state.reinit = true;
|
|
2424
|
+
this.logger.info(
|
|
2425
|
+
iface +
|
|
2426
|
+
': ' +
|
|
2427
|
+
method +
|
|
2428
|
+
' outside our init - the interface process restarted and kept the subscription, subscribing afresh',
|
|
2429
|
+
);
|
|
2430
|
+
this.rpcInit(iface)
|
|
2431
|
+
.then(() => this.rpcInitSucceeded(iface))
|
|
2432
|
+
.catch((error) => this.rpcInitFailed(iface, error));
|
|
2433
|
+
return true;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2377
2436
|
/**
|
|
2378
2437
|
* Returns the links of a specific channel
|
|
2379
2438
|
* @param {String} iface
|
|
@@ -3457,7 +3516,8 @@ module.exports = function (RED) {
|
|
|
3457
3516
|
this.saveMetadata();
|
|
3458
3517
|
}
|
|
3459
3518
|
|
|
3460
|
-
|
|
3519
|
+
// B-37: a restart that kept the subscription gets a fresh init, no ping
|
|
3520
|
+
if (iface && !this.keptSubscription(iface, 'newDevices')) {
|
|
3461
3521
|
this.deviceCallback(iface);
|
|
3462
3522
|
}
|
|
3463
3523
|
},
|
|
@@ -3498,6 +3558,9 @@ module.exports = function (RED) {
|
|
|
3498
3558
|
|
|
3499
3559
|
this.logger.debug(' >', iface, 'listDevices', JSON.stringify(res));
|
|
3500
3560
|
callback(null, res);
|
|
3561
|
+
if (iface) {
|
|
3562
|
+
this.keptSubscription(iface, 'listDevices');
|
|
3563
|
+
}
|
|
3501
3564
|
},
|
|
3502
3565
|
event: (_, parameters, callback) => {
|
|
3503
3566
|
const iface = this.rpcIface('event', parameters);
|