node-red-contrib-knx-ultimate 4.3.3 → 4.3.5
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 +13 -0
- package/nodes/knxUltimate-config.js +105 -90
- package/nodes/knxUltimate.html +101 -2
- package/nodes/knxUltimate.js +94 -46
- package/nodes/locales/de/knxUltimate.html +4 -3
- package/nodes/locales/en/knxUltimate.html +11 -9
- package/nodes/locales/es/knxUltimate.html +9 -7
- package/nodes/locales/fr/knxUltimate.html +9 -7
- package/nodes/locales/it/knxUltimate.html +4 -3
- package/nodes/locales/zh-CN/knxUltimate.html +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 4.3.5** - April 2026<br/>
|
|
10
|
+
|
|
11
|
+
- NEW: **KNX DEVICE** now supports explicit **`dpt = raw`** mode for incoming telegrams: decoding is skipped, `msg.payload` is `null`, and raw bytes remain available in `msg.knx.rawValue`.<br/>
|
|
12
|
+
- FIX: **KNX DEVICE RAW write** now accepts both **`msg.bitlength`** and legacy **`msg.bitlenght`**, with `bitlength` preferred and old flows kept backward compatible.<br/>
|
|
13
|
+
- CHANGE: **KNX DEVICE** in **`dpt = raw`** mode now accepts raw outgoing telegrams only via **`msg.writeraw`**, preventing accidental normal writes with an invalid datapoint.<br/>
|
|
14
|
+
- Docs/help/wiki: updated **KNX DEVICE** help HTML and **Device** wiki pages in all supported languages (**EN/IT/DE/FR/ES/zh-CN**) to document **raw mode** and the preferred **`bitlength`** property.<br/>
|
|
15
|
+
|
|
16
|
+
**Version 4.3.4** - April 2026<br/>
|
|
17
|
+
|
|
18
|
+
- UI: **KNX Function** editor areas now have colored backgrounds (light green for *input→bus*, light yellow for *bus→output*) for easier visual distinction.<br/>
|
|
19
|
+
- UI: **KNX Function** Monaco editor no longer shows red underlines for custom KNX functions (`getGAValue`, `setGAValue`, `toggle`, `self`).<br/>
|
|
20
|
+
- NEW: **KNX Function** : **`let val = await getGAValue(...)`** now automatically sends a `GroupValue_Read` to the KNX bus when the requested group address has no cached value yet, and waits up to 3 seconds for the device to respond before returning `null`. Use `await getGAValue(...)` in your KNX Function code to benefit from this behaviour.<br/>
|
|
21
|
+
|
|
9
22
|
**Version 4.3.3** - April 2026<br/>
|
|
10
23
|
|
|
11
24
|
- UI: in the **KNX Function helper**, the **Search GA** field is now always visible; if the ETS CSV is not imported, the field is disabled and shows `To enable the search, IMPORT THE ETS FILE`.<br/>
|
|
@@ -2066,6 +2066,8 @@ module.exports = (RED) => {
|
|
|
2066
2066
|
}
|
|
2067
2067
|
}
|
|
2068
2068
|
|
|
2069
|
+
const isRawMode = typeof _inputDpt === 'string' && _inputDpt.trim().toLowerCase() === 'raw'
|
|
2070
|
+
|
|
2069
2071
|
const errorMessage = {
|
|
2070
2072
|
topic: _outputtopic,
|
|
2071
2073
|
payload: 'UNKNOWN, PLEASE IMPORT THE ETS FILE!',
|
|
@@ -2088,83 +2090,18 @@ module.exports = (RED) => {
|
|
|
2088
2090
|
|
|
2089
2091
|
// Resolve DPT and convert value if available
|
|
2090
2092
|
if (_Rawvalue !== null) {
|
|
2091
|
-
|
|
2092
|
-
sInputDpt =
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
_srcGA +
|
|
2098
|
-
' Destination ' +
|
|
2099
|
-
_destGA +
|
|
2100
|
-
' Event ' +
|
|
2101
|
-
_event +
|
|
2102
|
-
" GA's Datapoint " +
|
|
2103
|
-
(_inputDpt === null
|
|
2104
|
-
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2105
|
-
: _inputDpt) +
|
|
2106
|
-
' Devicename ' +
|
|
2107
|
-
_devicename +
|
|
2108
|
-
' Topic ' +
|
|
2109
|
-
_outputtopic +
|
|
2110
|
-
' ' +
|
|
2111
|
-
error.message
|
|
2112
|
-
)
|
|
2113
|
-
errorMessage.payload = 'UNKNOWN: ERROR tryToFigureOutDataPointFromRawValue:' + error.message
|
|
2114
|
-
return errorMessage
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
try {
|
|
2118
|
-
var dpt = dptlib.resolve(sInputDpt)
|
|
2119
|
-
} catch (error) {
|
|
2120
|
-
node.sysLogger?.error(
|
|
2121
|
-
'buildInputMessage: Error returning from dptlib.resolve(sInputDpt). Device ' +
|
|
2122
|
-
_srcGA +
|
|
2123
|
-
' Destination ' +
|
|
2124
|
-
_destGA +
|
|
2125
|
-
' Event ' +
|
|
2126
|
-
_event +
|
|
2127
|
-
" GA's Datapoint " +
|
|
2128
|
-
(_inputDpt === null
|
|
2129
|
-
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2130
|
-
: _inputDpt) +
|
|
2131
|
-
' Devicename ' +
|
|
2132
|
-
_devicename +
|
|
2133
|
-
' Topic ' +
|
|
2134
|
-
_outputtopic +
|
|
2135
|
-
' ' +
|
|
2136
|
-
error.message
|
|
2137
|
-
)
|
|
2138
|
-
errorMessage.payload = 'UNKNOWN: ERROR dptlib.resolve:' + error.messages
|
|
2139
|
-
return errorMessage
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
if (dpt !== null && _Rawvalue !== null) {
|
|
2093
|
+
if (isRawMode) {
|
|
2094
|
+
sInputDpt = 'raw'
|
|
2095
|
+
sPayloadmeasureunit = ''
|
|
2096
|
+
sDptdesc = 'Raw value'
|
|
2097
|
+
sPayloadsubtypevalue = ''
|
|
2098
|
+
} else {
|
|
2143
2099
|
try {
|
|
2144
|
-
|
|
2145
|
-
if (jsValue === null) {
|
|
2146
|
-
node.sysLogger?.warn(
|
|
2147
|
-
'buildInputMessage: received a wrong datagram form KNX BUS, from device ' +
|
|
2148
|
-
_srcGA +
|
|
2149
|
-
' Destination ' +
|
|
2150
|
-
_destGA +
|
|
2151
|
-
' Event ' +
|
|
2152
|
-
_event +
|
|
2153
|
-
" GA's Datapoint " +
|
|
2154
|
-
(_inputDpt === null
|
|
2155
|
-
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2156
|
-
: _inputDpt) +
|
|
2157
|
-
' Devicename ' +
|
|
2158
|
-
_devicename +
|
|
2159
|
-
' Topic ' +
|
|
2160
|
-
_outputtopic +
|
|
2161
|
-
' NodeID ' +
|
|
2162
|
-
_oNode.id || ''
|
|
2163
|
-
)
|
|
2164
|
-
}
|
|
2100
|
+
sInputDpt = _inputDpt === null ? tryToFigureOutDataPointFromRawValue(_Rawvalue) : _inputDpt
|
|
2165
2101
|
} catch (error) {
|
|
2102
|
+
// Here comes if no datapoint has beeen found
|
|
2166
2103
|
node.sysLogger?.error(
|
|
2167
|
-
'buildInputMessage: Error returning from
|
|
2104
|
+
'buildInputMessage: Error returning from tryToFigureOutDataPointFromRawValue. Device ' +
|
|
2168
2105
|
_srcGA +
|
|
2169
2106
|
' Destination ' +
|
|
2170
2107
|
_destGA +
|
|
@@ -2179,34 +2116,112 @@ module.exports = (RED) => {
|
|
|
2179
2116
|
' Topic ' +
|
|
2180
2117
|
_outputtopic +
|
|
2181
2118
|
' ' +
|
|
2182
|
-
error.message
|
|
2183
|
-
' NodeID ' +
|
|
2184
|
-
_oNode.id || ''
|
|
2119
|
+
error.message
|
|
2185
2120
|
)
|
|
2186
|
-
errorMessage.payload = 'UNKNOWN: ERROR
|
|
2121
|
+
errorMessage.payload = 'UNKNOWN: ERROR tryToFigureOutDataPointFromRawValue:' + error.message
|
|
2187
2122
|
return errorMessage
|
|
2188
2123
|
}
|
|
2189
|
-
}
|
|
2190
2124
|
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2125
|
+
try {
|
|
2126
|
+
var dpt = dptlib.resolve(sInputDpt)
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
node.sysLogger?.error(
|
|
2129
|
+
'buildInputMessage: Error returning from dptlib.resolve(sInputDpt). Device ' +
|
|
2130
|
+
_srcGA +
|
|
2131
|
+
' Destination ' +
|
|
2132
|
+
_destGA +
|
|
2133
|
+
' Event ' +
|
|
2134
|
+
_event +
|
|
2135
|
+
" GA's Datapoint " +
|
|
2136
|
+
(_inputDpt === null
|
|
2137
|
+
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2138
|
+
: _inputDpt) +
|
|
2139
|
+
' Devicename ' +
|
|
2140
|
+
_devicename +
|
|
2141
|
+
' Topic ' +
|
|
2142
|
+
_outputtopic +
|
|
2143
|
+
' ' +
|
|
2144
|
+
error.message
|
|
2145
|
+
)
|
|
2146
|
+
errorMessage.payload = 'UNKNOWN: ERROR dptlib.resolve:' + error.messages
|
|
2147
|
+
return errorMessage
|
|
2148
|
+
}
|
|
2195
2149
|
|
|
2196
|
-
|
|
2197
|
-
sPayloadmeasureunit = dpt.subtype.unit !== undefined ? dpt.subtype.unit : 'unknown'
|
|
2198
|
-
sDptdesc = dpt.subtype.desc !== undefined ? dpt.subtype.desc.charAt(0).toUpperCase() + dpt.subtype.desc.slice(1) : 'unknown'
|
|
2199
|
-
if (dpt.subtype.enc !== undefined) {
|
|
2150
|
+
if (dpt !== null && _Rawvalue !== null) {
|
|
2200
2151
|
try {
|
|
2201
|
-
|
|
2202
|
-
if (jsValue
|
|
2152
|
+
jsValue = dptlib.fromBuffer(_Rawvalue, dpt)
|
|
2153
|
+
if (jsValue === null) {
|
|
2154
|
+
node.sysLogger?.warn(
|
|
2155
|
+
'buildInputMessage: received a wrong datagram form KNX BUS, from device ' +
|
|
2156
|
+
_srcGA +
|
|
2157
|
+
' Destination ' +
|
|
2158
|
+
_destGA +
|
|
2159
|
+
' Event ' +
|
|
2160
|
+
_event +
|
|
2161
|
+
" GA's Datapoint " +
|
|
2162
|
+
(_inputDpt === null
|
|
2163
|
+
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2164
|
+
: _inputDpt) +
|
|
2165
|
+
' Devicename ' +
|
|
2166
|
+
_devicename +
|
|
2167
|
+
' Topic ' +
|
|
2168
|
+
_outputtopic +
|
|
2169
|
+
' NodeID ' +
|
|
2170
|
+
_oNode.id || ''
|
|
2171
|
+
)
|
|
2172
|
+
}
|
|
2203
2173
|
} catch (error) {
|
|
2204
|
-
|
|
2174
|
+
node.sysLogger?.error(
|
|
2175
|
+
'buildInputMessage: Error returning from DPT decoding. Device ' +
|
|
2176
|
+
_srcGA +
|
|
2177
|
+
' Destination ' +
|
|
2178
|
+
_destGA +
|
|
2179
|
+
' Event ' +
|
|
2180
|
+
_event +
|
|
2181
|
+
" GA's Datapoint " +
|
|
2182
|
+
(_inputDpt === null
|
|
2183
|
+
? "THE ETS FILE HAS NOT BEEN IMPORTED, SO I'M TRYING TO FIGURE OUT WHAT DATAPOINT BELONGS THIS GROUP ADDRESS. DON'T BLAME ME IF I'M WRONG, INSTEAD, IMPORT THE ETS FILE!"
|
|
2184
|
+
: _inputDpt) +
|
|
2185
|
+
' Devicename ' +
|
|
2186
|
+
_devicename +
|
|
2187
|
+
' Topic ' +
|
|
2188
|
+
_outputtopic +
|
|
2189
|
+
' ' +
|
|
2190
|
+
error.message +
|
|
2191
|
+
' NodeID ' +
|
|
2192
|
+
_oNode.id || ''
|
|
2193
|
+
)
|
|
2194
|
+
errorMessage.payload = 'UNKNOWN: ERROR dptlib.fromBuffer:' + error.stack
|
|
2195
|
+
return errorMessage
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// 19/01/2023 FORMATTING THE OUTPUT PAYLOAD (ROUND, ETC) BASED ON THE NODE CONFIG
|
|
2200
|
+
//* ********************************************************
|
|
2201
|
+
jsValue = payloadRounder.Manipulate(_oNode, jsValue)
|
|
2202
|
+
//* ********************************************************
|
|
2203
|
+
|
|
2204
|
+
if (dpt.subtype !== undefined) {
|
|
2205
|
+
sPayloadmeasureunit = dpt.subtype.unit !== undefined ? dpt.subtype.unit : 'unknown'
|
|
2206
|
+
sDptdesc = dpt.subtype.desc !== undefined ? dpt.subtype.desc.charAt(0).toUpperCase() + dpt.subtype.desc.slice(1) : 'unknown'
|
|
2207
|
+
if (dpt.subtype.enc !== undefined) {
|
|
2208
|
+
try {
|
|
2209
|
+
if (!jsValue) sPayloadsubtypevalue = dpt.subtype.enc[0]
|
|
2210
|
+
if (jsValue) sPayloadsubtypevalue = dpt.subtype.enc[1]
|
|
2211
|
+
} catch (error) {
|
|
2212
|
+
// Don't care
|
|
2213
|
+
}
|
|
2205
2214
|
}
|
|
2206
2215
|
}
|
|
2207
2216
|
}
|
|
2208
2217
|
} else {
|
|
2209
2218
|
// Don't care, it's a READ REQUEST
|
|
2219
|
+
if (isRawMode) {
|
|
2220
|
+
sInputDpt = 'raw'
|
|
2221
|
+
sPayloadmeasureunit = ''
|
|
2222
|
+
sDptdesc = 'Raw value'
|
|
2223
|
+
sPayloadsubtypevalue = ''
|
|
2224
|
+
}
|
|
2210
2225
|
}
|
|
2211
2226
|
|
|
2212
2227
|
try {
|
package/nodes/knxUltimate.html
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
<!-- <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/jquery.searchableSelect.js"></script> -->
|
|
2
2
|
|
|
3
|
+
<style>
|
|
4
|
+
/* Monaco editor - first area (input to bus): light green */
|
|
5
|
+
#sendMsgToKNXCode-editor .monaco-editor,
|
|
6
|
+
#sendMsgToKNXCode-editor .monaco-editor-background,
|
|
7
|
+
#sendMsgToKNXCode-editor .monaco-editor .margin,
|
|
8
|
+
#sendMsgToKNXCode-editor .monaco-editor .overflow-guard,
|
|
9
|
+
#sendMsgToKNXCode-editor .monaco-editor .lines-content,
|
|
10
|
+
#sendMsgToKNXCode-editor .monaco-editor .editor-scrollable {
|
|
11
|
+
background-color: #e8f5e9 !important;
|
|
12
|
+
}
|
|
13
|
+
/* Monaco editor - second area (bus to output): light yellow */
|
|
14
|
+
#receiveMsgFromKNXCode-editor .monaco-editor,
|
|
15
|
+
#receiveMsgFromKNXCode-editor .monaco-editor-background,
|
|
16
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .margin,
|
|
17
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .overflow-guard,
|
|
18
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .lines-content,
|
|
19
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .editor-scrollable {
|
|
20
|
+
background-color: #fffde7 !important;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
3
23
|
|
|
4
24
|
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
|
|
5
25
|
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/KNXSendSnippets.js"></script>
|
|
@@ -225,6 +245,15 @@
|
|
|
225
245
|
}
|
|
226
246
|
];
|
|
227
247
|
const globalScope = typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : {});
|
|
248
|
+
const rawDptOption = { value: 'raw', text: 'raw Raw telegram (no decode)' }
|
|
249
|
+
|
|
250
|
+
function appendRawDptOption($select) {
|
|
251
|
+
if (!$select || !$select.length) return
|
|
252
|
+
if ($select.find("option[value='raw']").length > 0) return
|
|
253
|
+
$select.prepend($("<option></option>")
|
|
254
|
+
.attr("value", rawDptOption.value)
|
|
255
|
+
.text(rawDptOption.text))
|
|
256
|
+
}
|
|
228
257
|
|
|
229
258
|
function knxUltimateDptsGetHelp(_dpt, _forceClose) {
|
|
230
259
|
const detailsContainer = $("#dptDetailsContainer")
|
|
@@ -233,6 +262,39 @@
|
|
|
233
262
|
}
|
|
234
263
|
const serverId = $("#node-input-server").val()
|
|
235
264
|
if (serverId === "_ADD_" || serverId === '' || _dpt === null || _dpt === '') return
|
|
265
|
+
if (String(_dpt).trim().toLowerCase() === 'raw') {
|
|
266
|
+
const helplinkContainer = $("#sampleCodeEditor")
|
|
267
|
+
try {
|
|
268
|
+
if (node.sampleEditor) {
|
|
269
|
+
node.sampleEditor.destroy()
|
|
270
|
+
delete node.sampleEditor
|
|
271
|
+
}
|
|
272
|
+
} catch (error) { }
|
|
273
|
+
$("#example-editor").empty()
|
|
274
|
+
helplinkContainer.empty()
|
|
275
|
+
node.sampleEditor = RED.editor.createEditor({
|
|
276
|
+
id: 'example-editor',
|
|
277
|
+
mode: 'ace/mode/javascript',
|
|
278
|
+
value: `// KNX-Ultimate set as RAW NODE
|
|
279
|
+
// Incoming telegrams skip datapoint decoding.
|
|
280
|
+
// msg.payload will be null and the raw telegram bytes are available in msg.knx.rawValue.
|
|
281
|
+
// For outgoing telegrams, use msg.writeraw = Buffer.from("0730", "hex")
|
|
282
|
+
// and optionally msg.bitlength for 1-bit / 2-bit / 4-bit datapoints.
|
|
283
|
+
return msg;`
|
|
284
|
+
})
|
|
285
|
+
try {
|
|
286
|
+
node.sampleEditor.setReadOnly(true)
|
|
287
|
+
node.sampleEditor.setShowPrintMargin(false)
|
|
288
|
+
} catch (error) { }
|
|
289
|
+
$("<div>", {
|
|
290
|
+
class: "dpt-details-link"
|
|
291
|
+
})
|
|
292
|
+
.append($("<span>").text("RAW mode keeps the telegram undecoded and exposes the bytes in "))
|
|
293
|
+
.append($("<code>").text("msg.knx.rawValue"))
|
|
294
|
+
.append($("<span>").text("."))
|
|
295
|
+
.appendTo(helplinkContainer)
|
|
296
|
+
return
|
|
297
|
+
}
|
|
236
298
|
$.getJSON("knxUltimateDptsGetHelp?dpt=" + _dpt + "&serverId=" + serverId + "&" + { _: new Date().getTime() }, (data) => {
|
|
237
299
|
const helplinkContainer = $("#sampleCodeEditor")
|
|
238
300
|
try {
|
|
@@ -336,15 +398,29 @@
|
|
|
336
398
|
}
|
|
337
399
|
if (typeof monaco !== 'undefined' && !globalScope.knxFunctionMonacoCompletionProvider) {
|
|
338
400
|
try {
|
|
339
|
-
const
|
|
401
|
+
const functionSuggestions = knxFunctionHelperItems.map(item => ({
|
|
340
402
|
label: item.label,
|
|
341
403
|
kind: monaco.languages.CompletionItemKind.Function,
|
|
342
404
|
insertText: item.snippet,
|
|
343
405
|
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
344
406
|
documentation: item.doc
|
|
345
407
|
}));
|
|
408
|
+
|
|
346
409
|
globalScope.knxFunctionMonacoCompletionProvider = monaco.languages.registerCompletionItemProvider('javascript', {
|
|
347
|
-
provideCompletionItems: () => ({ suggestions })
|
|
410
|
+
provideCompletionItems: () => ({ suggestions: functionSuggestions })
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
monaco.languages.typescript.javascriptDefaults.addExtraLib([
|
|
414
|
+
'/** Read the cached value of a KNX group address. */',
|
|
415
|
+
'declare function getGAValue(address: string, dpt?: string): any;',
|
|
416
|
+
'/** Send a value to a KNX group address. */',
|
|
417
|
+
'declare function setGAValue(address: string, value: any, dpt?: string): void;',
|
|
418
|
+
'/** Toggle (invert) this node\'s current value on the KNX bus. */',
|
|
419
|
+
'declare function toggle(): void;',
|
|
420
|
+
].join('\n'), 'knx-ultimate-globals.d.ts');
|
|
421
|
+
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
|
|
422
|
+
noSemanticValidation: true,
|
|
423
|
+
noSyntaxValidation: false
|
|
348
424
|
});
|
|
349
425
|
} catch (error) { }
|
|
350
426
|
}
|
|
@@ -499,6 +575,27 @@
|
|
|
499
575
|
value: node.receiveMsgFromKNXCode
|
|
500
576
|
});
|
|
501
577
|
applyEditorOptions(node.receiveMsgFromKNXCodeEditor);
|
|
578
|
+
if (typeof monaco !== 'undefined') {
|
|
579
|
+
try {
|
|
580
|
+
if (!globalScope._knxEditorThemesDefined) {
|
|
581
|
+
monaco.editor.defineTheme('knx-send-theme', {
|
|
582
|
+
base: 'vs', inherit: true, rules: [],
|
|
583
|
+
colors: { 'editor.background': '#e8f5e9', 'editorGutter.background': '#e8f5e9' }
|
|
584
|
+
});
|
|
585
|
+
monaco.editor.defineTheme('knx-receive-theme', {
|
|
586
|
+
base: 'vs', inherit: true, rules: [],
|
|
587
|
+
colors: { 'editor.background': '#fffde7', 'editorGutter.background': '#fffde7' }
|
|
588
|
+
});
|
|
589
|
+
globalScope._knxEditorThemesDefined = true;
|
|
590
|
+
}
|
|
591
|
+
if (typeof node.sendMsgToKNXCodeEditor.updateOptions === 'function') {
|
|
592
|
+
node.sendMsgToKNXCodeEditor.updateOptions({ theme: 'knx-send-theme' });
|
|
593
|
+
}
|
|
594
|
+
if (typeof node.receiveMsgFromKNXCodeEditor.updateOptions === 'function') {
|
|
595
|
+
node.receiveMsgFromKNXCodeEditor.updateOptions({ theme: 'knx-receive-theme' });
|
|
596
|
+
}
|
|
597
|
+
} catch (e) { }
|
|
598
|
+
}
|
|
502
599
|
node.activeCodeEditor = null;
|
|
503
600
|
attachFocusHandlers(node.sendMsgToKNXCodeEditor);
|
|
504
601
|
attachFocusHandlers(node.receiveMsgFromKNXCodeEditor);
|
|
@@ -666,11 +763,13 @@
|
|
|
666
763
|
refreshSecureGAs();
|
|
667
764
|
$.getJSON("knxUltimateDpts?serverId=" + $("#node-input-server").val() + "&_=" + new Date().getTime(), (data) => {
|
|
668
765
|
$("#node-input-dpt").empty();
|
|
766
|
+
appendRawDptOption($("#node-input-dpt"));
|
|
669
767
|
data.forEach(dpt => {
|
|
670
768
|
$("#node-input-dpt").append($("<option></option>")
|
|
671
769
|
.attr("value", dpt.value)
|
|
672
770
|
.text(dpt.text))
|
|
673
771
|
});
|
|
772
|
+
if (node.dpt === undefined || node.dpt === '') node.dpt = '1.001'
|
|
674
773
|
$("#node-input-dpt").val(node.dpt);
|
|
675
774
|
// Load help sample
|
|
676
775
|
knxUltimateDptsGetHelp(node.dpt, true);
|
package/nodes/knxUltimate.js
CHANGED
|
@@ -136,6 +136,7 @@ module.exports = function (RED) {
|
|
|
136
136
|
}
|
|
137
137
|
const mode = (req.body && req.body.mode ? String(req.body.mode) : 'read').toLowerCase()
|
|
138
138
|
const grpaddr = targetNode.topic
|
|
139
|
+
const isRawMode = typeof targetNode.dpt === 'string' && targetNode.dpt.trim().toLowerCase() === 'raw'
|
|
139
140
|
if (mode !== 'read') {
|
|
140
141
|
if (!grpaddr || String(grpaddr).trim() === '') {
|
|
141
142
|
res.status(400).json({ error: 'Group address not set' })
|
|
@@ -145,6 +146,10 @@ module.exports = function (RED) {
|
|
|
145
146
|
res.status(400).json({ error: 'Datapoint not defined for this node' })
|
|
146
147
|
return
|
|
147
148
|
}
|
|
149
|
+
if (isRawMode) {
|
|
150
|
+
res.status(400).json({ error: 'Manual write is not available when the node datapoint is set to raw' })
|
|
151
|
+
return
|
|
152
|
+
}
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
if (mode === 'read') {
|
|
@@ -299,7 +304,7 @@ module.exports = function (RED) {
|
|
|
299
304
|
const payloadRounder = require('./utils/payloadManipulation')
|
|
300
305
|
const dptlib = require('knxultimate').dptlib
|
|
301
306
|
|
|
302
|
-
function knxUltimate
|
|
307
|
+
function knxUltimate(config) {
|
|
303
308
|
RED.nodes.createNode(this, config)
|
|
304
309
|
const node = this
|
|
305
310
|
node.serverKNX = RED.nodes.getNode(config.server) || undefined
|
|
@@ -365,7 +370,7 @@ module.exports = function (RED) {
|
|
|
365
370
|
// Get the Group Address from various sources
|
|
366
371
|
if (config.setTopicType === undefined || config.setTopicType === 'str') {
|
|
367
372
|
node.topic = config.topic
|
|
368
|
-
node.dpt = config.dpt || '1.001'
|
|
373
|
+
node.dpt = (config.dpt === undefined || config.dpt === '') ? '1.001' : config.dpt
|
|
369
374
|
} else if (config.setTopicType === 'flow') {
|
|
370
375
|
try {
|
|
371
376
|
node.topic = node.context().flow.get(config.topic)
|
|
@@ -467,6 +472,7 @@ module.exports = function (RED) {
|
|
|
467
472
|
if (node.listenallga === true) return
|
|
468
473
|
if (typeof node.topic !== 'string' || node.topic === '') return
|
|
469
474
|
if (typeof node.dpt !== 'string' || node.dpt === '' || node.dpt === 'auto') return
|
|
475
|
+
if (node.dpt.trim().toLowerCase() === 'raw') return
|
|
470
476
|
|
|
471
477
|
node.serverKNX.sendKNXTelegramToKNXEngine({
|
|
472
478
|
grpaddr: node.topic,
|
|
@@ -500,36 +506,61 @@ module.exports = function (RED) {
|
|
|
500
506
|
}
|
|
501
507
|
|
|
502
508
|
// Used in the KNX Function TAB
|
|
503
|
-
const getGAValue = function getGAValue
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
509
|
+
const getGAValue = async function getGAValue(_ga = undefined, _dpt = undefined) {
|
|
510
|
+
if (_ga === undefined) return null
|
|
511
|
+
// Strip devicename if present (e.g. "1/1/1 Light name" → "1/1/1")
|
|
512
|
+
const blankSpacePosition = _ga.indexOf(' ')
|
|
513
|
+
if (blankSpacePosition > -1) _ga = _ga.substring(0, blankSpacePosition)
|
|
514
|
+
|
|
515
|
+
const tryDecode = (rawValue, dpt) => {
|
|
516
|
+
try {
|
|
517
|
+
if (rawValue === null || rawValue === undefined) return undefined
|
|
518
|
+
return dptlib.fromBuffer(rawValue, dptlib.resolve(dpt))
|
|
519
|
+
} catch (_e) { return undefined }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const sendRead = () => {
|
|
523
|
+
try {
|
|
524
|
+
node.serverKNX.sendKNXTelegramToKNXEngine({
|
|
525
|
+
grpaddr: _ga, payload: '', dpt: '', outputtype: 'read', nodecallerid: node.id
|
|
526
|
+
})
|
|
527
|
+
RED.log.warn('getGAValue: sent groupvalue_read for ' + _ga)
|
|
528
|
+
} catch (e) {
|
|
529
|
+
RED.log.error('getGAValue: failed to send read for ' + _ga + ': ' + e.message)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Check cache first
|
|
534
|
+
let found = node.serverKNX.exposedGAs.find(a => a.ga === _ga)
|
|
535
|
+
if (found !== undefined) {
|
|
536
|
+
const dptFinal = _dpt || found.dpt
|
|
537
|
+
if (!dptFinal) {
|
|
538
|
+
RED.log.error('getGAValue: no DPT for ' + _ga + '. Provide it as second parameter.')
|
|
539
|
+
return null
|
|
540
|
+
}
|
|
541
|
+
const val = tryDecode(found.rawValue, dptFinal)
|
|
542
|
+
if (val !== undefined) return val
|
|
543
|
+
// rawValue present but decode failed (bad format) — fall through to read
|
|
544
|
+
RED.log.warn('getGAValue: cached rawValue for ' + _ga + ' could not be decoded, sending read request')
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Not cached or decode failed: send read and poll up to 3 seconds
|
|
548
|
+
sendRead()
|
|
549
|
+
for (let i = 0; i < 30; i++) {
|
|
550
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
551
|
+
found = node.serverKNX.exposedGAs.find(a => a.ga === _ga)
|
|
512
552
|
if (found !== undefined) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return
|
|
518
|
-
};
|
|
519
|
-
return dptlib.fromBuffer(found.rawValue, dptlib.resolve(_dpt || found.dpt))
|
|
520
|
-
} else {
|
|
521
|
-
const errM = 'getGaValue: node ID:' + node.id + ' ' + 'Group Address not yet read, try later.'
|
|
522
|
-
RED.log.error(errM)
|
|
523
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(errM)
|
|
553
|
+
const dptFinal = _dpt || found.dpt
|
|
554
|
+
if (!dptFinal) return null
|
|
555
|
+
const val = tryDecode(found.rawValue, dptFinal)
|
|
556
|
+
if (val !== undefined) return val
|
|
524
557
|
}
|
|
525
|
-
} catch (error) {
|
|
526
|
-
const errM = 'getGaValue: node ID:' + node.id + ' ' + error.stack
|
|
527
|
-
RED.log.error(errM)
|
|
528
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(errM)
|
|
529
558
|
}
|
|
559
|
+
RED.log.warn('getGAValue: no response from KNX bus for ' + _ga + ' within 3 seconds')
|
|
560
|
+
return null
|
|
530
561
|
}
|
|
531
562
|
// Used in the KNX Function TAB
|
|
532
|
-
const setGAValue = function setGAValue
|
|
563
|
+
const setGAValue = function setGAValue(_ga = undefined, _value = undefined, _dpt = undefined) {
|
|
533
564
|
try {
|
|
534
565
|
if (_ga === undefined) return
|
|
535
566
|
// The GA can have the devicename as well, separated by a blank space (1/1/0 light table ovest),
|
|
@@ -556,7 +587,7 @@ module.exports = function (RED) {
|
|
|
556
587
|
}
|
|
557
588
|
}
|
|
558
589
|
// Used in the KNX Function TAB
|
|
559
|
-
const self = function self
|
|
590
|
+
const self = function self(_value) {
|
|
560
591
|
try {
|
|
561
592
|
node.serverKNX.sendKNXTelegramToKNXEngine({
|
|
562
593
|
grpaddr: node.topic, payload: _value, dpt: node.dpt, outputtype: 'write', nodecallerid: node.id
|
|
@@ -568,7 +599,7 @@ module.exports = function (RED) {
|
|
|
568
599
|
}
|
|
569
600
|
}
|
|
570
601
|
// Used in the KNX Function TAB
|
|
571
|
-
const toggle = function toggle
|
|
602
|
+
const toggle = function toggle() {
|
|
572
603
|
if (node.currentPayload === true || node.currentPayload === false) {
|
|
573
604
|
try {
|
|
574
605
|
node.serverKNX.sendKNXTelegramToKNXEngine({
|
|
@@ -583,7 +614,7 @@ module.exports = function (RED) {
|
|
|
583
614
|
}
|
|
584
615
|
|
|
585
616
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
586
|
-
node.handleSend = (msg) => {
|
|
617
|
+
node.handleSend = async (msg) => {
|
|
587
618
|
// 27/03/2020 can i merge the last input msg arrived, with the output?
|
|
588
619
|
try {
|
|
589
620
|
if (node.passthrough === 'yes') {
|
|
@@ -613,8 +644,8 @@ module.exports = function (RED) {
|
|
|
613
644
|
// -+++++++++++++++++++++++++++++++++++++++++++
|
|
614
645
|
if (node.receiveMsgFromKNXCode !== undefined) {
|
|
615
646
|
try {
|
|
616
|
-
const receiveMsgFromKNXCode = new
|
|
617
|
-
msg = receiveMsgFromKNXCode(msg, getGAValue, node, RED, self, toggle, setGAValue)
|
|
647
|
+
const receiveMsgFromKNXCode = (new (Object.getPrototypeOf(async function () { }).constructor)('msg', 'getGAValue', 'node', 'RED', 'self', 'toggle', 'setGAValue', node.receiveMsgFromKNXCode))
|
|
648
|
+
msg = await receiveMsgFromKNXCode(msg, getGAValue, node, RED, self, toggle, setGAValue)
|
|
618
649
|
} catch (error) {
|
|
619
650
|
RED.log.error('knxUltimate: receiveMsgFromKNXCode: node ID:' + node.id + ' ' + error.message)
|
|
620
651
|
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`receiveMsgFromKNXCode: node id ${node.id} ` || ' ' + error.stack)
|
|
@@ -631,7 +662,7 @@ module.exports = function (RED) {
|
|
|
631
662
|
if (msg !== undefined) node.send(msg)
|
|
632
663
|
}
|
|
633
664
|
|
|
634
|
-
node.on('input', (msg) => {
|
|
665
|
+
node.on('input', async (msg) => {
|
|
635
666
|
if (typeof msg === 'undefined') return
|
|
636
667
|
if (!node.serverKNX) return // 29/08/2019 Server not instantiate
|
|
637
668
|
|
|
@@ -679,8 +710,8 @@ module.exports = function (RED) {
|
|
|
679
710
|
// -+++++++++++++++++++++++++++++++++++++++++++
|
|
680
711
|
if (node.sendMsgToKNXCode !== undefined) {
|
|
681
712
|
try {
|
|
682
|
-
const sendMsgToKNXCode = new
|
|
683
|
-
msg = sendMsgToKNXCode(msg, getGAValue, node, RED, self, toggle, setGAValue)
|
|
713
|
+
const sendMsgToKNXCode = (new (Object.getPrototypeOf(async function () { }).constructor)('msg', 'getGAValue', 'node', 'RED', 'self', 'toggle', 'setGAValue', node.sendMsgToKNXCode))
|
|
714
|
+
msg = await sendMsgToKNXCode(msg, getGAValue, node, RED, self, toggle, setGAValue)
|
|
684
715
|
if (msg === undefined) return
|
|
685
716
|
} catch (error) {
|
|
686
717
|
RED.log.error('knxUltimate: sendMsgToKNXCode: node ID:' + node.id + ' ' + error.message)
|
|
@@ -886,8 +917,11 @@ module.exports = function (RED) {
|
|
|
886
917
|
// connection.writeRaw('1/0/0', Buffer.from('0730', 'hex'))
|
|
887
918
|
if (msg.hasOwnProperty('writeraw') && msg.hasOwnProperty('writeraw') !== null) {
|
|
888
919
|
try {
|
|
889
|
-
|
|
890
|
-
|
|
920
|
+
const rawBitlength = msg.hasOwnProperty('bitlength') && msg.bitlength !== null
|
|
921
|
+
? msg.bitlength
|
|
922
|
+
: (msg.hasOwnProperty('bitlenght') && msg.bitlenght !== null ? msg.bitlenght : null)
|
|
923
|
+
if (rawBitlength !== null) {
|
|
924
|
+
node.serverKNX.knxConnection.writeRaw(grpaddr, msg.writeraw, rawBitlength)
|
|
891
925
|
} else {
|
|
892
926
|
node.serverKNX.knxConnection.writeRaw(grpaddr, msg.writeraw)
|
|
893
927
|
}
|
|
@@ -902,6 +936,20 @@ module.exports = function (RED) {
|
|
|
902
936
|
return
|
|
903
937
|
}
|
|
904
938
|
|
|
939
|
+
if (typeof dpt === 'string' && dpt.trim().toLowerCase() === 'raw') {
|
|
940
|
+
node.setNodeStatus({
|
|
941
|
+
fill: 'red',
|
|
942
|
+
shape: 'dot',
|
|
943
|
+
text: 'RAW mode accepts only msg.writeraw for outgoing telegrams',
|
|
944
|
+
payload: '',
|
|
945
|
+
GA: grpaddr,
|
|
946
|
+
dpt: '',
|
|
947
|
+
devicename: ''
|
|
948
|
+
})
|
|
949
|
+
node.sysLogger?.error(`node id: ${node.id} RAW mode accepts only msg.writeraw for outgoing telegrams (${grpaddr})`)
|
|
950
|
+
return
|
|
951
|
+
}
|
|
952
|
+
|
|
905
953
|
if (outputtype == 'response') {
|
|
906
954
|
try {
|
|
907
955
|
node.currentPayload = msg.payload// 31/12/2019 Set the current value (because, if the node is a virtual device, then it'll never fire "GroupValue_Write" in the server node, causing the currentPayload to never update)
|
|
@@ -960,9 +1008,9 @@ module.exports = function (RED) {
|
|
|
960
1008
|
})
|
|
961
1009
|
|
|
962
1010
|
// On each deploy, add the node to the server list
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1011
|
+
if (node.serverKNX) {
|
|
1012
|
+
node.serverKNX.addClient(node)
|
|
1013
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`addClient: node id ${node.id}` || '' + ` with topic ${node.topic || ''} has been added to the server.`)
|
|
966
1014
|
// 05/11/2021 if the node is set to read from bus, issue a read.
|
|
967
1015
|
// "node-input-initialread0": "No",
|
|
968
1016
|
// "node-input-initialread1": "Leggi dal BUS KNX",
|
|
@@ -975,11 +1023,11 @@ module.exports = function (RED) {
|
|
|
975
1023
|
const t = setTimeout(() => { // 21/03/2022 fixed possible memory leak. Previously was setTimeout without "let t = ".
|
|
976
1024
|
node.emit('input', { readstatus: true })
|
|
977
1025
|
}, 3000)
|
|
978
|
-
|
|
979
|
-
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
980
1028
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1029
|
+
// Start periodic send timer (optional)
|
|
1030
|
+
startPeriodicSendTimer()
|
|
1031
|
+
}
|
|
1032
|
+
RED.nodes.registerType('knxUltimate', knxUltimate)
|
|
985
1033
|
}
|
|
@@ -112,7 +112,7 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){ return; }
|
|
|
112
112
|
- **event (string)**: `GroupValue_Write`, `GroupValue_Response`, `Update_NoWrite` (nur interner Wert, kein BUS-Senden)
|
|
113
113
|
- **readstatus (boolean)**: Leseauftrag an den BUS
|
|
114
114
|
- **dpt (string)**: z. B. `1.001`
|
|
115
|
-
- **writeraw (buffer)** + **
|
|
115
|
+
- **writeraw (buffer)** + **bitlength (int)**: RAW-Senden; `bitlength` in Bit (`bitlenght` wird aus Kompatibilitaetsgruenden weiterhin akzeptiert)
|
|
116
116
|
- **resetRBE (boolean)**: RBE-Filter zurücksetzen
|
|
117
117
|
- **setConfig (json)**: GA/DPT des Nodes per Nachricht ändern
|
|
118
118
|
|
|
@@ -181,7 +181,8 @@ Für Read statt `event` bitte `msg.readstatus = true` verwenden.
|
|
|
181
181
|
|
|
182
182
|
- **msg.readstatus = true**: Read an den BUS
|
|
183
183
|
- **msg.dpt**: z. B. `1.001` (auch `9`, `"9"`, `"DPT9.001"`)
|
|
184
|
-
- **msg.
|
|
184
|
+
- **msg.dpt = raw**: eingehende Telegramme bleiben undekodiert; `msg.payload` ist `null` und die Bytes stehen in `msg.knx.rawValue`. Zum Senden von RAW-Telegrammen `msg.writeraw` verwenden
|
|
185
|
+
- **msg.writeraw** + **msg.bitlength**: RAW-Senden; ignoriert den am Node gesetzten DPT
|
|
185
186
|
- **msg.resetRBE = true**: RBE-Filter zurücksetzen
|
|
186
187
|
|
|
187
188
|
## Konfiguration per Nachricht ändern
|
|
@@ -227,7 +228,7 @@ msg.readstatus = true; return msg;
|
|
|
227
228
|
```javascript
|
|
228
229
|
|
|
229
230
|
msg.writeraw = Buffer.from('01','hex');
|
|
230
|
-
msg.
|
|
231
|
+
msg.bitlength = 1; return msg;
|
|
231
232
|
// Temperatur (DPT9): 18.4 °C = <0730>
|
|
232
233
|
// msg.writeraw = Buffer.from('0730','hex'); return msg;
|
|
233
234
|
```
|
|
@@ -159,8 +159,9 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
|
|
|
159
159
|
- **event (string)**: `_GroupValue_Write_` / `_GroupValue_Response_` / `_Update_NoWrite_` (`_Update_NoWrite_` updates the internal value only)
|
|
160
160
|
- **readstatus (boolean)**: issue a read request to the KNX bus (pass `_true_` every time: `msg.readstatus = true`)
|
|
161
161
|
- **dpt (string)**: for example `"1.001"` (sets the datapoint)
|
|
162
|
-
- **
|
|
163
|
-
- **
|
|
162
|
+
- **dpt = raw**: keeps incoming telegrams undecoded; `msg.payload` will be `null` and the bytes will be available in `msg.knx.rawValue`. Use `msg.writeraw` to send raw telegrams
|
|
163
|
+
- **writeraw (buffer)**: sends a raw buffer value to the bus (use with **bitlength**)
|
|
164
|
+
- **bitlength (int)**: bit length of **writeraw** (`bitlenght` is still accepted for backward compatibility)
|
|
164
165
|
- **resetRBE (boolean)**: resets the internal RBE filters (`msg.resetRBE = true`)
|
|
165
166
|
- **setConfig (json)**: programmatically change the node Group Address and Datapoint (see details)
|
|
166
167
|
|
|
@@ -263,8 +264,9 @@ msg = {
|
|
|
263
264
|
- **event (string)**: `_GroupValue_Write_` / `_GroupValue_Response_` / `_Update_NoWrite_` (`_Update_NoWrite_` updates the internal value only)
|
|
264
265
|
- **readstatus (boolean)**: issue a read request to the KNX bus (pass `_true_` every time: `msg.readstatus = true`)
|
|
265
266
|
- **dpt (string)**: for example `"1.001"` (sets the datapoint)
|
|
266
|
-
- **
|
|
267
|
-
- **
|
|
267
|
+
- **dpt = raw**: keeps incoming telegrams undecoded; `msg.payload` will be `null` and the bytes will be available in `msg.knx.rawValue`. Use `msg.writeraw` to send raw telegrams
|
|
268
|
+
- **writeraw (buffer)**: sends a raw buffer value to the bus (use with **bitlength**)
|
|
269
|
+
- **bitlength (int)**: bit length of **writeraw** (`bitlenght` is still accepted for backward compatibility)
|
|
268
270
|
- **resetRBE (boolean)**: resets the internal RBE filters (`msg.resetRBE = true`)
|
|
269
271
|
- **setConfig (json)**: programmatically change the node Group Address and Datapoint (see details)
|
|
270
272
|
|
|
@@ -436,7 +438,7 @@ Issue a "Read" command to the BUS.
|
|
|
436
438
|
**msg.dpt** <br/>
|
|
437
439
|
For example "1.001". Sets the <b>Datapoint</b>. (You can write it in these formats: 9 , "9" , "9.001" or "DPT9.001")
|
|
438
440
|
|
|
439
|
-
**msg.writeraw ** <br/>**msg.
|
|
441
|
+
**msg.writeraw ** <br/>**msg.bitlength** <br/>
|
|
440
442
|
Writes RAW data to the KNX BUS. Please see below an example.<br/>
|
|
441
443
|
|
|
442
444
|
**msg.resetRBE** <br/> pass msg.resetRBE = true to a device node, to reset both input and output RBE filter on that particular node.<br/>
|
|
@@ -500,23 +502,23 @@ return msg;
|
|
|
500
502
|
|
|
501
503
|
**SEND RAW VALUE TO THE BUS** <br/>
|
|
502
504
|
|
|
503
|
-
To send a RAW buffer value to the KNX bus, use the _ **writeraw ** _ and _**
|
|
505
|
+
To send a RAW buffer value to the KNX bus, use the _ **writeraw ** _ and _**bitlength** _ properties of the msg input.<br/>
|
|
504
506
|
In this case, the _Datapoint_ you set in the property window will be ignored.<br/>
|
|
505
507
|
Slap a function node in front of knx-ultimate and paste his code:
|
|
506
508
|
|
|
507
509
|
```javascript
|
|
508
510
|
|
|
509
511
|
// If you encode the values by yourself, you can write raw buffers with writeraw.
|
|
510
|
-
// The
|
|
512
|
+
// The bitlength is necessary for datapoint types where the bitlength does not equal the buffer bytelength * 8. This is the case for dpt 1 (bitlength 1), 2 (bitlength 2) and 3 (bitlength 4).
|
|
511
513
|
// Write raw buffer to a groupaddress with dpt 1 (e.g light on = value true = Buffer<01>) with a bitlength of 1
|
|
512
514
|
msg.writeraw = Buffer.from('01', 'hex'); // Put '00' instead of '01' to switch off the light.
|
|
513
|
-
msg.
|
|
515
|
+
msg.bitlength = 1;
|
|
514
516
|
return msg;
|
|
515
517
|
|
|
516
518
|
// Temperature sample (uncomment below and comment above)
|
|
517
519
|
// Write raw buffer to a groupaddress with dpt 9 (e.g temperature 18.4 °C = Buffer<0730>) without bitlength
|
|
518
520
|
// msg.writeraw = Buffer.from('0730', 'hex');
|
|
519
|
-
// msg.
|
|
521
|
+
// msg.bitlength = 1;
|
|
520
522
|
// return msg;
|
|
521
523
|
|
|
522
524
|
```
|
|
@@ -159,7 +159,8 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
|
|
|
159
159
|
- **event (string)**: `GroupValue_Write` / `GroupValue_Response` / `Update_NoWrite` (`Update_NoWrite` actualiza el valor interno y no envía al BUS)
|
|
160
160
|
- **readstatus (boolean)**: emite una solicitud de lectura al bus (use siempre `true`: `msg.readstatus = true`)
|
|
161
161
|
- **dpt (string)**: por ejemplo `1.001` (datapoint)
|
|
162
|
-
- **
|
|
162
|
+
- **dpt = raw**: mantiene los telegramas entrantes sin decodificar; `msg.payload` sera `null` y los bytes estaran disponibles en `msg.knx.rawValue`. Para enviar RAW use `msg.writeraw`
|
|
163
|
+
- **writeraw (buffer)** + **bitlength (int)**: envio RAW al BUS (ver ejemplo); `bitlenght` sigue siendo aceptado por compatibilidad
|
|
163
164
|
- **resetRBE (boolean)**: restablece filtros RBE internos (`msg.resetRBE = true`)
|
|
164
165
|
- **setConfig (json)**: cambia programáticamente GA y DPT (ver detalles)
|
|
165
166
|
|
|
@@ -257,7 +258,8 @@ msg = {
|
|
|
257
258
|
- **event (string)**: `GroupValue_Write` / `GroupValue_Response` / `Update_NoWrite`
|
|
258
259
|
- **readstatus (boolean)**: solicitud de lectura al bus (`msg.readstatus = true`)
|
|
259
260
|
- **dpt (string)**: por ejemplo `1.001`
|
|
260
|
-
- **
|
|
261
|
+
- **dpt = raw**: mantiene los telegramas entrantes sin decodificar; `msg.payload` sera `null` y los bytes estaran disponibles en `msg.knx.rawValue`. Para enviar RAW use `msg.writeraw`
|
|
262
|
+
- **writeraw (buffer)** + **bitlength (int)**: envio RAW (ver ejemplo); `bitlenght` sigue siendo aceptado por compatibilidad
|
|
261
263
|
- **resetRBE (boolean)**: restablece los filtros RBE (`msg.resetRBE = true`)
|
|
262
264
|
- **setConfig (json)**: cambia GA y DPT (ver detalles)
|
|
263
265
|
|
|
@@ -418,7 +420,7 @@ PRECAUCIÓN: en el caso de _msg.event = "update \ _nowrite" _ Todos los nodos co
|
|
|
418
420
|
|
|
419
421
|
Si desea emitir un comando "Leer", use "ReadStatus" en su lugar (ver más abajo). <BR/> ** msg.readstatus = true** <br/>
|
|
420
422
|
Emita un comando de "leer" al bus. ** msg.dpt** <br/>
|
|
421
|
-
Por ejemplo "1.001". Establece el punto de datos <b> </b>. (Puede escribirlo en estos formatos: 9, "9", "9.001" o "DPT9.001") ** msg.writeraw ** <br/>**msg.
|
|
423
|
+
Por ejemplo "1.001". Establece el punto de datos <b> </b>. (Puede escribirlo en estos formatos: 9, "9", "9.001" o "DPT9.001") ** msg.writeraw ** <br/>**msg.bitlength** <br/>
|
|
422
424
|
Escribe datos sin procesar en el bus KNX. Consulte a continuación un ejemplo. <br/> ** msg.resetrbe** <br/> pase msg.resetrbe = true a un nodo de dispositivo, para restablecer tanto el filtro de entrada como de salida RBE en ese nodo particular. <br/>
|
|
423
425
|
|
|
424
426
|
<br/>
|
|
@@ -478,23 +480,23 @@ return msg;
|
|
|
478
480
|
|
|
479
481
|
** Enviar valor bruto al bus** <br/>
|
|
480
482
|
|
|
481
|
-
Para enviar un valor de
|
|
483
|
+
Para enviar un valor de buffer RAW al bus KNX, use las propiedades _ **writeraw** _ y _**bitlength** _ de la entrada MSG. <br/>
|
|
482
484
|
En este caso, se ignorará el _datapoint_ que establece en la ventana de la propiedad. <br/>
|
|
483
485
|
Softe un nodo de función frente a KNX-Ulimate y pegue su código:
|
|
484
486
|
|
|
485
487
|
```javascript
|
|
486
488
|
|
|
487
489
|
// If you encode the values by yourself, you can write raw buffers with writeraw.
|
|
488
|
-
// The
|
|
490
|
+
// The bitlength is necessary for datapoint types where the bitlength does not equal the buffer bytelength * 8. This is the case for dpt 1 (bitlength 1), 2 (bitlength 2) and 3 (bitlength 4).
|
|
489
491
|
// Write raw buffer to a groupaddress with dpt 1 (e.g light on = value true = Buffer<01>) with a bitlength of 1
|
|
490
492
|
msg.writeraw = Buffer.from('01', 'hex'); // Put '00' instead of '01' to switch off the light.
|
|
491
|
-
msg.
|
|
493
|
+
msg.bitlength = 1;
|
|
492
494
|
return msg;
|
|
493
495
|
|
|
494
496
|
// Temperature sample (uncomment below and comment above)
|
|
495
497
|
// Write raw buffer to a groupaddress with dpt 9 (e.g temperature 18.4 °C = Buffer<0730>) without bitlength
|
|
496
498
|
// msg.writeraw = Buffer.from('0730', 'hex');
|
|
497
|
-
// msg.
|
|
499
|
+
// msg.bitlength = 1;
|
|
498
500
|
// return msg;
|
|
499
501
|
|
|
500
502
|
```
|
|
@@ -159,7 +159,8 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
|
|
|
159
159
|
- **event (string)**: `GroupValue_Write` / `GroupValue_Response` / `Update_NoWrite` (`Update_NoWrite` met à jour la valeur interne et n'envoie rien au BUS)
|
|
160
160
|
- **readstatus (boolean)**: demande de lecture au bus (utiliser toujours `true`: `msg.readstatus = true`)
|
|
161
161
|
- **dpt (string)**: par exemple `1.001` (datapoint)
|
|
162
|
-
- **
|
|
162
|
+
- **dpt = raw**: conserve les telegrammes entrants sans decodage ; `msg.payload` sera `null` et les octets seront disponibles dans `msg.knx.rawValue`. Pour envoyer du RAW, utilisez `msg.writeraw`
|
|
163
|
+
- **writeraw (buffer)** + **bitlength (int)**: envoi RAW au BUS (voir exemple) ; `bitlenght` reste accepte pour compatibilite
|
|
163
164
|
- **resetRBE (boolean)**: réinitialise les filtres RBE (`msg.resetRBE = true`)
|
|
164
165
|
- **setConfig (json)**: modifier GA et DPT par message (voir détails)
|
|
165
166
|
|
|
@@ -257,7 +258,8 @@ msg = {
|
|
|
257
258
|
- **event (string)**: `GroupValue_Write` / `GroupValue_Response` / `Update_NoWrite`
|
|
258
259
|
- **readstatus (boolean)**: demande de lecture au bus (`msg.readstatus = true`)
|
|
259
260
|
- **dpt (string)**: par exemple `1.001`
|
|
260
|
-
- **
|
|
261
|
+
- **dpt = raw**: conserve les telegrammes entrants sans decodage ; `msg.payload` sera `null` et les octets seront disponibles dans `msg.knx.rawValue`. Pour envoyer du RAW, utilisez `msg.writeraw`
|
|
262
|
+
- **writeraw (buffer)** + **bitlength (int)**: envoi RAW au BUS (voir exemple) ; `bitlenght` reste accepte pour compatibilite
|
|
261
263
|
- **resetRBE (boolean)**: réinitialise les filtres RBE (`msg.resetRBE = true`)
|
|
262
264
|
- **setConfig (json)**: modifier GA et DPT (voir détails)
|
|
263
265
|
|
|
@@ -418,7 +420,7 @@ ATTENTION: Dans le cas de _msg.event = "Update \ _Nowrite" _ Tous les nœuds ave
|
|
|
418
420
|
|
|
419
421
|
Si vous souhaitez émettre une commande "lire", veuillez utiliser "ReadStatus" à la place (voir ci-dessous). <br/> ** msg.readstatus = true** <br/>
|
|
420
422
|
Émettez une commande "lire" au bus. ** msg.dpt** <br/>
|
|
421
|
-
Par exemple "1.001". Définit le <b> datapoint </b>. (Vous pouvez l'écrire dans ces formats: 9, "9", "9.001" ou "DPT9.001") ** msg.writeraw ** <br/>**msg.
|
|
423
|
+
Par exemple "1.001". Définit le <b> datapoint </b>. (Vous pouvez l'écrire dans ces formats: 9, "9", "9.001" ou "DPT9.001") ** msg.writeraw ** <br/>**msg.bitlength** <br/>
|
|
422
424
|
Écrit des données brutes au bus KNX. Veuillez voir ci-dessous un exemple. <br/> ** MSG.RESETRBE** <BR/> PASS MSG.RESETRBE = VRAI À un nœud de périphérique, pour réinitialiser à la fois l'entrée et la sortie du filtre RBE sur ce nœud particulier. <br/>
|
|
423
425
|
|
|
424
426
|
<br/>
|
|
@@ -478,23 +480,23 @@ return msg;
|
|
|
478
480
|
|
|
479
481
|
**Envoyez la valeur brute au bus** <br/>
|
|
480
482
|
|
|
481
|
-
Pour envoyer une valeur
|
|
483
|
+
Pour envoyer une valeur RAW au bus KNX, utilisez les proprietes _ **writeraw** _ et _**bitlength** _ de l'entree msg. <br/>
|
|
482
484
|
Dans ce cas, le _datapoint_ que vous définissez dans la fenêtre de propriété sera ignoré. <br/>
|
|
483
485
|
Grapez un nœud de fonction devant KNX-ultime et collez son code:
|
|
484
486
|
|
|
485
487
|
```javascript
|
|
486
488
|
|
|
487
489
|
// If you encode the values by yourself, you can write raw buffers with writeraw.
|
|
488
|
-
// The
|
|
490
|
+
// The bitlength is necessary for datapoint types where the bitlength does not equal the buffer bytelength * 8. This is the case for dpt 1 (bitlength 1), 2 (bitlength 2) and 3 (bitlength 4).
|
|
489
491
|
// Write raw buffer to a groupaddress with dpt 1 (e.g light on = value true = Buffer<01>) with a bitlength of 1
|
|
490
492
|
msg.writeraw = Buffer.from('01', 'hex'); // Put '00' instead of '01' to switch off the light.
|
|
491
|
-
msg.
|
|
493
|
+
msg.bitlength = 1;
|
|
492
494
|
return msg;
|
|
493
495
|
|
|
494
496
|
// Temperature sample (uncomment below and comment above)
|
|
495
497
|
// Write raw buffer to a groupaddress with dpt 9 (e.g temperature 18.4 °C = Buffer<0730>) without bitlength
|
|
496
498
|
// msg.writeraw = Buffer.from('0730', 'hex');
|
|
497
|
-
// msg.
|
|
499
|
+
// msg.bitlength = 1;
|
|
498
500
|
// return msg;
|
|
499
501
|
|
|
500
502
|
```
|
|
@@ -141,7 +141,7 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
|
|
|
141
141
|
- **event (string)**: `GroupValue_Write` (scrive sul BUS), `GroupValue_Response` (risponde sul BUS), `Update_NoWrite` (non invia al BUS, aggiorna solo il valore interno del nodo)
|
|
142
142
|
- **readstatus (boolean)**: invia un "Read" al BUS (usa sempre `true`)
|
|
143
143
|
- **dpt (string)**: per es. `1.001` (imposta il Datapoint)
|
|
144
|
-
- **writeraw (buffer)** + **
|
|
144
|
+
- **writeraw (buffer)** + **bitlength (int)**: invio RAW verso il BUS (vedi esempio); `bitlength` è la lunghezza in bit del dato RAW (`bitlenght` resta accettato per retrocompatibilità)
|
|
145
145
|
- **resetRBE (boolean)**: resetta i filtri RBE interni (`msg.resetRBE = true`)
|
|
146
146
|
- **setConfig (json)**: cambia via msg il GA e il DPT del nodo (vedi sotto)
|
|
147
147
|
|
|
@@ -230,7 +230,8 @@ Se vuoi emettere un "read”, usa `msg.readstatus = true`.
|
|
|
230
230
|
|
|
231
231
|
- **msg.readstatus = true**: emette un comando di lettura verso il BUS
|
|
232
232
|
- **msg.dpt**: per es. `1.001` (accetta anche `9`, `"9"`, `"DPT9.001"`)
|
|
233
|
-
- **msg.
|
|
233
|
+
- **msg.dpt = raw**: lascia i telegrammi in ingresso non decodificati; `msg.payload` sara' `null` e i byte saranno disponibili in `msg.knx.rawValue`. Per inviare RAW usa `msg.writeraw`
|
|
234
|
+
- **msg.writeraw** + **msg.bitlength**: invio RAW al BUS (vedi esempio sotto); se usi `writeraw`, il DPT impostato nel nodo viene ignorato
|
|
234
235
|
- **msg.resetRBE = true**: resetta i filtri RBE di input e output del nodo
|
|
235
236
|
|
|
236
237
|
## Cambiare la configurazione via msg
|
|
@@ -285,7 +286,7 @@ Inserisci un function node prima del nodo KNX-Ultimate e usa questo codice:
|
|
|
285
286
|
|
|
286
287
|
// Esempio DPT 1: luce ON = Buffer<01> con bitlength = 1
|
|
287
288
|
msg.writeraw = Buffer.from('01', 'hex');
|
|
288
|
-
msg.
|
|
289
|
+
msg.bitlength = 1;
|
|
289
290
|
return msg;
|
|
290
291
|
|
|
291
292
|
// Esempio temperatura (DPT 9): 18.4 °C = Buffer<0730>
|
|
@@ -112,7 +112,7 @@ if (msg.payload === false && getGAValue('0/0/11','1.001') === false){ return; }
|
|
|
112
112
|
- **event (string)**:`GroupValue_Write` / `GroupValue_Response` / `Update_NoWrite`(`Update_NoWrite` 仅更新内部值,不发送)
|
|
113
113
|
- **readstatus (boolean)**:向总线发起读取(使用 `msg.readstatus = true`)
|
|
114
114
|
- **dpt (string)**:如 `1.001`
|
|
115
|
-
- **writeraw (buffer)** + **
|
|
115
|
+
- **writeraw (buffer)** + **bitlength (int)**:发送 RAW,`bitlength` 为比特长度(为兼容旧流程,`bitlenght` 仍然可用)
|
|
116
116
|
- **resetRBE (boolean)**:重置内部 RBE 过滤(`msg.resetRBE = true`)
|
|
117
117
|
- **setConfig (json)**:通过消息修改节点的 GA 与 DPT
|
|
118
118
|
|
|
@@ -181,7 +181,8 @@ msg = {
|
|
|
181
181
|
|
|
182
182
|
- **msg.readstatus = true**:向总线发起读取
|
|
183
183
|
- **msg.dpt**:如 `1.001`(也接受 `9`、`"9"`、`"DPT9.001"`)
|
|
184
|
-
- **msg.
|
|
184
|
+
- **msg.dpt = raw**:保持收到的报文不做解码;`msg.payload` 将为 `null`,原始字节可在 `msg.knx.rawValue` 中获取。发送 RAW 时请使用 `msg.writeraw`
|
|
185
|
+
- **msg.writeraw** + **msg.bitlength**:发送 RAW;会忽略节点上设置的 DPT
|
|
185
186
|
- **msg.resetRBE = true**:重置 RBE 过滤
|
|
186
187
|
|
|
187
188
|
## 通过消息修改配置
|
|
@@ -229,7 +230,7 @@ msg.readstatus = true; return msg;
|
|
|
229
230
|
|
|
230
231
|
// DPT1 示例:开灯 = Buffer<01>,bitlength = 1
|
|
231
232
|
msg.writeraw = Buffer.from('01','hex');
|
|
232
|
-
msg.
|
|
233
|
+
msg.bitlength = 1; return msg;
|
|
233
234
|
// 温度(DPT9)示例:18.4 °C = <0730>
|
|
234
235
|
// msg.writeraw = Buffer.from('0730','hex'); return msg;
|
|
235
236
|
```
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=20.18.1"
|
|
5
5
|
},
|
|
6
|
-
"version": "4.3.
|
|
6
|
+
"version": "4.3.5",
|
|
7
7
|
"description": "Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, and KNX routing between interfaces. Easy to use and highly configurable.",
|
|
8
8
|
"files": [
|
|
9
9
|
"nodes/",
|