node-red-contrib-knx-ultimate 4.1.18 → 4.1.21
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 +14 -2
- package/examples/KNX Multi Routing - KNXIP Server.json +133 -0
- package/nodes/knxUltimateAI.js +131 -85
- package/nodes/knxUltimateMultiRouting.html +75 -3
- package/nodes/knxUltimateMultiRouting.js +390 -28
- package/nodes/knxUltimateRouterFilter.html +1 -1
- package/nodes/knxUltimateRouterFilter.js +215 -9
- package/nodes/locales/de/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/de/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/de/knxUltimateRouterFilter.html +39 -1
- package/nodes/locales/en/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/en/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/en/knxUltimateRouterFilter.html +39 -1
- package/nodes/locales/es/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/es/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/es/knxUltimateRouterFilter.html +39 -1
- package/nodes/locales/fr/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/fr/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/fr/knxUltimateRouterFilter.html +39 -1
- package/nodes/locales/it/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/it/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/it/knxUltimateRouterFilter.html +39 -1
- package/nodes/locales/zh-CN/knxUltimateMultiRouting.html +16 -0
- package/nodes/locales/zh-CN/knxUltimateMultiRouting.json +15 -1
- package/nodes/locales/zh-CN/knxUltimateRouterFilter.html +39 -1
- package/package.json +2 -2
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
// KNX Router Filter - filters RAW telegram objects between KNX Multi Routing nodes
|
|
2
2
|
const normalizeText = (value) => String(value || '').trim()
|
|
3
3
|
|
|
4
|
+
const normalizeHex = (value) => {
|
|
5
|
+
if (value === undefined || value === null) return ''
|
|
6
|
+
const s = String(value).trim()
|
|
7
|
+
if (!s) return ''
|
|
8
|
+
return s.replace(/^0x/i, '').replace(/[^0-9a-fA-F]/g, '')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const parseBoolean = (value, fallback) => {
|
|
12
|
+
if (value === undefined || value === null) return fallback
|
|
13
|
+
if (typeof value === 'boolean') return value
|
|
14
|
+
if (typeof value === 'number') return value !== 0
|
|
15
|
+
const s = String(value).trim().toLowerCase()
|
|
16
|
+
if (s === 'true' || s === '1' || s === 'yes' || s === 'on') return true
|
|
17
|
+
if (s === 'false' || s === '0' || s === 'no' || s === 'off') return false
|
|
18
|
+
return fallback
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const normalizeMultiline = (value) => {
|
|
22
|
+
if (value === undefined || value === null) return ''
|
|
23
|
+
if (Array.isArray(value)) return value.map(v => String(v)).join('\n')
|
|
24
|
+
return String(value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let _knxultimateCache = null
|
|
28
|
+
const getKnxultimate = () => {
|
|
29
|
+
if (_knxultimateCache) return _knxultimateCache
|
|
30
|
+
_knxultimateCache = require('knxultimate')
|
|
31
|
+
return _knxultimateCache
|
|
32
|
+
}
|
|
33
|
+
|
|
4
34
|
const splitPatterns = (raw) => {
|
|
5
35
|
const s = normalizeText(raw)
|
|
6
36
|
if (!s) return []
|
|
@@ -162,6 +192,82 @@ const getPayloadObject = (msg) => {
|
|
|
162
192
|
return msg.payload
|
|
163
193
|
}
|
|
164
194
|
|
|
195
|
+
const getKnxObject = (msg) => {
|
|
196
|
+
const payload = getPayloadObject(msg)
|
|
197
|
+
if (payload && payload.knx && typeof payload.knx === 'object' && !Buffer.isBuffer(payload.knx)) return payload.knx
|
|
198
|
+
if (msg && msg.knx && typeof msg.knx === 'object' && !Buffer.isBuffer(msg.knx)) return msg.knx
|
|
199
|
+
return null
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const getCemiHexFromMsg = (msg) => {
|
|
203
|
+
const knx = getKnxObject(msg)
|
|
204
|
+
if (!knx) return ''
|
|
205
|
+
const cemi = knx.cemi
|
|
206
|
+
if (!cemi) return ''
|
|
207
|
+
if (typeof cemi === 'string') return cemi
|
|
208
|
+
if (typeof cemi === 'object' && !Buffer.isBuffer(cemi) && cemi.hex) return cemi.hex
|
|
209
|
+
return ''
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const setCemiHexOnMsg = (msg, cemiHex) => {
|
|
213
|
+
const knx = getKnxObject(msg)
|
|
214
|
+
if (!knx) return false
|
|
215
|
+
if (knx.cemi && typeof knx.cemi === 'object' && !Buffer.isBuffer(knx.cemi) && 'hex' in knx.cemi) {
|
|
216
|
+
knx.cemi.hex = cemiHex
|
|
217
|
+
return true
|
|
218
|
+
}
|
|
219
|
+
knx.cemi = { hex: cemiHex }
|
|
220
|
+
return true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const isIndividualAddressString = (value) => /^\d{1,2}\.\d{1,2}\.\d{1,3}$/.test(String(value || '').trim())
|
|
224
|
+
const isGroupAddressString = (value) => /^\d{1,2}\/\d{1,2}\/\d{1,3}$/.test(String(value || '').trim())
|
|
225
|
+
|
|
226
|
+
const syncCemiWithKnxFields = (msg) => {
|
|
227
|
+
const knx = getKnxObject(msg)
|
|
228
|
+
if (!knx) return false
|
|
229
|
+
const cemiHex = getCemiHexFromMsg(msg)
|
|
230
|
+
const clean = normalizeHex(cemiHex)
|
|
231
|
+
if (!clean || clean.length % 2 !== 0) return false
|
|
232
|
+
|
|
233
|
+
let cemi
|
|
234
|
+
try {
|
|
235
|
+
const { KNXTunnelingRequest } = getKnxultimate()
|
|
236
|
+
cemi = KNXTunnelingRequest.parseCEMIMessage(Buffer.from(clean, 'hex'), 0)
|
|
237
|
+
} catch (e) {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
if (!cemi || !cemi.control) return false
|
|
241
|
+
|
|
242
|
+
let updated = false
|
|
243
|
+
try {
|
|
244
|
+
if (isIndividualAddressString(knx.source)) {
|
|
245
|
+
const { KNXAddress } = getKnxultimate()
|
|
246
|
+
cemi.srcAddress = KNXAddress.createFromString(String(knx.source).trim(), KNXAddress.TYPE_INDIVIDUAL)
|
|
247
|
+
updated = true
|
|
248
|
+
}
|
|
249
|
+
} catch (e) { /* ignore */ }
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
if (isGroupAddressString(knx.destination)) {
|
|
253
|
+
const { KNXAddress } = getKnxultimate()
|
|
254
|
+
cemi.dstAddress = KNXAddress.createFromString(String(knx.destination).trim(), KNXAddress.TYPE_GROUP)
|
|
255
|
+
try { cemi.control.addressType = 1 } catch (e2) { /* ignore */ }
|
|
256
|
+
updated = true
|
|
257
|
+
}
|
|
258
|
+
} catch (e) { /* ignore */ }
|
|
259
|
+
|
|
260
|
+
if (!updated) return false
|
|
261
|
+
try {
|
|
262
|
+
const outHex = cemi.toBuffer().toString('hex')
|
|
263
|
+
setCemiHexOnMsg(msg, outHex)
|
|
264
|
+
try { knx.cemi.hopCount = knx.cemi.hopCount } catch (e) { /* ignore */ }
|
|
265
|
+
return true
|
|
266
|
+
} catch (e) {
|
|
267
|
+
return false
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
165
271
|
const attachFilterMeta = (msg, meta) => {
|
|
166
272
|
try {
|
|
167
273
|
const payload = getPayloadObject(msg)
|
|
@@ -185,24 +291,24 @@ module.exports = function (RED) {
|
|
|
185
291
|
const node = this
|
|
186
292
|
|
|
187
293
|
node.name = config.name || 'KNX Router Filter'
|
|
188
|
-
node.allowWrite =
|
|
189
|
-
node.allowResponse =
|
|
190
|
-
node.allowRead =
|
|
294
|
+
node.allowWrite = parseBoolean(config.allowWrite, true)
|
|
295
|
+
node.allowResponse = parseBoolean(config.allowResponse, true)
|
|
296
|
+
node.allowRead = parseBoolean(config.allowRead, true)
|
|
191
297
|
|
|
192
298
|
node.gaMode = config.gaMode || 'off' // off|allow|block
|
|
193
299
|
node.gaPatterns = config.gaPatterns || ''
|
|
194
300
|
node.srcMode = config.srcMode || 'off' // off|allow|block
|
|
195
301
|
node.srcPatterns = config.srcPatterns || ''
|
|
196
302
|
|
|
197
|
-
node.rewriteGA =
|
|
303
|
+
node.rewriteGA = parseBoolean(config.rewriteGA, false)
|
|
198
304
|
node.gaRewriteRules = config.gaRewriteRules || ''
|
|
199
|
-
node.rewriteSource =
|
|
305
|
+
node.rewriteSource = parseBoolean(config.rewriteSource, false)
|
|
200
306
|
node.srcRewriteRules = config.srcRewriteRules || ''
|
|
201
307
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
308
|
+
let gaRegexes = compileAddressPatterns({ raw: node.gaPatterns, kind: 'ga' })
|
|
309
|
+
let srcRegexes = compileAddressPatterns({ raw: node.srcPatterns, kind: 'src' })
|
|
310
|
+
let gaRewrite = compileRewriteRules({ raw: node.gaRewriteRules, kind: 'ga' })
|
|
311
|
+
let srcRewrite = compileRewriteRules({ raw: node.srcRewriteRules, kind: 'src' })
|
|
206
312
|
|
|
207
313
|
let passed = 0
|
|
208
314
|
let dropped = 0
|
|
@@ -251,6 +357,91 @@ module.exports = function (RED) {
|
|
|
251
357
|
applyStatus(msg, { fill: 'grey', shape: 'dot', text: `pass ${passed} / drop ${dropped}` })
|
|
252
358
|
}
|
|
253
359
|
|
|
360
|
+
let configStatusTimer = null
|
|
361
|
+
const setConfigStatus = (msg, text) => {
|
|
362
|
+
try {
|
|
363
|
+
if (configStatusTimer) clearTimeout(configStatusTimer)
|
|
364
|
+
applyStatus(msg, { fill: 'blue', shape: 'ring', text: text || 'Config changed' })
|
|
365
|
+
configStatusTimer = setTimeout(() => setCountersStatus(null), 2000)
|
|
366
|
+
} catch (e) { /* ignore */ }
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const rebuildCompiledRules = () => {
|
|
370
|
+
gaRegexes = compileAddressPatterns({ raw: node.gaPatterns, kind: 'ga' })
|
|
371
|
+
srcRegexes = compileAddressPatterns({ raw: node.srcPatterns, kind: 'src' })
|
|
372
|
+
gaRewrite = compileRewriteRules({ raw: node.gaRewriteRules, kind: 'ga' })
|
|
373
|
+
srcRewrite = compileRewriteRules({ raw: node.srcRewriteRules, kind: 'src' })
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const applySetConfig = (msg) => {
|
|
377
|
+
const sc = msg && msg.setConfig && typeof msg.setConfig === 'object' && !Buffer.isBuffer(msg.setConfig) ? msg.setConfig : null
|
|
378
|
+
if (!sc) return false
|
|
379
|
+
|
|
380
|
+
const changedKeys = []
|
|
381
|
+
const markChanged = (key) => { if (!changedKeys.includes(key)) changedKeys.push(key) }
|
|
382
|
+
|
|
383
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'name')) {
|
|
384
|
+
node.name = normalizeText(sc.name) || node.name
|
|
385
|
+
markChanged('name')
|
|
386
|
+
}
|
|
387
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'allowWrite')) {
|
|
388
|
+
node.allowWrite = parseBoolean(sc.allowWrite, node.allowWrite)
|
|
389
|
+
markChanged('allowWrite')
|
|
390
|
+
}
|
|
391
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'allowResponse')) {
|
|
392
|
+
node.allowResponse = parseBoolean(sc.allowResponse, node.allowResponse)
|
|
393
|
+
markChanged('allowResponse')
|
|
394
|
+
}
|
|
395
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'allowRead')) {
|
|
396
|
+
node.allowRead = parseBoolean(sc.allowRead, node.allowRead)
|
|
397
|
+
markChanged('allowRead')
|
|
398
|
+
}
|
|
399
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'gaMode')) {
|
|
400
|
+
const m = normalizeText(sc.gaMode).toLowerCase()
|
|
401
|
+
if (m === 'off' || m === 'allow' || m === 'block') {
|
|
402
|
+
node.gaMode = m
|
|
403
|
+
markChanged('gaMode')
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'gaPatterns')) {
|
|
407
|
+
node.gaPatterns = normalizeMultiline(sc.gaPatterns)
|
|
408
|
+
markChanged('gaPatterns')
|
|
409
|
+
}
|
|
410
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'srcMode')) {
|
|
411
|
+
const m = normalizeText(sc.srcMode).toLowerCase()
|
|
412
|
+
if (m === 'off' || m === 'allow' || m === 'block') {
|
|
413
|
+
node.srcMode = m
|
|
414
|
+
markChanged('srcMode')
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'srcPatterns')) {
|
|
418
|
+
node.srcPatterns = normalizeMultiline(sc.srcPatterns)
|
|
419
|
+
markChanged('srcPatterns')
|
|
420
|
+
}
|
|
421
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'rewriteGA')) {
|
|
422
|
+
node.rewriteGA = parseBoolean(sc.rewriteGA, node.rewriteGA)
|
|
423
|
+
markChanged('rewriteGA')
|
|
424
|
+
}
|
|
425
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'gaRewriteRules')) {
|
|
426
|
+
node.gaRewriteRules = normalizeMultiline(sc.gaRewriteRules)
|
|
427
|
+
markChanged('gaRewriteRules')
|
|
428
|
+
}
|
|
429
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'rewriteSource')) {
|
|
430
|
+
node.rewriteSource = parseBoolean(sc.rewriteSource, node.rewriteSource)
|
|
431
|
+
markChanged('rewriteSource')
|
|
432
|
+
}
|
|
433
|
+
if (Object.prototype.hasOwnProperty.call(sc, 'srcRewriteRules')) {
|
|
434
|
+
node.srcRewriteRules = normalizeMultiline(sc.srcRewriteRules)
|
|
435
|
+
markChanged('srcRewriteRules')
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (changedKeys.length > 0) {
|
|
439
|
+
rebuildCompiledRules()
|
|
440
|
+
setConfigStatus(msg, `Config changed: ${changedKeys.join(', ')}`)
|
|
441
|
+
}
|
|
442
|
+
return true
|
|
443
|
+
}
|
|
444
|
+
|
|
254
445
|
const extractFields = (msg) => {
|
|
255
446
|
const payload = msg && msg.payload !== undefined ? msg.payload : null
|
|
256
447
|
const knx = (payload && payload.knx) ? payload.knx : (msg && msg.knx ? msg.knx : null)
|
|
@@ -279,6 +470,11 @@ module.exports = function (RED) {
|
|
|
279
470
|
|
|
280
471
|
node.on('input', function (msg) {
|
|
281
472
|
try {
|
|
473
|
+
if (msg && Object.prototype.hasOwnProperty.call(msg, 'setConfig')) {
|
|
474
|
+
applySetConfig(msg)
|
|
475
|
+
return
|
|
476
|
+
}
|
|
477
|
+
|
|
282
478
|
const fields = extractFields(msg)
|
|
283
479
|
|
|
284
480
|
// If message doesn't look like a KNX raw telegram, pass it through unchanged.
|
|
@@ -341,8 +537,11 @@ module.exports = function (RED) {
|
|
|
341
537
|
}
|
|
342
538
|
|
|
343
539
|
if (anyRewrite) {
|
|
540
|
+
let cemiSynced = false
|
|
541
|
+
try { cemiSynced = syncCemiWithKnxFields(msg) } catch (e) { cemiSynced = false }
|
|
344
542
|
meta = Object.assign({}, meta, {
|
|
345
543
|
rewritten: true,
|
|
544
|
+
cemiSynced,
|
|
346
545
|
original: Object.assign({}, meta.original || {}, {
|
|
347
546
|
destination: beforeGA,
|
|
348
547
|
source: beforeSrc
|
|
@@ -360,6 +559,13 @@ module.exports = function (RED) {
|
|
|
360
559
|
}
|
|
361
560
|
})
|
|
362
561
|
|
|
562
|
+
node.on('close', function () {
|
|
563
|
+
try {
|
|
564
|
+
if (configStatusTimer) clearTimeout(configStatusTimer)
|
|
565
|
+
configStatusTimer = null
|
|
566
|
+
} catch (e) { /* ignore */ }
|
|
567
|
+
})
|
|
568
|
+
|
|
363
569
|
setCountersStatus(null)
|
|
364
570
|
}
|
|
365
571
|
|
|
@@ -4,6 +4,12 @@ Dieser Node dient dazu, **mehrere KNX-Ultimate-Gateways** (mehrere `knxUltimate-
|
|
|
4
4
|
Er gibt für jedes Telegramm vom KNX-Bus des ausgewählten Gateways ein Objekt mit **RAW-Telegramm-Informationen** (APDU + cEMI-Hex + Adressen) aus.
|
|
5
5
|
Außerdem kann er die gleichen RAW-Objekte am Eingang annehmen und an das ausgewählte Gateway weiterleiten.
|
|
6
6
|
|
|
7
|
+
## Server KNX/IP Modus
|
|
8
|
+
Setze **Modus** auf **Server KNX/IP**, um einen eingebetteten KNXnet/IP-Tunneling-Server (UDP) zu starten. Eingehende Client-Telegramme werden im gleichen RAW-Format ausgegeben.
|
|
9
|
+
Der Node akzeptiert außerdem RAW-Telegramm-Objekte am Eingang und injiziert sie an die verbundenen Tunneling-Clients.
|
|
10
|
+
|
|
11
|
+
**Wichtig (Advertise host):** KNXnet/IP-Clients senden die Daten an die IP, die der Server in der CONNECT_RESPONSE ankündigt. Wenn der Client als *connected* angezeigt wird, der Server aber keine Telegramme empfängt, setze **Advertise host** auf die LAN-IP des Servers, die vom Client erreichbar ist (insbesondere bei Docker/VM oder Multi-Homing).
|
|
12
|
+
|
|
7
13
|
## Output-Nachrichtenformat
|
|
8
14
|
`msg.payload` enthält:
|
|
9
15
|
- `knx.event`: `GroupValue_Write` / `GroupValue_Response` / `GroupValue_Read`
|
|
@@ -15,6 +21,16 @@ Außerdem kann er die gleichen RAW-Objekte am Eingang annehmen und an das ausgew
|
|
|
15
21
|
- `knx.echoed`: `true`, wenn vom Gateway „echoed“
|
|
16
22
|
- `knxMultiRouting.gateway`: Gateway-Metadaten (`id`, `name`, `physAddr`)
|
|
17
23
|
|
|
24
|
+
## Routing counter (hop count)
|
|
25
|
+
MultiRouting kann den KNX Routing Counter (Hop Count) aus `knx.cemi.hex` nutzen, um Telegramm-Loops zu verhindern.
|
|
26
|
+
- **Respect routing counter (drop if 0)**: Telegramme mit Routing Counter `0` werden nicht weitergeleitet.
|
|
27
|
+
- **Decrement routing counter when routing**: Der Node dekrementiert den Routing Counter beim Weiterleiten. Erreicht er `0`, wird das Telegramm verworfen.
|
|
28
|
+
|
|
29
|
+
Der aktuelle Wert steht in `knx.routingCounter` (und in `knx.cemi.hopCount`, wenn `knx.cemi` ein Objekt ist).
|
|
30
|
+
|
|
31
|
+
## Telegramme umschreiben
|
|
32
|
+
Wenn du `knx.source` / `knx.destination` im Flow umschreibst, musst du auch `knx.cemi.hex` konsistent halten. Empfehlung: setze **KNX Router Filter** zwischen MultiRouting-Nodes; er hält `knx.cemi.hex` beim Umschreiben automatisch konsistent.
|
|
33
|
+
|
|
18
34
|
## Hinweise
|
|
19
35
|
- Beim Weiterleiten an ein anderes Gateway **ändert sich die Source-PA** (es wird die phys. Adresse des sendenden Gateways). Nutze `knx.source` und/oder `knxMultiRouting.gateway`, um Loops zu filtern.
|
|
20
36
|
- Die Option **„Drop messages already tagged for this gateway“** hilft, einfache Loops zu verhindern, wenn mehrere Router miteinander verbunden sind.
|
|
@@ -2,10 +2,24 @@
|
|
|
2
2
|
"knxUltimateMultiRouting": {
|
|
3
3
|
"title": "KNX Multi Routing",
|
|
4
4
|
"properties": {
|
|
5
|
+
"mode": "Mode",
|
|
6
|
+
"modeGateway": "Gateway / Routing",
|
|
7
|
+
"modeServer": "Server KNX/IP",
|
|
5
8
|
"server": "Gateway",
|
|
6
9
|
"name": "Name",
|
|
7
10
|
"outputtopic": "Output topic",
|
|
8
|
-
"dropIfSameGateway": "Drop messages already tagged for this gateway"
|
|
11
|
+
"dropIfSameGateway": "Drop messages already tagged for this gateway",
|
|
12
|
+
"respectRoutingCounter": "Respect routing counter (drop if 0)",
|
|
13
|
+
"decrementRoutingCounter": "Decrement routing counter when routing",
|
|
14
|
+
"tunnelTitle": "KNX/IP Server",
|
|
15
|
+
"tunnelListenHost": "Listen host",
|
|
16
|
+
"tunnelListenPort": "Listen port",
|
|
17
|
+
"tunnelAdvertiseHost": "Advertise host",
|
|
18
|
+
"tunnelAdvertiseHostPlaceholder": "(optional) Host/IP to advertise to clients",
|
|
19
|
+
"tunnelAssignedIndividualAddress": "Assigned individual address",
|
|
20
|
+
"tunnelGatewayId": "Gateway id (tag)",
|
|
21
|
+
"tunnelGatewayIdPlaceholder": "(auto) Used as knxMultiRouting.gateway.id",
|
|
22
|
+
"tunnelMaxSessions": "Max sessions"
|
|
9
23
|
},
|
|
10
24
|
"outputs": {
|
|
11
25
|
"raw": "RAW telegrams"
|
|
@@ -24,6 +24,8 @@ Optionales Umschreiben von:
|
|
|
24
24
|
- Ziel-Gruppenadresse (`knx.destination`)
|
|
25
25
|
- Source-PA (`knx.source`)
|
|
26
26
|
|
|
27
|
+
Beim Umschreiben aktualisiert der Node auch `knx.cemi.hex`, damit es zu den umgeschriebenen Werten von `knx.source`/`knx.destination` passt (wenn `knx.cemi.hex` vorhanden ist).
|
|
28
|
+
|
|
27
29
|
Regeln werden von oben nach unten ausgewertet (first match wins).
|
|
28
30
|
|
|
29
31
|
Beispiele:
|
|
@@ -33,5 +35,41 @@ Beispiele:
|
|
|
33
35
|
## Metadaten
|
|
34
36
|
Der Node fügt `msg.payload.knxRouterFilter` hinzu:
|
|
35
37
|
- verworfen: `{ dropped: true, reason: 'event'|'ga'|'source', ... }`
|
|
36
|
-
- durchgelassen: `{ dropped: false, rewritten: <bool>, rewrite: { ... }, original: { ... } }`
|
|
38
|
+
- durchgelassen: `{ dropped: false, rewritten: <bool>, cemiSynced: <bool>, rewrite: { ... }, original: { ... } }`
|
|
39
|
+
|
|
40
|
+
## msg.setConfig
|
|
41
|
+
Du kannst die Node-Konfiguration zur Laufzeit ändern, indem du ein `msg.setConfig` Objekt an den Eingang sendest.
|
|
42
|
+
Unterstützte Keys: `allowWrite`, `allowResponse`, `allowRead`, `gaMode`, `gaPatterns`, `srcMode`, `srcPatterns`, `rewriteGA`, `gaRewriteRules`, `rewriteSource`, `srcRewriteRules`.
|
|
43
|
+
Die Konfiguration bleibt bis zum nächsten `msg.setConfig` oder bis Redeploy/Neustart erhalten. Konfigurationsnachrichten werden nicht weitergeleitet.
|
|
44
|
+
|
|
45
|
+
Bedeutung der Properties:
|
|
46
|
+
- `allowWrite`: erlaubt `GroupValue_Write` Telegramme.
|
|
47
|
+
- `allowResponse`: erlaubt `GroupValue_Response` Telegramme.
|
|
48
|
+
- `allowRead`: erlaubt `GroupValue_Read` Telegramme.
|
|
49
|
+
- `gaMode`: Filtermodus für die Ziel-GA (`off` = kein Filter, `allow` = nur Matches durchlassen, `block` = Matches verwerfen).
|
|
50
|
+
- `gaPatterns`: Ziel-GA Patterns für `gaMode` (eine pro Zeile, unterstützt `*` und `re:<regex>`).
|
|
51
|
+
- `srcMode`: Filtermodus für die Source-PA (`off`/`allow`/`block`).
|
|
52
|
+
- `srcPatterns`: Source-Patterns für `srcMode` (eine pro Zeile, unterstützt `*` und `re:<regex>`).
|
|
53
|
+
- `rewriteGA`: aktiviert Umschreiben von `knx.destination` für durchgelassene Telegramme.
|
|
54
|
+
- `gaRewriteRules`: Umschreibregeln für Ziel-GA (`von => nach`, first match wins; unterstützt `*` und `re:<regex>`).
|
|
55
|
+
- `rewriteSource`: aktiviert Umschreiben von `knx.source` für durchgelassene Telegramme.
|
|
56
|
+
- `srcRewriteRules`: Umschreibregeln für Source-PA (`von => nach`, first match wins; unterstützt `*` und `re:<regex>`).
|
|
57
|
+
|
|
58
|
+
Beispiel:
|
|
59
|
+
```js
|
|
60
|
+
msg.setConfig = {
|
|
61
|
+
allowWrite: true,
|
|
62
|
+
allowResponse: true,
|
|
63
|
+
allowRead: true,
|
|
64
|
+
gaMode: "allow",
|
|
65
|
+
gaPatterns: "1/1/*\n1/2/3",
|
|
66
|
+
srcMode: "off",
|
|
67
|
+
srcPatterns: "",
|
|
68
|
+
rewriteGA: true,
|
|
69
|
+
gaRewriteRules: "5/5/1 => 1/1/1",
|
|
70
|
+
rewriteSource: true,
|
|
71
|
+
srcRewriteRules: "15.*.* => 1.1.254"
|
|
72
|
+
};
|
|
73
|
+
return msg;
|
|
74
|
+
```
|
|
37
75
|
</script>
|
|
@@ -4,6 +4,12 @@ This node is used to **bridge multiple KNX Ultimate gateways** (multiple `knxUlt
|
|
|
4
4
|
It outputs **RAW telegram information** (APDU + cEMI hex + addresses) for every telegram received from the KNX bus of the selected gateway.
|
|
5
5
|
It can also accept those RAW telegram objects on its input and forward them to the selected gateway.
|
|
6
6
|
|
|
7
|
+
## Server KNX/IP mode
|
|
8
|
+
Set **Mode** to **Server KNX/IP** to start an embedded KNXnet/IP tunneling server (UDP). Incoming client telegrams are emitted as the same RAW format used by MultiRouting.
|
|
9
|
+
The node also accepts RAW telegram objects on its input and injects them to the connected tunneling client(s).
|
|
10
|
+
|
|
11
|
+
**Important (Advertise host):** KNXnet/IP clients will send data to the IP advertised by the server in the CONNECT_RESPONSE. If the client shows *connected* but the server receives no telegrams, set **Advertise host** to the server LAN IP reachable by the client (especially when running Node-RED in Docker/VM or on a multi-homed host).
|
|
12
|
+
|
|
7
13
|
## Output message format
|
|
8
14
|
`msg.payload` contains:
|
|
9
15
|
- `knx.event`: `GroupValue_Write` / `GroupValue_Response` / `GroupValue_Read`
|
|
@@ -15,6 +21,16 @@ It can also accept those RAW telegram objects on its input and forward them to t
|
|
|
15
21
|
- `knx.echoed`: `true` if echoed by the gateway
|
|
16
22
|
- `knxMultiRouting.gateway`: gateway meta (`id`, `name`, `physAddr`)
|
|
17
23
|
|
|
24
|
+
## Routing counter (hop count)
|
|
25
|
+
MultiRouting can use the KNX routing counter (hop count) found in `knx.cemi.hex` to prevent telegram loops.
|
|
26
|
+
- **Respect routing counter (drop if 0)**: telegrams with routing counter `0` are not forwarded.
|
|
27
|
+
- **Decrement routing counter when routing**: the node decrements the routing counter while forwarding. If it reaches `0`, the telegram is dropped.
|
|
28
|
+
|
|
29
|
+
The current value is exposed as `knx.routingCounter` (and as `knx.cemi.hopCount` when `knx.cemi` is an object).
|
|
30
|
+
|
|
31
|
+
## Rewriting telegrams
|
|
32
|
+
If you rewrite `knx.source` / `knx.destination` in your flow, you must also keep `knx.cemi.hex` coherent. Recommended: place **KNX Router Filter** between MultiRouting nodes: it will keep `knx.cemi.hex` in sync when rewriting.
|
|
33
|
+
|
|
18
34
|
## Notes
|
|
19
35
|
- When forwarded to another gateway, the **source physical address will change** (it becomes the sender gateway address). Use `knx.source` and/or `knxMultiRouting.gateway` to filter loops.
|
|
20
36
|
- The option **Drop messages already tagged for this gateway** helps prevent simple loops when you connect multiple routers together.
|
|
@@ -2,10 +2,24 @@
|
|
|
2
2
|
"knxUltimateMultiRouting": {
|
|
3
3
|
"title": "KNX Multi Routing",
|
|
4
4
|
"properties": {
|
|
5
|
+
"mode": "Mode",
|
|
6
|
+
"modeGateway": "Gateway / Routing",
|
|
7
|
+
"modeServer": "Server KNX/IP",
|
|
5
8
|
"server": "Gateway",
|
|
6
9
|
"name": "Name",
|
|
7
10
|
"outputtopic": "Output topic",
|
|
8
|
-
"dropIfSameGateway": "Drop messages already tagged for this gateway"
|
|
11
|
+
"dropIfSameGateway": "Drop messages already tagged for this gateway",
|
|
12
|
+
"respectRoutingCounter": "Respect routing counter (drop if 0)",
|
|
13
|
+
"decrementRoutingCounter": "Decrement routing counter when routing",
|
|
14
|
+
"tunnelTitle": "KNX/IP Server",
|
|
15
|
+
"tunnelListenHost": "Listen host",
|
|
16
|
+
"tunnelListenPort": "Listen port",
|
|
17
|
+
"tunnelAdvertiseHost": "Advertise host",
|
|
18
|
+
"tunnelAdvertiseHostPlaceholder": "(optional) Host/IP to advertise to clients",
|
|
19
|
+
"tunnelAssignedIndividualAddress": "Assigned individual address",
|
|
20
|
+
"tunnelGatewayId": "Gateway id (tag)",
|
|
21
|
+
"tunnelGatewayIdPlaceholder": "(auto) Used as knxMultiRouting.gateway.id",
|
|
22
|
+
"tunnelMaxSessions": "Max sessions"
|
|
9
23
|
},
|
|
10
24
|
"outputs": {
|
|
11
25
|
"raw": "RAW telegrams"
|
|
@@ -24,6 +24,8 @@ You can optionally rewrite:
|
|
|
24
24
|
- destination Group Address (`knx.destination`)
|
|
25
25
|
- source physical address (`knx.source`)
|
|
26
26
|
|
|
27
|
+
When rewriting, the node also updates `knx.cemi.hex` to match the rewritten `knx.source`/`knx.destination` (when `knx.cemi.hex` is present).
|
|
28
|
+
|
|
27
29
|
Rewrite rules are evaluated top-to-bottom, first match wins.
|
|
28
30
|
|
|
29
31
|
Syntax examples:
|
|
@@ -33,5 +35,41 @@ Syntax examples:
|
|
|
33
35
|
## Metadata
|
|
34
36
|
The node adds `msg.payload.knxRouterFilter`:
|
|
35
37
|
- dropped messages: `{ dropped: true, reason: 'event'|'ga'|'source', ... }`
|
|
36
|
-
- passed messages: `{ dropped: false, rewritten: <bool>, rewrite: { ... }, original: { ... } }`
|
|
38
|
+
- passed messages: `{ dropped: false, rewritten: <bool>, cemiSynced: <bool>, rewrite: { ... }, original: { ... } }`
|
|
39
|
+
|
|
40
|
+
## msg.setConfig
|
|
41
|
+
You can change the node configuration at runtime by sending a `msg.setConfig` object to the input.
|
|
42
|
+
Supported keys: `allowWrite`, `allowResponse`, `allowRead`, `gaMode`, `gaPatterns`, `srcMode`, `srcPatterns`, `rewriteGA`, `gaRewriteRules`, `rewriteSource`, `srcRewriteRules`.
|
|
43
|
+
The new configuration is retained until next `msg.setConfig` or until redeploy/restart. Configuration messages are not forwarded.
|
|
44
|
+
|
|
45
|
+
Meaning of the properties:
|
|
46
|
+
- `allowWrite`: allow `GroupValue_Write` telegrams.
|
|
47
|
+
- `allowResponse`: allow `GroupValue_Response` telegrams.
|
|
48
|
+
- `allowRead`: allow `GroupValue_Read` telegrams.
|
|
49
|
+
- `gaMode`: destination GA filter mode (`off` = no filter, `allow` = allow only matching, `block` = drop matching).
|
|
50
|
+
- `gaPatterns`: destination GA patterns used by `gaMode` (one per line, supports `*` and `re:<regex>`).
|
|
51
|
+
- `srcMode`: source (physical address) filter mode (`off`/`allow`/`block`).
|
|
52
|
+
- `srcPatterns`: source patterns used by `srcMode` (one per line, supports `*` and `re:<regex>`).
|
|
53
|
+
- `rewriteGA`: enable rewrite of `knx.destination` on passed telegrams.
|
|
54
|
+
- `gaRewriteRules`: rewrite rules for destination GA (`from => to`, first match wins; supports `*` and `re:<regex>`).
|
|
55
|
+
- `rewriteSource`: enable rewrite of `knx.source` on passed telegrams.
|
|
56
|
+
- `srcRewriteRules`: rewrite rules for source PA (`from => to`, first match wins; supports `*` and `re:<regex>`).
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
```js
|
|
60
|
+
msg.setConfig = {
|
|
61
|
+
allowWrite: true,
|
|
62
|
+
allowResponse: true,
|
|
63
|
+
allowRead: true,
|
|
64
|
+
gaMode: "allow",
|
|
65
|
+
gaPatterns: "1/1/*\n1/2/3",
|
|
66
|
+
srcMode: "off",
|
|
67
|
+
srcPatterns: "",
|
|
68
|
+
rewriteGA: true,
|
|
69
|
+
gaRewriteRules: "5/5/1 => 1/1/1",
|
|
70
|
+
rewriteSource: true,
|
|
71
|
+
srcRewriteRules: "15.*.* => 1.1.254"
|
|
72
|
+
};
|
|
73
|
+
return msg;
|
|
74
|
+
```
|
|
37
75
|
</script>
|
|
@@ -4,6 +4,12 @@ Este nodo se utiliza para **interconectar varios gateways KNX Ultimate** (varios
|
|
|
4
4
|
En la salida emite un objeto con información **RAW del telegrama** (APDU + cEMI en hex + direcciones) para cada telegrama recibido del bus KNX del gateway seleccionado.
|
|
5
5
|
En la entrada puede recibir esos mismos objetos RAW y reenviarlos al bus KNX del gateway seleccionado.
|
|
6
6
|
|
|
7
|
+
## Modo servidor KNX/IP
|
|
8
|
+
Establece **Modo** en **Server KNX/IP** para iniciar un servidor KNXnet/IP tunneling (UDP) integrado. Los telegramas recibidos de los clientes se emiten en el mismo formato RAW.
|
|
9
|
+
El nodo también acepta objetos RAW en la entrada y los inyecta hacia los clientes tunneling conectados.
|
|
10
|
+
|
|
11
|
+
**Importante (Advertise host):** los clientes KNXnet/IP enviarán los datos a la IP anunciada por el servidor en la CONNECT_RESPONSE. Si el cliente muestra *conectado* pero el servidor no recibe telegramas, configura **Advertise host** con la IP LAN del servidor alcanzable por el cliente (especialmente si Node-RED se ejecuta en Docker/VM o en un host con múltiples interfaces).
|
|
12
|
+
|
|
7
13
|
## Formato del mensaje de salida
|
|
8
14
|
`msg.payload` contiene:
|
|
9
15
|
- `knx.event`: `GroupValue_Write` / `GroupValue_Response` / `GroupValue_Read`
|
|
@@ -15,6 +21,16 @@ En la entrada puede recibir esos mismos objetos RAW y reenviarlos al bus KNX del
|
|
|
15
21
|
- `knx.echoed`: `true` si el gateway lo ha “echoed”
|
|
16
22
|
- `knxMultiRouting.gateway`: metadatos del gateway (`id`, `name`, `physAddr`)
|
|
17
23
|
|
|
24
|
+
## Routing counter (hop count)
|
|
25
|
+
MultiRouting puede usar el routing counter (hop count) presente en `knx.cemi.hex` para evitar bucles de telegramas.
|
|
26
|
+
- **Respect routing counter (drop if 0)**: los telegramas con routing counter `0` no se reenvían.
|
|
27
|
+
- **Decrement routing counter when routing**: el nodo decrementa el routing counter durante el reenvío. Si llega a `0`, el telegrama se descarta.
|
|
28
|
+
|
|
29
|
+
El valor actual se expone como `knx.routingCounter` (y como `knx.cemi.hopCount` cuando `knx.cemi` es un objeto).
|
|
30
|
+
|
|
31
|
+
## Reescritura de telegramas
|
|
32
|
+
Si en tu flow reescribes `knx.source` / `knx.destination`, también debes mantener `knx.cemi.hex` coherente. Recomendado: coloca **KNX Router Filter** entre nodos MultiRouting: sincronizará automáticamente `knx.cemi.hex` durante la reescritura.
|
|
33
|
+
|
|
18
34
|
## Notas
|
|
19
35
|
- Al reenviar a otro gateway, la **dirección física de origen cambia** (pasa a ser la del gateway que envía). Usa `knx.source` y/o `knxMultiRouting.gateway` para filtrar bucles.
|
|
20
36
|
- La opción **“Drop messages already tagged for this gateway”** ayuda a prevenir bucles simples cuando conectas varios routers entre sí.
|
|
@@ -2,10 +2,24 @@
|
|
|
2
2
|
"knxUltimateMultiRouting": {
|
|
3
3
|
"title": "KNX Multi Routing",
|
|
4
4
|
"properties": {
|
|
5
|
+
"mode": "Mode",
|
|
6
|
+
"modeGateway": "Gateway / Routing",
|
|
7
|
+
"modeServer": "Server KNX/IP",
|
|
5
8
|
"server": "Gateway",
|
|
6
9
|
"name": "Name",
|
|
7
10
|
"outputtopic": "Output topic",
|
|
8
|
-
"dropIfSameGateway": "Drop messages already tagged for this gateway"
|
|
11
|
+
"dropIfSameGateway": "Drop messages already tagged for this gateway",
|
|
12
|
+
"respectRoutingCounter": "Respect routing counter (drop if 0)",
|
|
13
|
+
"decrementRoutingCounter": "Decrement routing counter when routing",
|
|
14
|
+
"tunnelTitle": "KNX/IP Server",
|
|
15
|
+
"tunnelListenHost": "Listen host",
|
|
16
|
+
"tunnelListenPort": "Listen port",
|
|
17
|
+
"tunnelAdvertiseHost": "Advertise host",
|
|
18
|
+
"tunnelAdvertiseHostPlaceholder": "(optional) Host/IP to advertise to clients",
|
|
19
|
+
"tunnelAssignedIndividualAddress": "Assigned individual address",
|
|
20
|
+
"tunnelGatewayId": "Gateway id (tag)",
|
|
21
|
+
"tunnelGatewayIdPlaceholder": "(auto) Used as knxMultiRouting.gateway.id",
|
|
22
|
+
"tunnelMaxSessions": "Max sessions"
|
|
9
23
|
},
|
|
10
24
|
"outputs": {
|
|
11
25
|
"raw": "RAW telegrams"
|
|
@@ -24,6 +24,8 @@ Puedes reescribir opcionalmente:
|
|
|
24
24
|
- destino (Group Address) `knx.destination`
|
|
25
25
|
- origen (dirección física) `knx.source`
|
|
26
26
|
|
|
27
|
+
Durante la reescritura, el nodo también actualiza `knx.cemi.hex` para reflejar los valores reescritos de `knx.source`/`knx.destination` (cuando `knx.cemi.hex` está presente).
|
|
28
|
+
|
|
27
29
|
Las reglas se evalúan de arriba hacia abajo (la primera coincidencia gana).
|
|
28
30
|
|
|
29
31
|
Ejemplos:
|
|
@@ -33,5 +35,41 @@ Ejemplos:
|
|
|
33
35
|
## Metadatos
|
|
34
36
|
El nodo añade `msg.payload.knxRouterFilter`:
|
|
35
37
|
- descartados: `{ dropped: true, reason: 'event'|'ga'|'source', ... }`
|
|
36
|
-
- pasados: `{ dropped: false, rewritten: <bool>, rewrite: { ... }, original: { ... } }`
|
|
38
|
+
- pasados: `{ dropped: false, rewritten: <bool>, cemiSynced: <bool>, rewrite: { ... }, original: { ... } }`
|
|
39
|
+
|
|
40
|
+
## msg.setConfig
|
|
41
|
+
Puedes cambiar la configuración del nodo en tiempo de ejecución enviando un objeto `msg.setConfig` a la entrada.
|
|
42
|
+
Claves soportadas: `allowWrite`, `allowResponse`, `allowRead`, `gaMode`, `gaPatterns`, `srcMode`, `srcPatterns`, `rewriteGA`, `gaRewriteRules`, `rewriteSource`, `srcRewriteRules`.
|
|
43
|
+
La configuración se mantiene hasta el siguiente `msg.setConfig` o hasta redeploy/reinicio. Los mensajes de configuración no se reenvían.
|
|
44
|
+
|
|
45
|
+
Significado de las propiedades:
|
|
46
|
+
- `allowWrite`: permite telegramas `GroupValue_Write`.
|
|
47
|
+
- `allowResponse`: permite telegramas `GroupValue_Response`.
|
|
48
|
+
- `allowRead`: permite telegramas `GroupValue_Read`.
|
|
49
|
+
- `gaMode`: modo de filtro para la GA de destino (`off` = sin filtro, `allow` = permitir solo coincidencias, `block` = descartar coincidencias).
|
|
50
|
+
- `gaPatterns`: patrones GA de destino usados por `gaMode` (uno por línea, soporta `*` y `re:<regex>`).
|
|
51
|
+
- `srcMode`: modo de filtro para el source (dirección física) (`off`/`allow`/`block`).
|
|
52
|
+
- `srcPatterns`: patrones source usados por `srcMode` (uno por línea, soporta `*` y `re:<regex>`).
|
|
53
|
+
- `rewriteGA`: habilita la reescritura de `knx.destination` en los telegramas que pasan.
|
|
54
|
+
- `gaRewriteRules`: reglas de reescritura GA (`de => a`, primera coincidencia gana; soporta `*` y `re:<regex>`).
|
|
55
|
+
- `rewriteSource`: habilita la reescritura de `knx.source` en los telegramas que pasan.
|
|
56
|
+
- `srcRewriteRules`: reglas de reescritura source (`de => a`, primera coincidencia gana; soporta `*` y `re:<regex>`).
|
|
57
|
+
|
|
58
|
+
Ejemplo:
|
|
59
|
+
```js
|
|
60
|
+
msg.setConfig = {
|
|
61
|
+
allowWrite: true,
|
|
62
|
+
allowResponse: true,
|
|
63
|
+
allowRead: true,
|
|
64
|
+
gaMode: "allow",
|
|
65
|
+
gaPatterns: "1/1/*\n1/2/3",
|
|
66
|
+
srcMode: "off",
|
|
67
|
+
srcPatterns: "",
|
|
68
|
+
rewriteGA: true,
|
|
69
|
+
gaRewriteRules: "5/5/1 => 1/1/1",
|
|
70
|
+
rewriteSource: true,
|
|
71
|
+
srcRewriteRules: "15.*.* => 1.1.254"
|
|
72
|
+
};
|
|
73
|
+
return msg;
|
|
74
|
+
```
|
|
37
75
|
</script>
|
|
@@ -4,6 +4,12 @@ Ce nœud sert à **interconnecter plusieurs gateways KNX Ultimate** (plusieurs `
|
|
|
4
4
|
Il émet en sortie un objet contenant des informations **RAW** (APDU + cEMI hex + adresses) pour chaque télégramme reçu sur le bus KNX du gateway sélectionné.
|
|
5
5
|
Il peut aussi accepter ces objets RAW en entrée et les transmettre vers le bus KNX du gateway sélectionné.
|
|
6
6
|
|
|
7
|
+
## Mode serveur KNX/IP
|
|
8
|
+
Réglez **Mode** sur **Server KNX/IP** pour démarrer un serveur KNXnet/IP tunneling (UDP) intégré. Les télégrammes reçus des clients sont émis au même format RAW.
|
|
9
|
+
Le nœud accepte aussi en entrée des objets RAW et les injecte vers les clients tunneling connectés.
|
|
10
|
+
|
|
11
|
+
**Important (Advertise host) :** les clients KNXnet/IP enverront les données à l’IP annoncée par le serveur dans la CONNECT_RESPONSE. Si le client indique *connecté* mais que le serveur ne reçoit aucun télégramme, définissez **Advertise host** sur l’IP LAN du serveur accessible par le client (surtout si Node-RED tourne dans Docker/VM ou sur une machine multi-homée).
|
|
12
|
+
|
|
7
13
|
## Format du message en sortie
|
|
8
14
|
`msg.payload` contient :
|
|
9
15
|
- `knx.event` : `GroupValue_Write` / `GroupValue_Response` / `GroupValue_Read`
|
|
@@ -15,6 +21,16 @@ Il peut aussi accepter ces objets RAW en entrée et les transmettre vers le bus
|
|
|
15
21
|
- `knx.echoed` : `true` si le gateway l’a « echoed »
|
|
16
22
|
- `knxMultiRouting.gateway` : métadonnées du gateway (`id`, `name`, `physAddr`)
|
|
17
23
|
|
|
24
|
+
## Routing counter (hop count)
|
|
25
|
+
MultiRouting peut utiliser le routing counter (hop count) présent dans `knx.cemi.hex` pour éviter les boucles de télégrammes.
|
|
26
|
+
- **Respect routing counter (drop if 0)** : les télégrammes avec routing counter `0` ne sont pas transmis.
|
|
27
|
+
- **Decrement routing counter when routing** : le nœud décrémente le routing counter lors du transfert. S’il atteint `0`, le télégramme est supprimé.
|
|
28
|
+
|
|
29
|
+
La valeur courante est exposée via `knx.routingCounter` (et via `knx.cemi.hopCount` lorsque `knx.cemi` est un objet).
|
|
30
|
+
|
|
31
|
+
## Réécriture des télégrammes
|
|
32
|
+
Si vous réécrivez `knx.source` / `knx.destination` dans votre flow, vous devez aussi maintenir `knx.cemi.hex` cohérent. Recommandé : placez **KNX Router Filter** entre les nœuds MultiRouting : il maintient automatiquement `knx.cemi.hex` cohérent lors d’une réécriture.
|
|
33
|
+
|
|
18
34
|
## Notes
|
|
19
35
|
- Lors d’un transfert vers un autre gateway, l’**adresse physique source change** (elle devient celle du gateway émetteur). Utilisez `knx.source` et/ou `knxMultiRouting.gateway` pour filtrer les boucles.
|
|
20
36
|
- L’option **« Drop messages already tagged for this gateway »** aide à prévenir des boucles simples lorsque plusieurs routeurs sont interconnectés.
|