node-red-contrib-knx-ultimate 6.1.0 → 6.2.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/examples/KNX AI - Conversational Control with Confirmation.json +374 -0
  3. package/examples/KNX AI - Telegrambot Direct Chat.json +221 -0
  4. package/examples/Matter Controller - Semantic Flow Input.json +248 -0
  5. package/nodes/commonFunctions.js +0 -174
  6. package/nodes/knxUltimateAI.html +254 -90
  7. package/nodes/knxUltimateAI.js +1286 -61
  8. package/nodes/knxUltimateMatterBridge.html +144 -11
  9. package/nodes/knxUltimateMatterControllerDevice.html +80 -55
  10. package/nodes/locales/de/knxUltimateAI.html +38 -2
  11. package/nodes/locales/de/knxUltimateAI.json +16 -3
  12. package/nodes/locales/de/knxUltimateMatterBridge.html +1 -1
  13. package/nodes/locales/de/knxUltimateMatterBridge.json +14 -0
  14. package/nodes/locales/de/knxUltimateMatterControllerDevice.html +2 -2
  15. package/nodes/locales/de/knxUltimateMatterControllerDevice.json +2 -1
  16. package/nodes/locales/en/knxUltimateAI.html +38 -2
  17. package/nodes/locales/en/knxUltimateAI.json +16 -3
  18. package/nodes/locales/en/knxUltimateMatterBridge.html +1 -1
  19. package/nodes/locales/en/knxUltimateMatterBridge.json +14 -0
  20. package/nodes/locales/en/knxUltimateMatterControllerDevice.html +2 -2
  21. package/nodes/locales/en/knxUltimateMatterControllerDevice.json +2 -1
  22. package/nodes/locales/es/knxUltimateAI.html +38 -2
  23. package/nodes/locales/es/knxUltimateAI.json +16 -3
  24. package/nodes/locales/es/knxUltimateMatterBridge.html +1 -1
  25. package/nodes/locales/es/knxUltimateMatterBridge.json +14 -0
  26. package/nodes/locales/es/knxUltimateMatterControllerDevice.html +2 -2
  27. package/nodes/locales/es/knxUltimateMatterControllerDevice.json +2 -1
  28. package/nodes/locales/fr/knxUltimateAI.html +38 -2
  29. package/nodes/locales/fr/knxUltimateAI.json +16 -3
  30. package/nodes/locales/fr/knxUltimateMatterBridge.html +1 -1
  31. package/nodes/locales/fr/knxUltimateMatterBridge.json +14 -0
  32. package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +2 -2
  33. package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +2 -1
  34. package/nodes/locales/it/knxUltimateAI.html +38 -2
  35. package/nodes/locales/it/knxUltimateAI.json +16 -3
  36. package/nodes/locales/it/knxUltimateMatterBridge.html +1 -1
  37. package/nodes/locales/it/knxUltimateMatterBridge.json +14 -0
  38. package/nodes/locales/it/knxUltimateMatterControllerDevice.html +2 -2
  39. package/nodes/locales/it/knxUltimateMatterControllerDevice.json +2 -1
  40. package/nodes/locales/zh-CN/knxUltimateAI.html +38 -2
  41. package/nodes/locales/zh-CN/knxUltimateAI.json +16 -3
  42. package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +1 -1
  43. package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +14 -0
  44. package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +2 -2
  45. package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +2 -1
  46. package/nodes/utils/sysLogger.js +0 -109
  47. package/package.json +1 -2
  48. package/resources/KNXAIChatAdapterMappings.js +87 -0
  49. package/nodes/plugins/knxUltimateMonitor-sidebar-plugin.html +0 -922
@@ -0,0 +1,248 @@
1
+ [
2
+ {
3
+ "id": "matter_semantic_tab",
4
+ "type": "tab",
5
+ "label": "Matter Controller - Flow input",
6
+ "disabled": false,
7
+ "info": "Example of the semantic msg.payload = { function, value } contract for Control Matter from KNX (BETA).\n\nAfter importing:\n1. Open the example Matter Controller node.\n2. Select your existing Matter Controller configuration and a commissioned non-light endpoint.\n3. Enable Node Input/Output PINs.\n4. In the Flow input section shown below the PIN selector, use only the Inject nodes matching functions advertised by the device.\n\nThis example creates no matter-config and sends no command automatically."
8
+ },
9
+ {
10
+ "id": "matter_semantic_instructions",
11
+ "type": "comment",
12
+ "z": "matter_semantic_tab",
13
+ "name": "1. Open the Controller, select matter-config + endpoint, then enable the flow PINs",
14
+ "info": "The Inject nodes are alternative examples: one endpoint normally does not support every function shown. Use only functions listed in the Controller node's Flow input section.",
15
+ "x": 520,
16
+ "y": 60,
17
+ "wires": []
18
+ },
19
+ {
20
+ "id": "matter_semantic_supported",
21
+ "type": "comment",
22
+ "z": "matter_semantic_tab",
23
+ "name": "2. Trigger only Inject nodes supported by the selected endpoint",
24
+ "info": "No Inject is configured to run automatically. Reads omit value; writes include value.",
25
+ "x": 340,
26
+ "y": 110,
27
+ "wires": []
28
+ },
29
+ {
30
+ "id": "matter_semantic_on",
31
+ "type": "inject",
32
+ "z": "matter_semantic_tab",
33
+ "name": "On: {onoff, true}",
34
+ "props": [
35
+ {
36
+ "p": "payload",
37
+ "v": "{\"function\":\"onoff\",\"value\":true}",
38
+ "vt": "json"
39
+ }
40
+ ],
41
+ "repeat": "",
42
+ "crontab": "",
43
+ "once": false,
44
+ "onceDelay": 0.1,
45
+ "topic": "",
46
+ "x": 190,
47
+ "y": 180,
48
+ "wires": [
49
+ [
50
+ "matter_semantic_controller"
51
+ ]
52
+ ]
53
+ },
54
+ {
55
+ "id": "matter_semantic_off",
56
+ "type": "inject",
57
+ "z": "matter_semantic_tab",
58
+ "name": "Off: {onoff, false}",
59
+ "props": [
60
+ {
61
+ "p": "payload",
62
+ "v": "{\"function\":\"onoff\",\"value\":false}",
63
+ "vt": "json"
64
+ }
65
+ ],
66
+ "repeat": "",
67
+ "crontab": "",
68
+ "once": false,
69
+ "onceDelay": 0.1,
70
+ "topic": "",
71
+ "x": 190,
72
+ "y": 230,
73
+ "wires": [
74
+ [
75
+ "matter_semantic_controller"
76
+ ]
77
+ ]
78
+ },
79
+ {
80
+ "id": "matter_semantic_level",
81
+ "type": "inject",
82
+ "z": "matter_semantic_tab",
83
+ "name": "Level 50%",
84
+ "props": [
85
+ {
86
+ "p": "payload",
87
+ "v": "{\"function\":\"level\",\"value\":50}",
88
+ "vt": "json"
89
+ }
90
+ ],
91
+ "repeat": "",
92
+ "crontab": "",
93
+ "once": false,
94
+ "onceDelay": 0.1,
95
+ "topic": "",
96
+ "x": 180,
97
+ "y": 280,
98
+ "wires": [
99
+ [
100
+ "matter_semantic_controller"
101
+ ]
102
+ ]
103
+ },
104
+ {
105
+ "id": "matter_semantic_position",
106
+ "type": "inject",
107
+ "z": "matter_semantic_tab",
108
+ "name": "Cover position 35%",
109
+ "props": [
110
+ {
111
+ "p": "payload",
112
+ "v": "{\"function\":\"position\",\"value\":35}",
113
+ "vt": "json"
114
+ }
115
+ ],
116
+ "repeat": "",
117
+ "crontab": "",
118
+ "once": false,
119
+ "onceDelay": 0.1,
120
+ "topic": "",
121
+ "x": 190,
122
+ "y": 330,
123
+ "wires": [
124
+ [
125
+ "matter_semantic_controller"
126
+ ]
127
+ ]
128
+ },
129
+ {
130
+ "id": "matter_semantic_setpoint",
131
+ "type": "inject",
132
+ "z": "matter_semantic_tab",
133
+ "name": "Setpoint 21,5 °C",
134
+ "props": [
135
+ {
136
+ "p": "payload",
137
+ "v": "{\"function\":\"setpoint\",\"value\":21.5}",
138
+ "vt": "json"
139
+ }
140
+ ],
141
+ "repeat": "",
142
+ "crontab": "",
143
+ "once": false,
144
+ "onceDelay": 0.1,
145
+ "topic": "",
146
+ "x": 190,
147
+ "y": 380,
148
+ "wires": [
149
+ [
150
+ "matter_semantic_controller"
151
+ ]
152
+ ]
153
+ },
154
+ {
155
+ "id": "matter_semantic_temperature",
156
+ "type": "inject",
157
+ "z": "matter_semantic_tab",
158
+ "name": "Read temperature",
159
+ "props": [
160
+ {
161
+ "p": "payload",
162
+ "v": "{\"function\":\"temperature\"}",
163
+ "vt": "json"
164
+ }
165
+ ],
166
+ "repeat": "",
167
+ "crontab": "",
168
+ "once": false,
169
+ "onceDelay": 0.1,
170
+ "topic": "",
171
+ "x": 190,
172
+ "y": 430,
173
+ "wires": [
174
+ [
175
+ "matter_semantic_controller"
176
+ ]
177
+ ]
178
+ },
179
+ {
180
+ "id": "matter_semantic_identify",
181
+ "type": "inject",
182
+ "z": "matter_semantic_tab",
183
+ "name": "Identify",
184
+ "props": [
185
+ {
186
+ "p": "payload",
187
+ "v": "{\"function\":\"identify\"}",
188
+ "vt": "json"
189
+ }
190
+ ],
191
+ "repeat": "",
192
+ "crontab": "",
193
+ "once": false,
194
+ "onceDelay": 0.1,
195
+ "topic": "",
196
+ "x": 170,
197
+ "y": 480,
198
+ "wires": [
199
+ [
200
+ "matter_semantic_controller"
201
+ ]
202
+ ]
203
+ },
204
+ {
205
+ "id": "matter_semantic_controller",
206
+ "type": "knxUltimateMatterControllerDevice",
207
+ "z": "matter_semantic_tab",
208
+ "server": "",
209
+ "serverMatter": "",
210
+ "matterControllerMode": "single",
211
+ "universalService": "batteryMonitor",
212
+ "matterNodeId": "",
213
+ "matterEndpointId": 1,
214
+ "matterDeviceName": "",
215
+ "matterDeviceCapabilities": "",
216
+ "matterMappings": "[]",
217
+ "name": "SELECT A MATTER ENDPOINT",
218
+ "readStatusAtStartup": "no",
219
+ "enableNodePINS": "yes",
220
+ "updateLocalStateFromKNXWrite": true,
221
+ "inputs": 1,
222
+ "outputs": 1,
223
+ "x": 570,
224
+ "y": 330,
225
+ "wires": [
226
+ [
227
+ "matter_semantic_debug"
228
+ ]
229
+ ]
230
+ },
231
+ {
232
+ "id": "matter_semantic_debug",
233
+ "type": "debug",
234
+ "z": "matter_semantic_tab",
235
+ "name": "Complete Matter response",
236
+ "active": true,
237
+ "tosidebar": true,
238
+ "console": false,
239
+ "tostatus": true,
240
+ "complete": "true",
241
+ "targetType": "full",
242
+ "statusVal": "payload",
243
+ "statusType": "auto",
244
+ "x": 850,
245
+ "y": 330,
246
+ "wires": []
247
+ }
248
+ ]
@@ -7,7 +7,6 @@ const yaml = require('js-yaml')
7
7
  const dptlib = require('knxultimate').dptlib
8
8
  const customHTTP = require('./utils/http')
9
9
  const KNXClient = require('knxultimate').KNXClient
10
- const sysLogger = require('./utils/sysLogger')
11
10
  const { normalizeAuthFromAccessTokenQuery } = require('./utils/httpAdminAccessToken')
12
11
 
13
12
  // DATAPONT MANIPULATION HELPERS
@@ -62,52 +61,6 @@ module.exports = (RED) => {
62
61
  function commonFunctions () {
63
62
  const node = this
64
63
 
65
- const ensureBuffer = (rawValue) => {
66
- if (!rawValue) return null
67
- if (Buffer.isBuffer(rawValue)) return rawValue
68
- if (Array.isArray(rawValue)) return Buffer.from(rawValue)
69
- if (typeof rawValue === 'object' && Array.isArray(rawValue.data)) return Buffer.from(rawValue.data)
70
- if (rawValue.type === 'Buffer' && Array.isArray(rawValue.data)) return Buffer.from(rawValue.data)
71
- return null
72
- }
73
-
74
- const guessDptFromRawValue = (rawBuffer) => {
75
- if (!rawBuffer || !rawBuffer.length) return null
76
- if (rawBuffer.length === 1) {
77
- if (rawBuffer[0] === 0 || rawBuffer[0] === 1) return '1.001'
78
- return '5.001'
79
- }
80
- if (rawBuffer.length === 4) return '14.056'
81
- if (rawBuffer.length === 2) return '9.001'
82
- if (rawBuffer.length === 3) return '11.001'
83
- if (rawBuffer.length === 14) return '16.001'
84
-
85
- const dpts = Object.entries(dptlib).filter(onlyDptKeys).map(extractBaseNo).sort(sortBy('base')).reduce(toConcattedSubtypes, [])
86
- for (let index = 0; index < dpts.length; index++) {
87
- try {
88
- const resolved = dptlib.resolve(dpts[index].value)
89
- if (!resolved) continue
90
- const jsValue = dptlib.fromBuffer(rawBuffer, resolved)
91
- if (typeof jsValue !== 'undefined') return dpts[index].value
92
- } catch (error) { /* empty */ }
93
- }
94
- return null
95
- }
96
-
97
- const formatDisplayValue = (value) => {
98
- if (value === null || value === undefined) return ''
99
- if (value instanceof Date) return value.toISOString()
100
- if (typeof value === 'object') {
101
- try {
102
- return JSON.stringify(value)
103
- } catch (error) {
104
- return String(value)
105
- }
106
- }
107
- if (typeof value === 'boolean') return value ? 'true' : 'false'
108
- return String(value)
109
- }
110
-
111
64
  // // Gather infos about all interfaces on the lan and provides a static variable utils.aDiscoveredknxGateways
112
65
  // try {
113
66
  // require('./utils/utils').DiscoverKNXGateways()
@@ -624,133 +577,6 @@ module.exports = (RED) => {
624
577
  }
625
578
  })
626
579
 
627
- RED.httpAdmin.get('/knxUltimateMonitor', RED.auth.needsPermission('knxUltimate-config.read'), (req, res) => {
628
- try {
629
- const server = RED.nodes.getNode(req.query.serverId)
630
- if (!server) {
631
- res.json({ items: [], error: 'NO_SERVER' })
632
- return
633
- }
634
- const items = Array.isArray(server.exposedGAs) ? server.exposedGAs.map((entry) => {
635
- const rawBuffer = ensureBuffer(entry?.rawValue)
636
- const rawHex = rawBuffer ? rawBuffer.toString('hex') : ''
637
- let dpt = entry?.dpt
638
- let guessed = false
639
- if ((!dpt || dpt === '') && rawBuffer) {
640
- const inferred = guessDptFromRawValue(rawBuffer)
641
- if (inferred) {
642
- dpt = inferred
643
- guessed = true
644
- }
645
- }
646
- let value = null
647
- if (rawBuffer && dpt) {
648
- try {
649
- const resolved = dptlib.resolve(dpt)
650
- if (resolved) value = dptlib.fromBuffer(rawBuffer, resolved)
651
- } catch (error) { /* empty */ }
652
- }
653
- return {
654
- ga: entry?.ga || '',
655
- devicename: entry?.devicename || '',
656
- dpt: dpt || '',
657
- dptGuessed: guessed,
658
- rawHex,
659
- value,
660
- valueText: formatDisplayValue(value),
661
- updatedAt: entry?.updatedAt || null
662
- }
663
- }).sort((a, b) => a.ga.localeCompare(b.ga)) : []
664
- res.json({ items })
665
- } catch (error) {
666
- try { RED.log.error(`KNXUltimate: knxUltimateMonitor error: ${error.message}`) } catch (e) { }
667
- res.json({ items: [], error: error.message })
668
- }
669
- })
670
-
671
- RED.httpAdmin.post('/knxUltimateMonitorToggle', RED.auth.needsPermission('knxUltimate-config.write'), (req, res) => {
672
- try {
673
- const serverId = req.body?.serverId
674
- const ga = req.body?.ga
675
- const server = serverId ? RED.nodes.getNode(serverId) : null
676
- if (!server) {
677
- res.json({ status: 'error', error: 'NO_SERVER' })
678
- return
679
- }
680
- if (!ga) {
681
- res.json({ status: 'error', error: 'NO_GA' })
682
- return
683
- }
684
- const entry = Array.isArray(server.exposedGAs) ? server.exposedGAs.find((item) => item.ga === ga) : undefined
685
- if (!entry) {
686
- res.json({ status: 'error', error: 'GA_NOT_FOUND' })
687
- return
688
- }
689
- const rawBuffer = ensureBuffer(entry.rawValue)
690
- let dpt = entry.dpt
691
- if (!rawBuffer) {
692
- res.json({ status: 'error', error: 'NO_CURRENT_VALUE' })
693
- return
694
- }
695
- if (!dpt) {
696
- const inferred = guessDptFromRawValue(rawBuffer)
697
- if (inferred) dpt = inferred
698
- }
699
- let currentValue = null
700
- if (dpt) {
701
- try {
702
- const resolved = dptlib.resolve(dpt)
703
- if (resolved) currentValue = dptlib.fromBuffer(rawBuffer, resolved)
704
- } catch (error) { currentValue = null }
705
- }
706
- if (typeof currentValue !== 'boolean') {
707
- res.json({ status: 'error', error: 'NOT_BOOLEAN' })
708
- return
709
- }
710
- const nextValue = !currentValue
711
- const sendDpt = dpt || '1.001'
712
- try {
713
- server.sendKNXTelegramToKNXEngine({
714
- grpaddr: ga,
715
- payload: nextValue,
716
- dpt: sendDpt,
717
- outputtype: 'write',
718
- nodecallerid: 'knxUltimateMonitor'
719
- })
720
- } catch (error) {
721
- res.json({ status: 'error', error: error.message })
722
- return
723
- }
724
- res.json({ status: 'ok', value: nextValue })
725
- } catch (error) {
726
- try { RED.log.error(`KNXUltimate: knxUltimateMonitorToggle error: ${error.message}`) } catch (e) { }
727
- res.json({ status: 'error', error: error.message })
728
- }
729
- })
730
-
731
- RED.httpAdmin.get('/knxUltimateDebugLog', RED.auth.needsPermission('knxUltimate-config.read'), (req, res) => {
732
- try {
733
- const rawSince = req.query?.sinceSeq ?? req.query?.since ?? req.query?.after ?? null
734
- let sinceSeq = null
735
- if (rawSince !== null && rawSince !== undefined) {
736
- const parsed = Number(rawSince)
737
- if (!Number.isNaN(parsed) && Number.isFinite(parsed)) {
738
- sinceSeq = Math.floor(parsed)
739
- }
740
- }
741
- const snapshot = sinceSeq === null ? sysLogger.getDebugSnapshot() : sysLogger.getDebugSnapshot({ sinceSeq })
742
- res.json({
743
- entries: snapshot.entries,
744
- latestSeq: snapshot.latestSeq,
745
- total: snapshot.total,
746
- limit: snapshot.limit
747
- })
748
- } catch (error) {
749
- try { RED.log.error(`KNXUltimate: knxUltimateDebugLog error: ${error.message}`) } catch (e) { }
750
- res.json({ entries: [], error: error.message })
751
- }
752
- })
753
-
754
580
  RED.httpAdmin.post('/KNXUltimateLocateHueDevice', async (req, res) => {
755
581
  const respondError = (status, message) => {
756
582
  res.status(status).json({ error: message })