node-red-contrib-knx-ultimate 5.0.3 → 5.2.1
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 +25 -0
- package/nodes/commonFunctions.js +93 -0
- package/nodes/icons/node-matter-icon.svg +12 -0
- package/nodes/knxUltimateIoTBridge.html +10 -3
- package/nodes/knxUltimateMatterBridge.html +429 -0
- package/nodes/knxUltimateMatterBridge.js +456 -0
- package/nodes/knxUltimateMatterDevice.html +586 -0
- package/nodes/knxUltimateMatterDevice.js +308 -0
- package/nodes/locales/de/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/de/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/de/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/de/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/de/matter-config.html +25 -0
- package/nodes/locales/de/matter-config.json +26 -0
- package/nodes/locales/en/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/en/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/en/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/en/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/en/matter-config.html +25 -0
- package/nodes/locales/en/matter-config.json +26 -0
- package/nodes/locales/es/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/es/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/es/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/es/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/es/matter-config.html +25 -0
- package/nodes/locales/es/matter-config.json +26 -0
- package/nodes/locales/fr/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/fr/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/fr/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/fr/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/fr/matter-config.html +25 -0
- package/nodes/locales/fr/matter-config.json +26 -0
- package/nodes/locales/it/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/it/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/it/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/it/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/it/matter-config.html +25 -0
- package/nodes/locales/it/matter-config.json +26 -0
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +64 -0
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +80 -0
- package/nodes/locales/zh-CN/knxUltimateMatterDevice.html +56 -0
- package/nodes/locales/zh-CN/knxUltimateMatterDevice.json +109 -0
- package/nodes/locales/zh-CN/matter-config.html +25 -0
- package/nodes/locales/zh-CN/matter-config.json +26 -0
- package/nodes/matter-config.html +226 -0
- package/nodes/matter-config.js +242 -0
- package/nodes/utils/matterBridgeDeviceFactory.mjs +511 -0
- package/nodes/utils/matterBridgeEngine.mjs +290 -0
- package/nodes/utils/matterEngine.mjs +475 -0
- package/nodes/utils/matterKnxConverter.js +204 -0
- package/package.json +7 -2
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
const dptlib = require('knxultimate').dptlib
|
|
2
|
+
const matterKnxConverter = require('./utils/matterKnxConverter')
|
|
3
|
+
|
|
4
|
+
module.exports = function (RED) {
|
|
5
|
+
function knxUltimateMatterDevice (config) {
|
|
6
|
+
RED.nodes.createNode(this, config)
|
|
7
|
+
const node = this
|
|
8
|
+
node.serverKNX = RED.nodes.getNode(config.server) || undefined
|
|
9
|
+
node.serverMatter = RED.nodes.getNode(config.serverMatter) || undefined
|
|
10
|
+
|
|
11
|
+
node.topic = node.name
|
|
12
|
+
node.name = config.name === undefined || config.name === '' ? 'Matter Device (BETA)' : config.name
|
|
13
|
+
node.dpt = ''
|
|
14
|
+
node.notifyreadrequest = false
|
|
15
|
+
node.notifyreadrequestalsorespondtobus = 'false'
|
|
16
|
+
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
|
|
17
|
+
node.notifyresponse = false
|
|
18
|
+
node.notifywrite = true
|
|
19
|
+
node.initialread = true
|
|
20
|
+
node.listenallga = true // Don't remove
|
|
21
|
+
node.outputtype = 'write'
|
|
22
|
+
node.outputRBE = 'false'
|
|
23
|
+
node.inputRBE = 'false'
|
|
24
|
+
node.currentPayload = ''
|
|
25
|
+
node.passthrough = 'no'
|
|
26
|
+
node.formatmultiplyvalue = 1
|
|
27
|
+
node.formatnegativevalue = 'leave'
|
|
28
|
+
node.formatdecimalsvalue = 2
|
|
29
|
+
|
|
30
|
+
node.matterNodeId = config.matterNodeId || ''
|
|
31
|
+
node.mappings = []
|
|
32
|
+
try {
|
|
33
|
+
node.mappings = Array.isArray(config.mappings) ? config.mappings : JSON.parse(config.mappings || '[]')
|
|
34
|
+
} catch (error) {
|
|
35
|
+
node.mappings = []
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
node.initializingAtStart = config.readStatusAtStartup === undefined || config.readStatusAtStartup === 'yes'
|
|
39
|
+
node.enableNodePINS = config.enableNodePINS === 'yes'
|
|
40
|
+
node.inputs = node.enableNodePINS ? 1 : 0
|
|
41
|
+
node.outputs = node.enableNodePINS ? 1 : 0
|
|
42
|
+
|
|
43
|
+
let lastInitialReadTs = 0
|
|
44
|
+
|
|
45
|
+
const pushStatus = (status) => {
|
|
46
|
+
if (!status) return
|
|
47
|
+
const provider = node.serverKNX
|
|
48
|
+
if (provider && typeof provider.applyStatusUpdate === 'function') {
|
|
49
|
+
provider.applyStatusUpdate(node, status)
|
|
50
|
+
} else {
|
|
51
|
+
node.status(status)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const formatTs = (date) => {
|
|
56
|
+
const d = date instanceof Date ? date : new Date(date)
|
|
57
|
+
const provider = node.serverKNX
|
|
58
|
+
if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
|
|
59
|
+
return `${d.getDate()}, ${d.toLocaleTimeString()}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
node.setNodeStatus = ({ fill, shape, text, payload }) => {
|
|
63
|
+
try {
|
|
64
|
+
if (payload === undefined) payload = ''
|
|
65
|
+
const dDate = new Date()
|
|
66
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
|
|
67
|
+
node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
|
|
68
|
+
pushStatus({ fill, shape, text: (node.sMatterNodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
|
|
69
|
+
} catch (error) { /* empty */ }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
node.setNodeStatusMatter = ({ fill, shape, text, payload }) => {
|
|
73
|
+
try {
|
|
74
|
+
if (payload === undefined) payload = ''
|
|
75
|
+
const dDate = new Date()
|
|
76
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
|
|
77
|
+
node.sMatterNodeStatusText = `|Matter: ${text} ${payload} (${formatTs(dDate)})`
|
|
78
|
+
pushStatus({ fill, shape, text: node.sMatterNodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
|
|
79
|
+
} catch (error) { /* empty */ }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const safeSendToKNX = (telegram, context = 'write') => {
|
|
83
|
+
try {
|
|
84
|
+
if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
|
|
85
|
+
pushStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(new Date())})` })
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
|
|
89
|
+
} catch (error) {
|
|
90
|
+
pushStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const sendMappingToKNX = (mapping, payload, outputtype = 'write') => {
|
|
95
|
+
if (payload === undefined || mapping.ga === undefined || mapping.ga === '') return
|
|
96
|
+
safeSendToKNX({
|
|
97
|
+
grpaddr: mapping.ga,
|
|
98
|
+
payload,
|
|
99
|
+
dpt: mapping.dpt,
|
|
100
|
+
outputtype
|
|
101
|
+
}, outputtype)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const getMatterManager = () => {
|
|
105
|
+
if (!node.serverMatter || node.serverMatter.matterManager === null || node.serverMatter.matterManager === undefined) return undefined
|
|
106
|
+
return node.serverMatter.matterManager
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Send the cached Matter value of a status mapping to KNX
|
|
110
|
+
const sendCachedStatusToKNX = (mapping, outputtype = 'write') => {
|
|
111
|
+
try {
|
|
112
|
+
const manager = getMatterManager()
|
|
113
|
+
if (manager === undefined) return false
|
|
114
|
+
const rawValue = manager.getCachedAttribute(node.matterNodeId, mapping.endpointId, mapping.clusterId, mapping.target)
|
|
115
|
+
if (rawValue === undefined) return false
|
|
116
|
+
const payload = matterKnxConverter.matterToKnx(mapping.clusterId, mapping.target, rawValue)
|
|
117
|
+
if (payload === undefined) return false
|
|
118
|
+
sendMappingToKNX(mapping, payload, outputtype)
|
|
119
|
+
return true
|
|
120
|
+
} catch (error) {
|
|
121
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'dot', text: `Cached status error ${error.message}`, payload: '' })
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// KNX -> Matter
|
|
127
|
+
node.handleSend = (msg) => {
|
|
128
|
+
if (!msg || !msg.knx) return
|
|
129
|
+
if (node.matterNodeId === '') {
|
|
130
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'ring', text: 'Missing Matter device selection', payload: '' })
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
if (!Array.isArray(node.mappings)) return
|
|
135
|
+
if (msg.knx.event === 'GroupValue_Read') {
|
|
136
|
+
// Respond to read requests with the cached Matter values
|
|
137
|
+
node.mappings.filter((m) => m.direction === 'status' && m.ga === msg.knx.destination).forEach((mapping) => {
|
|
138
|
+
if (sendCachedStatusToKNX(mapping, 'response')) {
|
|
139
|
+
node.setNodeStatus({ fill: 'grey', shape: 'ring', text: 'Response', payload: mapping.ga })
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
const manager = getMatterManager()
|
|
145
|
+
node.mappings.filter((m) => m.direction === 'command' && m.ga === msg.knx.destination).forEach((mapping) => {
|
|
146
|
+
const value = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(mapping.dpt))
|
|
147
|
+
const matterAction = matterKnxConverter.knxToMatter(mapping, value)
|
|
148
|
+
if (matterAction === null) return
|
|
149
|
+
if (manager === undefined || node.serverMatter.linkStatus !== 'connected') {
|
|
150
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'ring', text: 'Matter controller unavailable', payload: '' })
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
manager.writeMatterQueueAdd({
|
|
154
|
+
nodeId: node.matterNodeId,
|
|
155
|
+
endpointId: mapping.endpointId,
|
|
156
|
+
clusterId: mapping.clusterId,
|
|
157
|
+
kind: matterAction.kind,
|
|
158
|
+
name: matterAction.name,
|
|
159
|
+
args: matterAction.args
|
|
160
|
+
})
|
|
161
|
+
node.setNodeStatusMatter({
|
|
162
|
+
fill: 'green',
|
|
163
|
+
shape: 'dot',
|
|
164
|
+
text: `KNX->Matter ${mapping.clusterName || mapping.clusterId}.${matterAction.name}`,
|
|
165
|
+
payload: value
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
} catch (error) {
|
|
169
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'dot', text: `KNX->Matter error ${error.message}`, payload: '' })
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Matter -> KNX (attribute change reported by the subscription)
|
|
174
|
+
node.handleSendMatter = (_event) => {
|
|
175
|
+
try {
|
|
176
|
+
if (_event === undefined) return
|
|
177
|
+
if (_event.nodeId !== node.matterNodeId) return
|
|
178
|
+
const matchingMappings = node.mappings.filter((m) => m.direction === 'status' &&
|
|
179
|
+
Number(m.endpointId) === Number(_event.endpointId) &&
|
|
180
|
+
Number(m.clusterId) === Number(_event.clusterId) &&
|
|
181
|
+
m.targetKind === 'attribute' &&
|
|
182
|
+
m.target === _event.attributeName)
|
|
183
|
+
matchingMappings.forEach((mapping) => {
|
|
184
|
+
const payload = matterKnxConverter.matterToKnx(_event.clusterId, _event.attributeName, _event.value)
|
|
185
|
+
if (payload === undefined) return
|
|
186
|
+
sendMappingToKNX(mapping, payload, 'write')
|
|
187
|
+
node.setNodeStatusMatter({
|
|
188
|
+
fill: 'blue',
|
|
189
|
+
shape: 'dot',
|
|
190
|
+
text: `Matter->KNX ${_event.attributeName}`,
|
|
191
|
+
payload
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
if (node.enableNodePINS) {
|
|
195
|
+
node.send({
|
|
196
|
+
topic: `${_event.clusterId}.${_event.attributeName}`,
|
|
197
|
+
payload: _event.value,
|
|
198
|
+
matter: _event
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'dot', text: `Matter->KNX error ${error.message}`, payload: '' })
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Cluster events (e.g. switch presses). Forwarded to the flow only.
|
|
207
|
+
node.handleMatterClusterEvent = (_event) => {
|
|
208
|
+
try {
|
|
209
|
+
if (_event === undefined || _event.nodeId !== node.matterNodeId) return
|
|
210
|
+
if (node.enableNodePINS) {
|
|
211
|
+
node.send({
|
|
212
|
+
topic: `${_event.clusterId}.${_event.eventName}`,
|
|
213
|
+
payload: _event.events,
|
|
214
|
+
matter: _event
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
} catch (error) { /* empty */ }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Called by matter-config when the paired node completed its initial sync (or reconnected)
|
|
221
|
+
node.handleMatterNodeInitialized = () => {
|
|
222
|
+
try {
|
|
223
|
+
if (!node.initializingAtStart) return
|
|
224
|
+
if (node.matterNodeId === '') return
|
|
225
|
+
const now = Date.now()
|
|
226
|
+
if ((now - lastInitialReadTs) < 5000) return // Avoid duplicate bursts on reconnections
|
|
227
|
+
let sentCount = 0
|
|
228
|
+
node.mappings.filter((m) => m.direction === 'status' && m.targetKind === 'attribute').forEach((mapping) => {
|
|
229
|
+
if (sendCachedStatusToKNX(mapping, 'write')) sentCount += 1
|
|
230
|
+
})
|
|
231
|
+
if (sentCount > 0) {
|
|
232
|
+
lastInitialReadTs = now
|
|
233
|
+
node.setNodeStatusMatter({ fill: 'green', shape: 'dot', text: `Initial read: ${sentCount} value(s) sent`, payload: '' })
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'dot', text: `Initial read error ${error.message}`, payload: '' })
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (node.serverKNX) {
|
|
241
|
+
try {
|
|
242
|
+
node.serverKNX.removeClient(node)
|
|
243
|
+
node.serverKNX.addClient(node)
|
|
244
|
+
} catch (error) {
|
|
245
|
+
RED.log.error(`knxUltimateMatterDevice: register KNX client error ${error.message}`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (node.serverMatter) {
|
|
249
|
+
try {
|
|
250
|
+
node.serverMatter.removeClient(node)
|
|
251
|
+
node.serverMatter.addClient(node)
|
|
252
|
+
} catch (error) {
|
|
253
|
+
RED.log.error(`knxUltimateMatterDevice: register client error ${error.message}`)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Flow input pin: send raw Matter commands/attribute writes.
|
|
258
|
+
// msg.payload = { endpointId, clusterId, command: 'on', args: {...} } or { endpointId, clusterId, attribute: 'onOff', value: true }
|
|
259
|
+
node.on('input', (msg, send, done) => {
|
|
260
|
+
if (!node.enableNodePINS) {
|
|
261
|
+
if (done) done()
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
const manager = getMatterManager()
|
|
266
|
+
if (manager === undefined) throw new Error('Matter controller unavailable')
|
|
267
|
+
const payload = msg.payload || {}
|
|
268
|
+
if (payload.command !== undefined) {
|
|
269
|
+
manager.writeMatterQueueAdd({
|
|
270
|
+
nodeId: node.matterNodeId,
|
|
271
|
+
endpointId: payload.endpointId,
|
|
272
|
+
clusterId: payload.clusterId,
|
|
273
|
+
kind: 'command',
|
|
274
|
+
name: payload.command,
|
|
275
|
+
args: payload.args
|
|
276
|
+
})
|
|
277
|
+
} else if (payload.attribute !== undefined) {
|
|
278
|
+
manager.writeMatterQueueAdd({
|
|
279
|
+
nodeId: node.matterNodeId,
|
|
280
|
+
endpointId: payload.endpointId,
|
|
281
|
+
clusterId: payload.clusterId,
|
|
282
|
+
kind: 'attributeWrite',
|
|
283
|
+
name: payload.attribute,
|
|
284
|
+
args: payload.value
|
|
285
|
+
})
|
|
286
|
+
} else {
|
|
287
|
+
throw new Error('msg.payload must contain "command" or "attribute"')
|
|
288
|
+
}
|
|
289
|
+
node.setNodeStatusMatter({ fill: 'green', shape: 'dot', text: 'Flow->Matter', payload })
|
|
290
|
+
if (done) done()
|
|
291
|
+
} catch (error) {
|
|
292
|
+
node.setNodeStatusMatter({ fill: 'red', shape: 'dot', text: `Flow error ${error.message}`, payload: '' })
|
|
293
|
+
if (done) done(error)
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
node.on('close', () => {
|
|
298
|
+
try {
|
|
299
|
+
if (node.serverKNX) node.serverKNX.removeClient(node)
|
|
300
|
+
} catch (error) { /* empty */ }
|
|
301
|
+
try {
|
|
302
|
+
if (node.serverMatter) node.serverMatter.removeClient(node)
|
|
303
|
+
} catch (error) { /* empty */ }
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
RED.nodes.registerType('knxUltimateMatterDevice', knxUltimateMatterDevice)
|
|
308
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script type="text/markdown" data-help-name="knxUltimateMatterBridge">
|
|
2
|
+
# Matter Bridge (BETA)
|
|
3
|
+
|
|
4
|
+
> Dieser Node ist in **BETA**: Er funktioniert, aber Details können sich zwischen Releases noch ändern.
|
|
5
|
+
|
|
6
|
+
## Übersicht
|
|
7
|
+
|
|
8
|
+
Der Matter Bridge Node stellt **KNX-Gruppenadressen als Matter-Geräte** bereit: Alexa, Google Home, Apple Home (oder jeder Matter-Controller) koppeln diese Bridge **einmal** und sehen alle konfigurierten KNX-Geräte mit den von dir vergebenen Namen — bereit für App- und Sprachsteuerung.
|
|
9
|
+
|
|
10
|
+
Das ist die Gegenrichtung zum *Matter Device*-Node: Dort steuert KNX ein Matter-Gerät, hier steuern Matter-Controller KNX.
|
|
11
|
+
|
|
12
|
+
## Konfiguration
|
|
13
|
+
|
|
14
|
+
|Feld|Beschreibung|
|
|
15
|
+
|--|--|
|
|
16
|
+
| KNX GW | KNX-Gateway für die Telegramme. **Optional**: Ohne Gateway arbeitet die Bridge im Nur-Flow-Modus — aktiviere die Node-PINs und steuere jedes Gerät über Flow-Nachrichten |
|
|
17
|
+
| Name der Matter-Bridge | Wie die Bridge selbst in den Matter-Apps heißt |
|
|
18
|
+
| Port | UDP-Port des Matter-Servers (Standard 5540). Nur ein Bridge-Node pro Node-RED-Instanz. |
|
|
19
|
+
| Geräte | Die auf Matter bereitgestellten virtuellen KNX-Geräte (siehe unten) |
|
|
20
|
+
| Status-GAs beim Start lesen | Sendet beim Start ein `GroupValue_Read` an jede Status-GA, damit die Matter-Attribute gefüllt sind |
|
|
21
|
+
|
|
22
|
+
## Geräte
|
|
23
|
+
|
|
24
|
+
Jede Zeile ist ein Gerät, das Alexa & Co. anzeigen. Wähle den **Typ**, gib den **Namen** ein, den der Assistent verwendet, und fülle die Gruppenadressen aus (mit ETS-Autovervollständigung):
|
|
25
|
+
|
|
26
|
+
|Typ|Gruppenadressen|
|
|
27
|
+
|--|--|
|
|
28
|
+
| Ein/Aus-Licht, Steckdose | Ein/Aus Befehls-GA, Ein/Aus Status-GA (DPT 1.001) |
|
|
29
|
+
| Dimmbares Licht | + Dimmen % Befehls-/Status-GA (DPT 5.001) |
|
|
30
|
+
| RGB-Farblicht | + RGB-Farbe Befehls-/Status-GA (DPT 232.600). Die Matter-Farbe (Hue/Saturation oder XY, vom Farbrad der App) wird von/zur KNX-RGB-Triplette konvertiert |
|
|
31
|
+
| Tunable-White-Licht | + Farbtemperatur Befehls-/Status-GA in Kelvin (DPT 7.600) |
|
|
32
|
+
| Rollladen / Jalousie | Auf/Ab (DPT 1.008), Stopp (DPT 1.017), Position % Befehl/Status (DPT 5.001), optionale Positionsumkehr |
|
|
33
|
+
| Thermostat (Heizung) | GA aktuelle Temperatur, Sollwert Befehls-/Status-GA (DPT 9.001) |
|
|
34
|
+
| Ventilator / Lüftung | Geschwindigkeit % Befehls-/Status-GA (DPT 5.001) |
|
|
35
|
+
| Sensoren (Temperatur, Feuchte, Licht, Präsenz, Kontakt) | Je eine Status-GA |
|
|
36
|
+
| Rauch-/CO-Melder | Rauchalarm Status-GA + optionale CO-Alarm Status-GA (DPT 1.005): kritische Benachrichtigungen auf dem Telefon |
|
|
37
|
+
| Wasserleckmelder | Leckage Status-GA (DPT 1.005) |
|
|
38
|
+
| Luftqualitätssensor (CO2) | CO2 Status-GA in ppm (DPT 9.008); die Luftqualitätsklasse (gut/mäßig/schlecht...) wird automatisch abgeleitet |
|
|
39
|
+
| Saugroboter | **Nur Flow**: keine Gruppenadressen. Aktiviere die Node-PINs: Befehle des Assistenten ("Reinigung starten", Pause/Fortsetzen/zur Basis) kommen am Output als `rvcmode`/`rvccommand` an; melde den Zustand zurück mit `msg.payload = { device, function: "rvcstate", value: "running"|"docked"|"charging"|"paused"|"error" }` und den Modus mit `function: "rvcmode", value: "cleaning"|"idle"`. Integriere deinen Roomba/Roborock mit beliebigen Node-RED-Nodes und stelle ihn Alexa/Apple bereit |
|
|
40
|
+
|
|
41
|
+
- **Befehls-GA**: wird auf den KNX-Bus geschrieben, wenn der Assistent einen Befehl sendet.
|
|
42
|
+
- **Status-GA**: wird vom Bus gelesen, um die Matter-Attribute (und die Apps) aktuell zu halten.
|
|
43
|
+
|
|
44
|
+
## Kopplung
|
|
45
|
+
|
|
46
|
+
1. **Deployen**, ein paar Sekunden warten, dann den Node erneut öffnen.
|
|
47
|
+
2. Das Kopplungs-Panel zeigt den **QR-Code** und den **manuellen Code**: scannen oder in Alexa / Google Home / Apple Home eingeben ("Matter-Gerät hinzufügen").
|
|
48
|
+
3. Mehrere Controller können mit derselben Bridge gekoppelt werden (Matter Multi-Fabric).
|
|
49
|
+
|
|
50
|
+
Der Button **Kopplung zurücksetzen** entfernt alle gekoppelten Controller und startet die Kopplungs-Ankündigung neu.
|
|
51
|
+
|
|
52
|
+
## Node-PINs
|
|
53
|
+
|
|
54
|
+
Wenn du die Input/Output-PINs des Nodes aktivierst:
|
|
55
|
+
|
|
56
|
+
- **Input**: aktualisiere den Matter-Zustand eines virtuellen Geräts aus dem Flow, ohne über den KNX-Bus zu gehen: `msg.payload = { device: "Küchenlicht", function: "onoff", value: true }` (`device` akzeptiert den Namen oder die interne ID; `function` ist eine von `onoff`, `level`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`). Nützlich, um im Flow berechnete Werte (z.B. einen virtuellen Sensor) für Alexa & Co. bereitzustellen.
|
|
57
|
+
- **Output**: Jeder von einem Matter-Controller empfangene Befehl wird an den Flow weitergeleitet: `msg.topic` = Gerätename, `msg.payload` = Wert, `msg.matter` = der rohe Befehl. Geräte ohne Befehls-GA werden zu **Nur-Flow-Geräten**: Der Befehl erreicht deinen Flow und du entscheidest, was damit geschieht.
|
|
58
|
+
|
|
59
|
+
## Hinweise
|
|
60
|
+
|
|
61
|
+
- Der Node-RED-Host muss **IPv6 link-local** aktiviert haben (Standard-Matter-Anforderung) und vom Controller im lokalen Netzwerk erreichbar sein.
|
|
62
|
+
- Die Bridge-Identität wird in `knxultimatestorage/matter` im Node-RED-Benutzerverzeichnis gespeichert: Re-Deploys erfordern KEINE neue Kopplung.
|
|
63
|
+
- Umbenennungen und neue Geräte werden von den Controllern automatisch übernommen; entfernte Geräte verschwinden aus den Apps.
|
|
64
|
+
</script>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateMatterBridge": {
|
|
3
|
+
"title": "Matter Bridge (BETA)",
|
|
4
|
+
"bridge_name": "Name der Matter-Bridge",
|
|
5
|
+
"port": "Port",
|
|
6
|
+
"devices_help": "Füge die KNX-Geräte hinzu, die über Matter bereitgestellt werden sollen. Der \"Name\" ist der, den Alexa/Google/Apple anzeigen und für Sprachbefehle verwenden. Befehls-GA = wird geschrieben, wenn der Assistent einen Befehl sendet; Status-GA = wird vom Bus gelesen, um die App aktuell zu halten.",
|
|
7
|
+
"read_status_startup": "Status-Gruppenadressen beim Start lesen",
|
|
8
|
+
"opt_yes": "Ja",
|
|
9
|
+
"opt_no": "Nein",
|
|
10
|
+
"node_pins": "Node Input/Output PINs",
|
|
11
|
+
"node_pins_hide": "Ausblenden",
|
|
12
|
+
"node_pins_show": "Input/Output-PINs anzeigen",
|
|
13
|
+
"fields": {
|
|
14
|
+
"type": "Gerätetyp",
|
|
15
|
+
"name": "Name (von Alexa & Co. angezeigt)",
|
|
16
|
+
"name_placeholder": "z.B. Küchenlicht",
|
|
17
|
+
"invert": "Position % invertieren"
|
|
18
|
+
},
|
|
19
|
+
"functions": {
|
|
20
|
+
"fn_onoff_cmd": "Ein/Aus Befehls-GA",
|
|
21
|
+
"fn_onoff_status": "Ein/Aus Status-GA",
|
|
22
|
+
"fn_level_cmd": "Dimmen % Befehls-GA",
|
|
23
|
+
"fn_level_status": "Dimmen % Status-GA",
|
|
24
|
+
"fn_sensor_ga": "Sensor Status-GA",
|
|
25
|
+
"fn_updown": "Auf/Ab Befehls-GA",
|
|
26
|
+
"fn_stop": "Stopp Befehls-GA",
|
|
27
|
+
"fn_position_cmd": "Position % Befehls-GA",
|
|
28
|
+
"fn_position_status": "Position % Status-GA",
|
|
29
|
+
"fn_currenttemp": "GA aktuelle Temperatur",
|
|
30
|
+
"fn_setpoint_cmd": "Sollwert Befehls-GA",
|
|
31
|
+
"fn_setpoint_status": "Sollwert Status-GA",
|
|
32
|
+
"fn_rgb_cmd": "RGB-Farbe Befehls-GA (DPT 232.600)",
|
|
33
|
+
"fn_rgb_status": "RGB-Farbe Status-GA (DPT 232.600)",
|
|
34
|
+
"fn_ct_cmd": "Farbtemperatur Befehls-GA (Kelvin)",
|
|
35
|
+
"fn_ct_status": "Farbtemperatur Status-GA (Kelvin)",
|
|
36
|
+
"fn_smoke_ga": "Rauchalarm Status-GA",
|
|
37
|
+
"fn_co_ga": "CO-Alarm Status-GA (optional)",
|
|
38
|
+
"fn_leak_ga": "Leckage Status-GA",
|
|
39
|
+
"fn_co2_ga": "CO2 Status-GA (ppm, DPT 9.008)",
|
|
40
|
+
"fn_fanspeed_cmd": "Lüftergeschwindigkeit % Befehls-GA",
|
|
41
|
+
"fn_fanspeed_status": "Lüftergeschwindigkeit % Status-GA"
|
|
42
|
+
},
|
|
43
|
+
"types": {
|
|
44
|
+
"onofflight": "Ein/Aus-Licht",
|
|
45
|
+
"dimmablelight": "Dimmbares Licht",
|
|
46
|
+
"rgblight": "RGB-Farblicht",
|
|
47
|
+
"colortemperaturelight": "Tunable-White-Licht (Farbtemperatur)",
|
|
48
|
+
"onoffplug": "Steckdose (Ein/Aus)",
|
|
49
|
+
"temperaturesensor": "Temperatursensor",
|
|
50
|
+
"humiditysensor": "Feuchtesensor",
|
|
51
|
+
"lightsensor": "Lichtsensor (Lux)",
|
|
52
|
+
"occupancysensor": "Präsenzsensor",
|
|
53
|
+
"contactsensor": "Kontaktsensor",
|
|
54
|
+
"windowcovering": "Rollladen / Jalousie",
|
|
55
|
+
"thermostat": "Thermostat (Heizung)",
|
|
56
|
+
"fan": "Ventilator / Lüftung",
|
|
57
|
+
"smokecoalarm": "Rauch-/CO-Melder",
|
|
58
|
+
"waterleakdetector": "Wasserleckmelder",
|
|
59
|
+
"airqualitysensor": "Luftqualitätssensor (CO2)",
|
|
60
|
+
"robotvacuum": "Saugroboter (nur Flow, über Node-PINs)"
|
|
61
|
+
},
|
|
62
|
+
"pairing": {
|
|
63
|
+
"title": "Kopplung (Alexa, Google Home, Apple Home...)",
|
|
64
|
+
"not_running": "Die Matter-Bridge läuft noch nicht. Deployen, ein paar Sekunden warten und dann auf Aktualisieren klicken.",
|
|
65
|
+
"commissioned": "Bridge mit diesen Controllern gekoppelt:",
|
|
66
|
+
"awaiting": "Warte auf Kopplung. Scanne den QR-Code oder gib den manuellen Code in deiner Matter-App ein:",
|
|
67
|
+
"qr_link": "QR-Code im Browser öffnen",
|
|
68
|
+
"deploy_first": "Bitte zuerst diesen Node deployen und dann auf Aktualisieren klicken.",
|
|
69
|
+
"reset_button": "Kopplung zurücksetzen",
|
|
70
|
+
"reset_confirm": "ALLE gekoppelten Controller entfernen und die Kopplung neu starten? Die Geräte verschwinden aus Alexa/Google/Apple.",
|
|
71
|
+
"reset_ok": "Kopplung zurückgesetzt. Die Bridge wartet wieder auf Kopplung."
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"common": {
|
|
75
|
+
"ga": "GA",
|
|
76
|
+
"dpt": "DPT",
|
|
77
|
+
"name": "Name",
|
|
78
|
+
"knx_gw": "KNX GW"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script type="text/markdown" data-help-name="knxUltimateMatterDevice">
|
|
2
|
+
# Matter-Gerät (BETA)
|
|
3
|
+
|
|
4
|
+
> Dieser Node ist in **BETA**: Er funktioniert, aber Details können sich zwischen Releases noch ändern.
|
|
5
|
+
|
|
6
|
+
## Übersicht
|
|
7
|
+
|
|
8
|
+
Der Matter Device Node verbindet ein **Matter-Gerät** mit **KNX-Gruppenadressen**. Der Node arbeitet über den Konfigurations-Node *Matter Controller*, der die Geräte in seine eigene Matter-Fabric kommissioniert.
|
|
9
|
+
|
|
10
|
+
- **Steuere** jedes gekoppelte Matter-Gerät vom KNX-Bus aus (Ein/Aus, Dimmen, Rollläden, Thermostate, Schlösser, ...).
|
|
11
|
+
- **Verfolge** jedes Attribut des Geräts auf KNX-Gruppenadressen (Statusmeldungen, Sensoren, Leistungsmessung, Batterie...).
|
|
12
|
+
- Vollständig generisch: Die Zuordnungsliste wird aus den **realen Endpoints/Clustern** des Geräts aufgebaut.
|
|
13
|
+
|
|
14
|
+
## Konfiguration
|
|
15
|
+
|
|
16
|
+
|Feld|Beschreibung|
|
|
17
|
+
|--|--|
|
|
18
|
+
| KNX GW | KNX-Gateway für die Telegramme |
|
|
19
|
+
| Matter Ctrl | Der Matter-Controller-Konfigurations-Node (dort werden die Geräte gekoppelt) |
|
|
20
|
+
| Gerät | Wähle das gekoppelte Matter-Gerät aus der Autovervollständigung |
|
|
21
|
+
| Zuordnungen | Eine Zeile pro Zuordnung Gruppenadresse ↔ Matter-Cluster |
|
|
22
|
+
| Status beim Start lesen | Wenn aktiviert, sendet der Node die aktuellen zwischengespeicherten Werte beim Deploy/Verbinden |
|
|
23
|
+
| Node Input/Output PINs | Aktiviert die Node-RED-Pins für den rohen Matter-Zugriff |
|
|
24
|
+
|
|
25
|
+
## Zuordnungen
|
|
26
|
+
|
|
27
|
+
Die Ziel-Liste zeigt die unterstützten Funktionen mit verständlichen Namen wie *"Ein/Aus-Schalter"* oder *"Momentanleistung (W)"*, gefiltert nach dem, was das Gerät wirklich bereitstellt, mit dem aktuellen Wert in eckigen Klammern.
|
|
28
|
+
|
|
29
|
+
Jede Zuordnungszeile hat eine **Richtung**:
|
|
30
|
+
|
|
31
|
+
- **KNX → Matter (Befehl)**: Ein Telegramm auf der Gruppenadresse ruft einen Matter-Cluster-Befehl auf oder schreibt ein Attribut. Beispiel: GA `1/1/1` DPT 1.001 → `OnOff.on/off` (der boolesche Wert wählt automatisch Ein oder Aus).
|
|
32
|
+
- **Matter → KNX (Status)**: Wenn sich das abonnierte Matter-Attribut ändert, wird sein Wert konvertiert und auf die Gruppenadresse geschrieben. `GroupValue_Read`-Anfragen werden mit dem zwischengespeicherten Wert beantwortet.
|
|
33
|
+
|
|
34
|
+
Die gängigen Cluster werden automatisch in KNX-freundliche Werte konvertiert:
|
|
35
|
+
|
|
36
|
+
|Cluster|Konvertierung|
|
|
37
|
+
|--|--|
|
|
38
|
+
| OnOff | boolesch (DPT 1.001) |
|
|
39
|
+
| LevelControl | 0-254 ↔ Prozent (DPT 5.001) |
|
|
40
|
+
| WindowCovering | percent100ths ↔ Prozent (DPT 5.001), Auf/Ab (DPT 1.008) |
|
|
41
|
+
| ColorControl | Mired ↔ Kelvin (DPT 7.600) |
|
|
42
|
+
| Thermostat | Zenti-°C ↔ °C (DPT 9.001) |
|
|
43
|
+
| Temperatur/Feuchte | Zenti-Einheiten ↔ Einheiten (DPT 9.001/9.007) |
|
|
44
|
+
| Beleuchtungsstärke | Log-Skala ↔ Lux (DPT 9.004) |
|
|
45
|
+
| PowerSource | Halb-Prozent ↔ Batterie-Prozent (DPT 5.001) |
|
|
46
|
+
| Elektrische Leistung/Energie | mW ↔ W (DPT 14.056), mWh ↔ kWh (DPT 13.013) |
|
|
47
|
+
|
|
48
|
+
Andere Cluster/Attribute werden unverändert durchgereicht; der gewählte DPT übernimmt die finale KNX-Kodierung.
|
|
49
|
+
|
|
50
|
+
## Node-PINs
|
|
51
|
+
|
|
52
|
+
Wenn du die Node-PINs aktivierst:
|
|
53
|
+
|
|
54
|
+
- **Input**: sende rohe Befehle: `msg.payload = { endpointId: 1, clusterId: 6, command: "toggle" }` oder Attribut-Schreibzugriffe: `msg.payload = { endpointId: 1, clusterId: 8, attribute: "onLevel", value: 128 }`
|
|
55
|
+
- **Output**: empfängt jede Attributänderung (`msg.matter` enthält das komplette Ereignis) und jedes Cluster-Ereignis (Tastendrücke usw.).
|
|
56
|
+
</script>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateMatterDevice": {
|
|
3
|
+
"title": "Matter-Gerät (BETA)",
|
|
4
|
+
"paletteLabel": "Matter Device (BETA)",
|
|
5
|
+
"matter_controller": "Matter Ctrl",
|
|
6
|
+
"matter_device": "Gerät",
|
|
7
|
+
"device_placeholder": "Klicken, um ein gekoppeltes Matter-Gerät auszuwählen",
|
|
8
|
+
"mapping_help": "Füge pro Zuordnung eine Zeile hinzu. \"KNX → Matter\" ruft einen Cluster-Befehl auf (oder schreibt ein Attribut), wenn ein Telegramm auf der Gruppenadresse eintrifft. \"Matter → KNX\" schreibt den Attributwert bei jeder Änderung auf die Gruppenadresse (Leseanfragen werden mit dem zwischengespeicherten Wert beantwortet).",
|
|
9
|
+
"select_target": "-- Matter-Ziel auswählen --",
|
|
10
|
+
"stored_target": "Gespeichert",
|
|
11
|
+
"read_status_startup": "Status beim Start lesen",
|
|
12
|
+
"opt_yes_emit": "Ja, und KNX-Telegramme senden.",
|
|
13
|
+
"opt_no": "Nein",
|
|
14
|
+
"node_pins": "Node Input/Output PINs",
|
|
15
|
+
"node_pins_hide": "Ausblenden",
|
|
16
|
+
"node_pins_show": "Input/Output-PINs anzeigen",
|
|
17
|
+
"direction": {
|
|
18
|
+
"command": "KNX → Matter (Befehl)",
|
|
19
|
+
"status": "Matter → KNX (Status)"
|
|
20
|
+
},
|
|
21
|
+
"fields": {
|
|
22
|
+
"direction": "Richtung",
|
|
23
|
+
"target": "Matter Endpoint / Cluster / Ziel",
|
|
24
|
+
"ga": "Gruppenadresse",
|
|
25
|
+
"dpt": "DPT",
|
|
26
|
+
"ganame": "Name"
|
|
27
|
+
},
|
|
28
|
+
"no_simple_targets": "Keine unterstützten Funktionen auf diesem Gerät gefunden (oder das Gerät ist noch nicht verbunden).",
|
|
29
|
+
"endpoint": "Endpoint",
|
|
30
|
+
"friendly": {
|
|
31
|
+
"onoff_switch": "Ein/Aus-Schalter (ein-/ausschalten)",
|
|
32
|
+
"onoff_toggle": "Umschalten (Toggle)",
|
|
33
|
+
"onoff_state": "Ein/Aus-Status",
|
|
34
|
+
"level_set": "Helligkeit / Stufe setzen (%)",
|
|
35
|
+
"level_state": "Aktuelle Helligkeit / Stufe (%)",
|
|
36
|
+
"colortemp_set": "Farbtemperatur setzen (Kelvin)",
|
|
37
|
+
"colortemp_state": "Aktuelle Farbtemperatur (Kelvin)",
|
|
38
|
+
"cover_updown": "Rollladen Auf/Ab",
|
|
39
|
+
"cover_stop": "Rollladen Stopp",
|
|
40
|
+
"cover_position_set": "Rollladen auf Position fahren (%)",
|
|
41
|
+
"cover_position_state": "Aktuelle Rollladenposition (%)",
|
|
42
|
+
"cover_tilt_set": "Lamellen neigen (%)",
|
|
43
|
+
"cover_tilt_state": "Aktuelle Lamellenneigung (%)",
|
|
44
|
+
"thermo_localtemp": "Raumtemperatur (°C)",
|
|
45
|
+
"thermo_heatsp_set": "Heiz-Sollwert setzen (°C)",
|
|
46
|
+
"thermo_coolsp_set": "Kühl-Sollwert setzen (°C)",
|
|
47
|
+
"thermo_heatsp_state": "Heiz-Sollwert (°C)",
|
|
48
|
+
"thermo_coolsp_state": "Kühl-Sollwert (°C)",
|
|
49
|
+
"fan_speed_set": "Lüftergeschwindigkeit setzen (%)",
|
|
50
|
+
"fan_speed_state": "Aktuelle Lüftergeschwindigkeit (%)",
|
|
51
|
+
"lock_cmd": "Tür ver-/entriegeln",
|
|
52
|
+
"lock_state": "Schlossstatus (verriegelt)",
|
|
53
|
+
"temp_measured": "Gemessene Temperatur (°C)",
|
|
54
|
+
"humidity_measured": "Relative Luftfeuchte (%)",
|
|
55
|
+
"illuminance_measured": "Beleuchtungsstärke (Lux)",
|
|
56
|
+
"occupancy_state": "Anwesenheit erkannt",
|
|
57
|
+
"contact_state": "Kontaktstatus (geschlossen/offen)",
|
|
58
|
+
"battery_percent": "Batteriestand (%)",
|
|
59
|
+
"power_active": "Momentanleistung (W)",
|
|
60
|
+
"power_voltage": "Spannung (V)",
|
|
61
|
+
"power_current": "Strom (A)",
|
|
62
|
+
"energy_imported": "Energieverbrauch (kWh)",
|
|
63
|
+
"identify_cmd": "Gerät identifizieren (blinken)",
|
|
64
|
+
"switch_position": "Tastenposition"
|
|
65
|
+
},
|
|
66
|
+
"devtype": {
|
|
67
|
+
"onofflight": "Ein/Aus-Licht",
|
|
68
|
+
"dimmablelight": "Dimmbares Licht",
|
|
69
|
+
"colortemperaturelight": "Licht mit Farbtemperatur",
|
|
70
|
+
"extendedcolorlight": "Farblicht",
|
|
71
|
+
"onoffpluginunit": "Steckdose (Ein/Aus)",
|
|
72
|
+
"dimmablepluginunit": "Steckdose (dimmbar)",
|
|
73
|
+
"smartplug": "Smarte Steckdose",
|
|
74
|
+
"contactsensor": "Kontaktsensor",
|
|
75
|
+
"lightsensor": "Lichtsensor",
|
|
76
|
+
"occupancysensor": "Präsenzsensor",
|
|
77
|
+
"temperaturesensor": "Temperatursensor",
|
|
78
|
+
"humiditysensor": "Feuchtesensor",
|
|
79
|
+
"pressuresensor": "Drucksensor",
|
|
80
|
+
"flowsensor": "Durchflusssensor",
|
|
81
|
+
"doorlock": "Türschloss",
|
|
82
|
+
"windowcovering": "Rollladen/Jalousie",
|
|
83
|
+
"thermostat": "Thermostat",
|
|
84
|
+
"fan": "Ventilator",
|
|
85
|
+
"genericswitch": "Taster/Schalter",
|
|
86
|
+
"airqualitysensor": "Luftqualitätssensor",
|
|
87
|
+
"smokecoalarm": "Rauch-/CO-Melder",
|
|
88
|
+
"waterleakdetector": "Wasserleckmelder",
|
|
89
|
+
"roomairconditioner": "Klimagerät",
|
|
90
|
+
"airpurifier": "Luftreiniger",
|
|
91
|
+
"speaker": "Lautsprecher",
|
|
92
|
+
"pumpcontroller": "Pumpe",
|
|
93
|
+
"onofflightswitch": "Lichtschalter",
|
|
94
|
+
"dimmerswitch": "Dimmerschalter",
|
|
95
|
+
"colordimmerswitch": "Farbdimmerschalter",
|
|
96
|
+
"powersource": "Stromversorgung",
|
|
97
|
+
"electricalsensor": "Elektrischer Sensor",
|
|
98
|
+
"bridgednode": "Bridged-Gerät",
|
|
99
|
+
"aggregator": "Bridge/Aggregator",
|
|
100
|
+
"rootnode": "Root-Node"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"common": {
|
|
104
|
+
"ga": "GA",
|
|
105
|
+
"dpt": "DPT",
|
|
106
|
+
"name": "Name",
|
|
107
|
+
"knx_gw": "KNX GW"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script type="text/markdown" data-help-name="matter-config">
|
|
2
|
+
# Matter Controller
|
|
3
|
+
|
|
4
|
+
## Übersicht
|
|
5
|
+
|
|
6
|
+
Dieser Konfigurations-Node ist ein vollwertiger **Matter-Controller**: Er erstellt seine eigene Matter-*Fabric* und koppelt (kommissioniert) deine Matter-Geräte. Die gekoppelten Geräte stehen anschließend den **Matter Device**-Nodes zur Verfügung, die sie auf KNX-Gruppenadressen abbilden.
|
|
7
|
+
|
|
8
|
+
Der Controller kommuniziert mit den Geräten über das **IP-Netzwerk** (WLAN, Ethernet oder Thread über einen Border Router). Bluetooth-Kommissionierung wird nicht unterstützt: Das Gerät muss bereits im Netzwerk erreichbar sein.
|
|
9
|
+
|
|
10
|
+
## Ein Gerät koppeln
|
|
11
|
+
|
|
12
|
+
1. Zuerst diesen Konfigurations-Node **deployen** (der Controller muss laufen).
|
|
13
|
+
2. Den Node erneut öffnen und den **Kopplungscode** eingeben: entweder den 11-stelligen manuellen Code (z.B. `3497-011-2332`) oder den QR-Code-Inhalt (`MT:....`).
|
|
14
|
+
3. Auf **KOPPELN** klicken. Die Kommissionierung kann bis zu einer Minute dauern.
|
|
15
|
+
|
|
16
|
+
Wenn das Gerät fabrikneu ist und nur Bluetooth-Kommissionierung unterstützt, kopple es zuerst mit der Hersteller-App oder einem anderen Matter-Controller (Alexa, Google Home, Apple Home) und nutze dann dessen Funktion **"mit weiterem Hub teilen"**, um einen neuen Kopplungscode für KNX-Ultimate zu erzeugen. So tritt das Gerät mehreren Fabrics gleichzeitig bei.
|
|
17
|
+
|
|
18
|
+
## Speicherung
|
|
19
|
+
|
|
20
|
+
Die Fabric-Zugangsdaten und die gekoppelten Geräte werden im Ordner `knxultimatestorage/matter` im Node-RED-Benutzerverzeichnis gespeichert. Das Löschen dieses Ordners entfernt alle Kopplungen.
|
|
21
|
+
|
|
22
|
+
## Ein Gerät entfernen
|
|
23
|
+
|
|
24
|
+
Nutze den Papierkorb-Button in der Liste der gekoppelten Geräte. Der Controller versucht, das Gerät sauber zu dekommissionieren; ist es nicht erreichbar, wird es trotzdem aus der Fabric entfernt (danach kann ein Werksreset des Geräts nötig sein).
|
|
25
|
+
</script>
|