node-red-contrib-ccu 4.4.2 → 4.4.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/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ 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.4.3 (2026-09-09)
9
+
10
+ ### Fixed
11
+
12
+ - **Node-RED at 100 % CPU and unresponsive on openccu-lite** (B-27, found on
13
+ the maintainer's Pi 4 with RedMatic 9.4.2). openccu-lite has no ReGaHSS, so
14
+ nothing listens on port 31999 — but the connection node still created the
15
+ ReGaHSS binrpc client in metadata mode, and binrpc up to 4.2.0 armed a new
16
+ reconnect timer for both the `error` and the `close` event of every refused
17
+ connect, so the number of attempts doubled every 2.5 s without bound: about
18
+ 7,000 refused connects per second, 30,000 pending timers and 68 million
19
+ connects in under three hours. The same explosion hit a CCU whenever its
20
+ ReGaHSS was down for a while (restart, backup restore); it just ended when
21
+ the port came back. Two changes: the ReGaHSS client is not created at all
22
+ while the box is an openccu-lite (and is closed when a box becomes one, or
23
+ created when it stops being one), and this release requires
24
+ [binrpc 4.3.0](https://github.com/hobbyquaker/binrpc/blob/master/CHANGELOG.md),
25
+ which keeps a single reconnect timer per client with exponential backoff
26
+ (2.5 s doubling to 30 s) and has `close()`. The connection node now closes
27
+ its rpc clients when it is removed or redeployed, and when it replaces a
28
+ client after a failed call — a replaced binrpc client used to keep
29
+ reconnecting in the background for the life of the process.
30
+
8
31
  ## 4.4.2 (2026-09-09)
9
32
 
10
33
  ### Fixed
@@ -1151,6 +1151,13 @@ module.exports = function (RED) {
1151
1151
  this.metaMode = true;
1152
1152
  this.lastMetaProbe = now();
1153
1153
  clearTimeout(this.regaPollTimeout);
1154
+ if (this.clients.ReGaHSS) {
1155
+ // the box became an openccu-lite while Node-RED was running
1156
+ // (recheckMeta): no ReGaHSS to talk to any more (B-27)
1157
+ this.logger.info('rpc client ReGaHSS closed: openccu-lite has no ReGaHSS');
1158
+ this.closeClient('ReGaHSS');
1159
+ }
1160
+
1154
1161
  this.logger.info(
1155
1162
  'openccu-lite detected (' +
1156
1163
  (info.implementation || 'meta api') +
@@ -1205,6 +1212,10 @@ module.exports = function (RED) {
1205
1212
  return this.startMeta(again);
1206
1213
  }
1207
1214
 
1215
+ if (this.ifaceTypes.ReGaHSS.enabled && !this.clients.ReGaHSS) {
1216
+ this.createClient('ReGaHSS').then(() => this.setIfaceStatus('ReGaHSS', true));
1217
+ }
1218
+
1208
1219
  return this.getRegaData().then(() => this.regaPoll());
1209
1220
  });
1210
1221
  },
@@ -1868,6 +1879,16 @@ module.exports = function (RED) {
1868
1879
  Object.keys(this.ifaceTypes).forEach((iface) => {
1869
1880
  const enabled = config[this.ifaceTypes[iface].conf + 'Enabled'];
1870
1881
  this.ifaceTypes[iface].enabled = enabled;
1882
+ if (enabled && iface === 'ReGaHSS' && this.metaMode) {
1883
+ // B-27: openccu-lite has no ReGaHSS, nothing listens on 31999.
1884
+ // A client would only reconnect forever (with binrpc < 4.3.0
1885
+ // exponentially, at 100 % CPU). The metadata api reports the
1886
+ // interface status instead; the client is created if the box
1887
+ // stops being an openccu-lite (onGone).
1888
+ this.logger.info('rpc client ReGaHSS not created: openccu-lite has no ReGaHSS');
1889
+ return;
1890
+ }
1891
+
1871
1892
  if (enabled) {
1872
1893
  this.createClient(iface)
1873
1894
  .then(() => {
@@ -1896,6 +1917,28 @@ module.exports = function (RED) {
1896
1917
  * @param iface
1897
1918
  * @returns {Promise<any>}
1898
1919
  */
1920
+ /**
1921
+ * Drop an rpc client and stop its reconnect timer (binrpc >= 4.3.0 has
1922
+ * close(); the xmlrpc client holds no timer). Without this a replaced
1923
+ * client kept reconnecting in the background for the life of the process.
1924
+ * @param iface
1925
+ */
1926
+ closeClient(iface) {
1927
+ const client = this.clients[iface];
1928
+ if (!client) {
1929
+ return;
1930
+ }
1931
+
1932
+ delete this.clients[iface];
1933
+ if (typeof client.close === 'function') {
1934
+ try {
1935
+ client.close();
1936
+ } catch (error) {
1937
+ this.logger.debug('close rpc client ' + iface + ': ' + error.message);
1938
+ }
1939
+ }
1940
+ }
1941
+
1899
1942
  createClient(iface) {
1900
1943
  return new Promise((resolve) => {
1901
1944
  const {rpc, port, path, protocol, auth, user, pass, tls, inSecure} = this.ifaceTypes[iface];
@@ -2166,6 +2209,15 @@ module.exports = function (RED) {
2166
2209
  });
2167
2210
  });
2168
2211
 
2212
+ // last: the clients themselves, so their reconnect timers die with the
2213
+ // node (a redeploy used to leave every binrpc client reconnecting)
2214
+ calls.push(() => {
2215
+ Object.keys(this.clients).forEach((iface) => {
2216
+ this.closeClient(iface);
2217
+ });
2218
+ return Promise.resolve();
2219
+ });
2220
+
2169
2221
  this.logger.debug('shutdown tasks: ' + calls.length);
2170
2222
  return calls.reduce((p, task) => p.then(task), Promise.resolve());
2171
2223
  }
@@ -3159,7 +3211,7 @@ module.exports = function (RED) {
3159
3211
  this.clients[iface].methodCall(method, parameters, (err, res) => {
3160
3212
  if (err) {
3161
3213
  this.logger.error(' <', iface, method, err);
3162
- delete this.clients[iface];
3214
+ this.closeClient(iface);
3163
3215
  this.createClient(iface);
3164
3216
  reject(err);
3165
3217
  } else if (res && res.faultCode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-ccu",
3
- "version": "4.4.2",
3
+ "version": "4.4.3",
4
4
  "license": "MIT",
5
5
  "main": "none",
6
6
  "keywords": [
@@ -50,7 +50,7 @@
50
50
  }
51
51
  ],
52
52
  "dependencies": {
53
- "binrpc": "^4.2.0",
53
+ "binrpc": "^4.3.0",
54
54
  "homematic-rega": "^2.0.1",
55
55
  "homematic-xmlrpc": "^2.0.0",
56
56
  "mqtt-wildcard": "^3.0.9"
@@ -75,7 +75,7 @@
75
75
  "overrides": {
76
76
  "hm-simulator": {
77
77
  "homematic-xmlrpc": "^2.0.0",
78
- "binrpc": "^4.2.0"
78
+ "binrpc": "^4.3.0"
79
79
  }
80
80
  },
81
81
  "engines": {