hm2mqtt 3.6.2 → 3.7.0
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/README.md +12 -0
- package/index.js +58 -5
- package/lib/interfaces.js +110 -10
- package/lib/rpc.js +47 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -76,6 +76,9 @@ the automatic detection.
|
|
|
76
76
|
|
|
77
77
|
The addon packages are marked `-beta` until someone has confirmed an install on real hardware.
|
|
78
78
|
|
|
79
|
+
On [openccu-lite](#openccu-lite) the same package is installed from the system's addon catalogue
|
|
80
|
+
(_Addons → Catalogue_); there is nothing to download by hand.
|
|
81
|
+
|
|
79
82
|
## Finding the CCU
|
|
80
83
|
|
|
81
84
|
```
|
|
@@ -271,6 +274,15 @@ names, rooms and functions from the box's **metadata API** instead of from the R
|
|
|
271
274
|
RaspberryMatic or OpenCCU answers 404 there and everything stays exactly as before — the same
|
|
272
275
|
configuration works on both, which is what a backup restored on the other kind of box needs.
|
|
273
276
|
|
|
277
|
+
**Install and start.** On the system itself hm2mqtt is installed from the addon catalogue
|
|
278
|
+
(_Addons → Catalogue_, the same package as on a CCU; its manifest `openccu-lite.json` ships in the
|
|
279
|
+
package). The addon declares the early start: its unit starts at boot before `rfd` and `hmipserver`,
|
|
280
|
+
and hm2mqtt waits for them quietly — an interface that does not answer yet is probed and subscribed
|
|
281
|
+
again after 1, 2, 4 and 8 s, then every 15 s, with one info line and no warning, and one that comes
|
|
282
|
+
up later (or after a restart of its process) is picked up without a restart of hm2mqtt. The system
|
|
283
|
+
can switch the early start off on its Addons page; the unit then starts after both. The broker URL
|
|
284
|
+
is the one setting to make, as on a CCU. The log is in the journal (`journalctl -u addon-hm2mqtt`).
|
|
285
|
+
|
|
274
286
|
Names arrive as a snapshot at start and then over the box's event stream (`/api/meta/v1/events/sse`):
|
|
275
287
|
a rename in the box's UI is in the topics about a second later, without a poll and without a
|
|
276
288
|
restart. The store is cached in `meta.json` in the state directory, so a start without the box
|
package/index.js
CHANGED
|
@@ -15,7 +15,15 @@ import {Rega} from 'homematic-rega';
|
|
|
15
15
|
import config from './config.js';
|
|
16
16
|
import pkg from './package.json' with {type: 'json'};
|
|
17
17
|
import {handle as handleInstall} from './lib/install.js';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
parseInterfaces,
|
|
20
|
+
probeInterfaces,
|
|
21
|
+
watchInterfaces,
|
|
22
|
+
interfaceConfig,
|
|
23
|
+
detectLocal,
|
|
24
|
+
regaPort,
|
|
25
|
+
INTERFACE_NAMES,
|
|
26
|
+
} from './lib/interfaces.js';
|
|
19
27
|
import {RpcServers, RpcConnection} from './lib/rpc.js';
|
|
20
28
|
import {Metadata} from './lib/metadata.js';
|
|
21
29
|
import {ValueStore, hmBlock, isEvent} from './lib/values.js';
|
|
@@ -69,6 +77,8 @@ const STOP_TIMEOUT_MS = 1500;
|
|
|
69
77
|
const RESOLVE_RETRY_MS = 10000;
|
|
70
78
|
/** how often an unreachable box is probed for the metadata api again (H-47) */
|
|
71
79
|
const REDETECT_MS = 60000;
|
|
80
|
+
/** how long the local-mode probe waits for the interface processes on a loopback address (B-1) */
|
|
81
|
+
const LOCAL_WAIT_MS = 120000;
|
|
72
82
|
|
|
73
83
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
74
84
|
/** the configured CCU address (the `ccu` field of every payload); connections use the resolved ip */
|
|
@@ -234,7 +244,16 @@ for (const pattern of listenPatterns) {
|
|
|
234
244
|
*/
|
|
235
245
|
// an explicit --ccu-tls is a tunnel or a proxy by definition - local mode would silently drop the
|
|
236
246
|
// TLS and the ports the user asked for, so it is never probed into, only chosen with --local
|
|
237
|
-
|
|
247
|
+
// on a loopback address where nothing answers yet the interface processes may still be starting
|
|
248
|
+
// (a boot, openccu-lite's early start): wait for them up to two minutes before deciding (B-1)
|
|
249
|
+
const localMode =
|
|
250
|
+
config.local === undefined
|
|
251
|
+
? !config.ccuTls &&
|
|
252
|
+
(await detectLocal(host, {
|
|
253
|
+
waitMs: LOCAL_WAIT_MS,
|
|
254
|
+
onWait: () => log.info('no interface process answers on', host, 'yet - waiting for them'),
|
|
255
|
+
}))
|
|
256
|
+
: Boolean(config.local);
|
|
238
257
|
if (localMode) {
|
|
239
258
|
log.info('local mode: BidCos over binrpc (32001/32000), hmipserver on 32010, ReGa on 8183');
|
|
240
259
|
}
|
|
@@ -331,6 +350,8 @@ function publishTopic(topic, value, {retain = true, extra, ts, lc} = {}) {
|
|
|
331
350
|
let indexTimer = null;
|
|
332
351
|
|
|
333
352
|
let enabled = [];
|
|
353
|
+
/** --interfaces auto: the interfaces the start probe missed, probed on until they answer (B-1) */
|
|
354
|
+
let interfaceWatch = null;
|
|
334
355
|
const connections = {};
|
|
335
356
|
let servers = null;
|
|
336
357
|
let regaOk = false;
|
|
@@ -995,10 +1016,16 @@ async function start() {
|
|
|
995
1016
|
} else if (regaSync) {
|
|
996
1017
|
regaSync.setHost(ccuIp);
|
|
997
1018
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1019
|
+
const listed = parseInterfaces(config.interfaces);
|
|
1020
|
+
enabled = listed || (await probeInterfaces(ccuIp, {tls: config.ccuTls, local: localMode}));
|
|
1000
1021
|
if (enabled.length === 0) {
|
|
1001
|
-
|
|
1022
|
+
// nothing answers yet: on the box itself the interface processes are still starting, which is
|
|
1023
|
+
// no fault; the probe goes on (B-1)
|
|
1024
|
+
(localMode ? log.info : log.warn)(
|
|
1025
|
+
'no interface answers on',
|
|
1026
|
+
host,
|
|
1027
|
+
'yet - probing again' + (localMode ? '' : '; check --ccu-address / --interfaces if this stays'),
|
|
1028
|
+
);
|
|
1002
1029
|
}
|
|
1003
1030
|
log.info('interfaces:', enabled.join(', ') || '(none)');
|
|
1004
1031
|
// locally the CCU calls back over loopback, so nothing of ours needs to listen on the LAN
|
|
@@ -1009,6 +1036,7 @@ async function start() {
|
|
|
1009
1036
|
xmlrpcPort: config.xmlrpcPort,
|
|
1010
1037
|
binrpcPort: config.binrpcPort,
|
|
1011
1038
|
log,
|
|
1039
|
+
ownPrefix: `hm2mqtt_${config.name}_`,
|
|
1012
1040
|
});
|
|
1013
1041
|
for (const iface of enabled) {
|
|
1014
1042
|
connections[iface] = createConnection(iface);
|
|
@@ -1022,6 +1050,13 @@ async function start() {
|
|
|
1022
1050
|
await Promise.all(enabled.map((iface) => connections[iface].start()));
|
|
1023
1051
|
updateConnected();
|
|
1024
1052
|
adapter.publishInfo();
|
|
1053
|
+
if (!listed) {
|
|
1054
|
+
interfaceWatch = watchInterfaces(
|
|
1055
|
+
ccuIp,
|
|
1056
|
+
INTERFACE_NAMES.filter((iface) => !enabled.includes(iface)),
|
|
1057
|
+
{tls: config.ccuTls, local: localMode, onFound: addInterface},
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1025
1060
|
|
|
1026
1061
|
timers.push(setInterval(publishCounters, COUNTER_INTERVAL_MS));
|
|
1027
1062
|
if (config.dutyCycleInterval > 0) {
|
|
@@ -1031,7 +1066,25 @@ async function start() {
|
|
|
1031
1066
|
timers.push(setInterval(() => values.save(), VALUES_SAVE_MS));
|
|
1032
1067
|
}
|
|
1033
1068
|
|
|
1069
|
+
/** An interface the start probe missed answers now: subscribe to it like to the others (B-1). */
|
|
1070
|
+
function addInterface(iface) {
|
|
1071
|
+
if (enabled.includes(iface)) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
enabled = INTERFACE_NAMES.filter((name) => name === iface || enabled.includes(name));
|
|
1075
|
+
log.info('interface', iface, 'answers now - interfaces:', enabled.join(', '));
|
|
1076
|
+
connections[iface] = createConnection(iface);
|
|
1077
|
+
publishItem(`interface/${iface}/connected`, false);
|
|
1078
|
+
rebuildIndex();
|
|
1079
|
+
updateConnected();
|
|
1080
|
+
adapter.publishInfo();
|
|
1081
|
+
connections[iface].start().catch((err) => log.warn('rpc', iface, 'start failed:', err.message));
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1034
1084
|
async function shutdown() {
|
|
1085
|
+
if (interfaceWatch) {
|
|
1086
|
+
interfaceWatch.stop();
|
|
1087
|
+
}
|
|
1035
1088
|
for (const timer of timers) {
|
|
1036
1089
|
clearInterval(timer);
|
|
1037
1090
|
}
|
package/lib/interfaces.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import net from 'node:net';
|
|
12
|
+
import {initRetryDelay} from './rpc.js';
|
|
12
13
|
|
|
13
14
|
export const INTERFACES = {
|
|
14
15
|
'BidCos-RF': {
|
|
@@ -139,12 +140,15 @@ function portOpen(host, port, timeout) {
|
|
|
139
140
|
/**
|
|
140
141
|
* Probes which interface ports answer on the CCU (--interfaces auto).
|
|
141
142
|
* @param {string} host
|
|
142
|
-
* @param {{tls?: boolean, local?: boolean, timeout?: number, connect?: Function}} [options]
|
|
143
|
-
* @returns {Promise<string[]>} interface names in table order
|
|
143
|
+
* @param {{tls?: boolean, local?: boolean, timeout?: number, connect?: Function, names?: string[]}} [options]
|
|
144
|
+
* @returns {Promise<string[]>} interface names in table order (of `names`)
|
|
144
145
|
*/
|
|
145
|
-
export async function probeInterfaces(
|
|
146
|
+
export async function probeInterfaces(
|
|
147
|
+
host,
|
|
148
|
+
{tls = false, local = false, timeout = 2000, connect = portOpen, names = INTERFACE_NAMES} = {},
|
|
149
|
+
) {
|
|
146
150
|
const found = [];
|
|
147
|
-
for (const name of
|
|
151
|
+
for (const name of names) {
|
|
148
152
|
const {port} = interfaceConfig(name, {tls, local});
|
|
149
153
|
if (await connect(host, port, timeout)) {
|
|
150
154
|
found.push(name);
|
|
@@ -153,6 +157,67 @@ export async function probeInterfaces(host, {tls = false, local = false, timeout
|
|
|
153
157
|
return found;
|
|
154
158
|
}
|
|
155
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Probes the interfaces `--interfaces auto` did not find at the start again, on the schedule of a
|
|
162
|
+
* failed init (1, 2, 4, 8 s, then every 15 s), and hands each one that answers to `onFound` - once;
|
|
163
|
+
* the others are probed on until they answer or `stop()` is called (B-1). An interface process that
|
|
164
|
+
* comes up after hm2mqtt (openccu-lite's early start, a restart of hmipserver, a LAN gateway added
|
|
165
|
+
* later) is picked up this way without a restart.
|
|
166
|
+
* @param {string} host
|
|
167
|
+
* @param {string[]} missing interface names
|
|
168
|
+
* @param {object} o
|
|
169
|
+
* @param {(name: string) => void} o.onFound
|
|
170
|
+
* @param {boolean} [o.tls]
|
|
171
|
+
* @param {boolean} [o.local]
|
|
172
|
+
* @param {number} [o.timeout] ms per probe
|
|
173
|
+
* @param {Function} [o.connect] for tests
|
|
174
|
+
* @param {{setTimeout: Function, clearTimeout: Function}} [o.timers] for tests
|
|
175
|
+
* @returns {{stop: () => void, missing: () => string[]}}
|
|
176
|
+
*/
|
|
177
|
+
export function watchInterfaces(
|
|
178
|
+
host,
|
|
179
|
+
missing,
|
|
180
|
+
{onFound, tls = false, local = false, timeout = 2000, connect = portOpen, timers = {setTimeout, clearTimeout}},
|
|
181
|
+
) {
|
|
182
|
+
let left = INTERFACE_NAMES.filter((name) => missing.includes(name));
|
|
183
|
+
let rounds = 0;
|
|
184
|
+
let timer = null;
|
|
185
|
+
let stopped = false;
|
|
186
|
+
const round = async () => {
|
|
187
|
+
timer = null;
|
|
188
|
+
const found = await probeInterfaces(host, {tls, local, timeout, connect, names: left});
|
|
189
|
+
if (stopped) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
left = left.filter((name) => !found.includes(name));
|
|
193
|
+
for (const name of found) {
|
|
194
|
+
onFound(name);
|
|
195
|
+
}
|
|
196
|
+
schedule();
|
|
197
|
+
};
|
|
198
|
+
const schedule = () => {
|
|
199
|
+
if (stopped || left.length === 0) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
rounds += 1;
|
|
203
|
+
timer = timers.setTimeout(round, initRetryDelay(rounds));
|
|
204
|
+
if (timer && typeof timer.unref === 'function') {
|
|
205
|
+
timer.unref();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
schedule();
|
|
209
|
+
return {
|
|
210
|
+
stop() {
|
|
211
|
+
stopped = true;
|
|
212
|
+
if (timer) {
|
|
213
|
+
timers.clearTimeout(timer);
|
|
214
|
+
timer = null;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
missing: () => [...left],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
156
221
|
/**
|
|
157
222
|
* Is this address the machine we run on?
|
|
158
223
|
* @param {string} host
|
|
@@ -171,17 +236,52 @@ export function isLocalHost(host) {
|
|
|
171
236
|
* node-red-contrib-ccu does - that check looks for `"port" => 32001` in
|
|
172
237
|
* /etc/lighttpd/conf.d/proxy.conf, which on firmware 3.8x is a one-line include, so it silently
|
|
173
238
|
* stopped detecting anything while the ports themselves are all still there.
|
|
239
|
+
*
|
|
240
|
+
* On a loopback address where nothing answers yet, the processes may still be starting (a boot, or
|
|
241
|
+
* openccu-lite's early start): with `waitMs` the probe is repeated on the init schedule (1, 2, 4,
|
|
242
|
+
* 8 s, then every 15 s) until one answers or `waitMs` has passed (B-1); `onWait` is called once,
|
|
243
|
+
* before the first repetition. When the CCU's usual ports answer instead (a tunnel, a simulator,
|
|
244
|
+
* lighttpd's proxies without the processes behind them) it is not local mode and nothing waits.
|
|
174
245
|
* @param {string} host
|
|
175
|
-
* @param {{timeout?: number, connect?: Function
|
|
246
|
+
* @param {{timeout?: number, connect?: Function, waitMs?: number, onWait?: Function,
|
|
247
|
+
* sleep?: (ms: number) => Promise<void>, now?: () => number}} [options]
|
|
176
248
|
* @returns {Promise<boolean>}
|
|
177
249
|
*/
|
|
178
|
-
export async function detectLocal(
|
|
250
|
+
export async function detectLocal(
|
|
251
|
+
host,
|
|
252
|
+
{
|
|
253
|
+
timeout = 500,
|
|
254
|
+
connect = portOpen,
|
|
255
|
+
waitMs = 0,
|
|
256
|
+
onWait = () => {},
|
|
257
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
258
|
+
now = Date.now,
|
|
259
|
+
} = {},
|
|
260
|
+
) {
|
|
179
261
|
if (!isLocalHost(host)) {
|
|
180
262
|
return false;
|
|
181
263
|
}
|
|
182
264
|
const ports = INTERFACE_NAMES.map((name) => INTERFACES[name].localPort).filter(Boolean);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
265
|
+
const start = now();
|
|
266
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
267
|
+
// concurrently: this runs at every startup, and closed-but-filtered ports would otherwise
|
|
268
|
+
// stack their timeouts
|
|
269
|
+
const results = await Promise.all([...ports, REGA_LOCAL_PORT].map((port) => connect(host, port, timeout)));
|
|
270
|
+
if (results.some(Boolean)) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
const delay = initRetryDelay(attempt);
|
|
274
|
+
if (now() - start + delay > waitMs) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const usual = INTERFACE_NAMES.map((name) => INTERFACES[name].port).filter(Boolean);
|
|
278
|
+
const proxied = await Promise.all([...usual, REGA_PORT].map((port) => connect(host, port, timeout)));
|
|
279
|
+
if (proxied.some(Boolean)) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (attempt === 1) {
|
|
283
|
+
onWait();
|
|
284
|
+
}
|
|
285
|
+
await sleep(delay);
|
|
286
|
+
}
|
|
187
287
|
}
|
package/lib/rpc.js
CHANGED
|
@@ -22,9 +22,9 @@ export const METHODS = [
|
|
|
22
22
|
];
|
|
23
23
|
|
|
24
24
|
const TX_METHODS = new Set(['setValue', 'putParamset', 'activateLinkParamset']);
|
|
25
|
-
/** a failed init is retried after 2, 4, 8
|
|
26
|
-
const INIT_BACKOFF_MS = [2000, 4000, 8000
|
|
27
|
-
const INIT_RETRY_MS =
|
|
25
|
+
/** a failed init is retried after 1, 2, 4, 8 s, then every INIT_RETRY_MS (the cap; task 21) */
|
|
26
|
+
const INIT_BACKOFF_MS = [1000, 2000, 4000, 8000];
|
|
27
|
+
export const INIT_RETRY_MS = 15000;
|
|
28
28
|
const DEINIT_TIMEOUT_MS = 1500;
|
|
29
29
|
const CALL_TIMEOUT_MS = 30000;
|
|
30
30
|
|
|
@@ -50,6 +50,19 @@ export function batchHints(events) {
|
|
|
50
50
|
return hints;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Whether an error only says that nothing listens on the port yet - an interface process that is
|
|
55
|
+
* still starting (openccu-lite starts the addon before rfd and hmipserver), or one that is
|
|
56
|
+
* restarting. Such a wait is normal at boot and logged as info, not as a warning.
|
|
57
|
+
* @param {Error|string} err
|
|
58
|
+
* @returns {boolean}
|
|
59
|
+
*/
|
|
60
|
+
export function notListening(err) {
|
|
61
|
+
const code = err && err.code;
|
|
62
|
+
const msg = String((err && err.message) || err || '');
|
|
63
|
+
return code === 'ECONNREFUSED' || /ECONNREFUSED/.test(msg);
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
/**
|
|
54
67
|
* The delay before the next init attempt.
|
|
55
68
|
* @param {number} failures failed attempts in a row (1 after the first failure)
|
|
@@ -72,8 +85,12 @@ export class RpcServers {
|
|
|
72
85
|
* @param {number} o.binrpcPort
|
|
73
86
|
* @param {object} o.log
|
|
74
87
|
* @param {{xmlrpc?: object, binrpc?: object}} [o.libs] for tests
|
|
88
|
+
* @param {string} [o.ownPrefix] the start of this instance's init ids: a call for one of them that
|
|
89
|
+
* is not registered (yet) is expected - an interface process that re-announces itself to a
|
|
90
|
+
* subscription of the last run before this one subscribed again - and logged as debug
|
|
75
91
|
*/
|
|
76
|
-
constructor({listenAddress, initAddress, xmlrpcPort, binrpcPort, log, libs = {}}) {
|
|
92
|
+
constructor({listenAddress, initAddress, xmlrpcPort, binrpcPort, log, libs = {}, ownPrefix}) {
|
|
93
|
+
this.ownPrefix = ownPrefix;
|
|
77
94
|
this.listenAddress = listenAddress;
|
|
78
95
|
this.initAddress = initAddress || listenAddress;
|
|
79
96
|
this.ports = {xmlrpc: xmlrpcPort, binrpc: binrpcPort};
|
|
@@ -130,7 +147,8 @@ export class RpcServers {
|
|
|
130
147
|
const initId = Array.isArray(params) ? params[0] : undefined;
|
|
131
148
|
const connection = this.connections.get(initId);
|
|
132
149
|
if (!connection) {
|
|
133
|
-
this.
|
|
150
|
+
const own = this.ownPrefix && typeof initId === 'string' && initId.startsWith(this.ownPrefix);
|
|
151
|
+
(own ? this.log.debug : this.log.warn)('rpc < call for unknown init id', String(initId));
|
|
134
152
|
}
|
|
135
153
|
return connection;
|
|
136
154
|
}
|
|
@@ -300,6 +318,18 @@ export class RpcConnection extends EventEmitter {
|
|
|
300
318
|
this.pingTimer = null;
|
|
301
319
|
this.lastInitError = null;
|
|
302
320
|
this.initFailures = 0;
|
|
321
|
+
this.everInitialized = false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Closes the client (binrpc: its socket and reconnect timer); the next call creates a new one. */
|
|
325
|
+
dropClient() {
|
|
326
|
+
if (this.protocol !== 'binrpc' || !this.client) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (typeof this.client.close === 'function') {
|
|
330
|
+
this.client.close();
|
|
331
|
+
}
|
|
332
|
+
this.client = null;
|
|
303
333
|
}
|
|
304
334
|
|
|
305
335
|
createClient() {
|
|
@@ -384,6 +414,7 @@ export class RpcConnection extends EventEmitter {
|
|
|
384
414
|
this.log.info('rpc', this.name, '> init', url, this.initId);
|
|
385
415
|
await this.methodCall('init', [url, this.initId]);
|
|
386
416
|
this.initialized = true;
|
|
417
|
+
this.everInitialized = true;
|
|
387
418
|
if (this.initFailures > 0) {
|
|
388
419
|
this.log.info('rpc', this.name, 'init succeeded after', this.initFailures + 1, 'attempts');
|
|
389
420
|
}
|
|
@@ -397,17 +428,19 @@ export class RpcConnection extends EventEmitter {
|
|
|
397
428
|
const msg = err.message || String(err);
|
|
398
429
|
this.initFailures += 1;
|
|
399
430
|
const delay = initRetryDelay(this.initFailures);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
)
|
|
431
|
+
// a binrpc client whose socket is gone waits for its own reconnect timer (2.5 s doubling
|
|
432
|
+
// up to 30 s) and fails every call meanwhile; a fresh one connects at the next attempt
|
|
433
|
+
this.dropClient();
|
|
434
|
+
const retry = `retrying in ${delay / 1000} s (then up to every ${INIT_RETRY_MS / 1000} s)`;
|
|
435
|
+
if (msg === this.lastInitError) {
|
|
436
|
+
this.log.debug('rpc', this.name, 'init failed:', msg, `- next attempt in ${delay / 1000} s`);
|
|
437
|
+
} else if (!this.everInitialized && notListening(err)) {
|
|
438
|
+
// the start before the interface process (openccu-lite's early start): a wait, not a fault
|
|
439
|
+
this.log.info('rpc', this.name, `nothing is listening on ${this.host}:${this.port} yet - ${retry}`);
|
|
408
440
|
this.lastInitError = msg;
|
|
409
441
|
} else {
|
|
410
|
-
this.log.
|
|
442
|
+
this.log.warn('rpc', this.name, 'init failed:', msg, `- ${retry}`);
|
|
443
|
+
this.lastInitError = msg;
|
|
411
444
|
}
|
|
412
445
|
this.setConnected(false);
|
|
413
446
|
this.initTimer = this.timers.setTimeout(() => this.init(), delay);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hm2mqtt",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "Interface between Homematic CCU and MQTT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"serviceExtra": []
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"binrpc": "^
|
|
70
|
+
"binrpc": "^4.3.0",
|
|
71
71
|
"homematic-rega": "^2.0.0",
|
|
72
72
|
"homematic-xmlrpc": "^2.0.0",
|
|
73
73
|
"mqtt-interfaces-core": "^0.15.2"
|