node-red-contrib-knx-ultimate 4.3.9 → 4.3.11

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 CHANGED
@@ -6,6 +6,17 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ **Version 4.3.11** - May 2026<br/>
10
+
11
+ - Docs/help/wiki: clarified in **KNX DEVICE / KNX Function** help HTML and **Device** wiki pages in all supported languages (**EN/IT/DE/FR/ES/zh-CN**) that `getGAValue(...)` is async and should be used as **`await getGAValue(...)`**, even in cache-only mode.<br/>
12
+ - UI: updated **KNX Function** editor helper/snippet and Monaco typings so `getGAValue(...)` is suggested with `await` and exposed as a `Promise` in the editor.<br/>
13
+
14
+ **Version 4.3.10** - April 2026<br/>
15
+
16
+ - CHANGE: **KNX Function** `getGAValue(...)` now accepts an optional `readIfMissing` boolean. Default is `true` to preserve the current behaviour; pass `false` to use cache-only mode and immediately return `undefined` when the GA value is not available locally.<br/>
17
+ - PERF: **KNX Function** `getGAValue(...)` and KNX cache lookups now use indexed GA access plus deduplicated pending reads, reducing overhead and avoiding duplicate simultaneous `GroupValue_Read` requests for the same GA.<br/>
18
+ - Docs/help/wiki: updated **KNX DEVICE / KNX Function** help HTML and **Device** wiki pages in all supported languages (**EN/IT/DE/FR/ES/zh-CN**) to document the new `getGAValue(..., readIfMissing)` signature and cache-only mode.<br/>
19
+
9
20
  **Version 4.3.9** - April 2026<br/>
10
21
 
11
22
  - Bump KNX Engine to 5.5.3 (fixed umlaut issue in dpt16)<br/>
@@ -200,6 +200,7 @@ module.exports = (RED) => {
200
200
  node.stopETSImportIfNoDatapoint = typeof config.stopETSImportIfNoDatapoint === 'undefined' ? 'stop' : config.stopETSImportIfNoDatapoint // 09/01/2020 Stop, Import Fake or Skip the import if a group address has unset datapoint
201
201
  node.userDir = path.join(RED.settings.userDir, 'knxultimatestorage') // 04/04/2021 Supergiovane: Storage for service files
202
202
  node.exposedGAs = []
203
+ node.exposedGAsByGa = new Map()
203
204
  node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error' // 18/02/2020 Loglevel default error
204
205
  if (node.loglevel === 'trace') node.loglevel = 'debug' // Backward compatibility
205
206
  if (node.loglevel === 'silent') node.loglevel = 'disable' // Backward compatibility
@@ -208,6 +209,44 @@ module.exports = (RED) => {
208
209
  node.sysLogger = new loggerClass({ loglevel: node.loglevel, setPrefix: node.type + ' <' + (node.name || node.id || '') + '>' })
209
210
  } catch (error) { console.log(error.stack) }
210
211
  node.csv = readCSV(config.csv) // Array from ETS CSV Group Addresses {ga:group address, dpt: datapoint, devicename: full device name with main and subgroups}
212
+ node.csvByGa = new Map()
213
+ if (Array.isArray(node.csv)) {
214
+ node.csv.forEach((entry) => {
215
+ if (entry && typeof entry.ga === 'string' && entry.ga !== '') node.csvByGa.set(entry.ga, entry)
216
+ })
217
+ }
218
+
219
+ node.rebuildExposedGAIndex = () => {
220
+ node.exposedGAsByGa = new Map()
221
+ if (!Array.isArray(node.exposedGAs)) return
222
+ node.exposedGAs.forEach((entry) => {
223
+ if (entry && typeof entry.ga === 'string' && entry.ga !== '') node.exposedGAsByGa.set(entry.ga, entry)
224
+ })
225
+ }
226
+
227
+ node.getExposedGAEntry = (ga) => {
228
+ if (typeof ga !== 'string' || ga === '') return undefined
229
+ return node.exposedGAsByGa.get(ga)
230
+ }
231
+
232
+ node.upsertExposedGAEntry = (entry) => {
233
+ if (!entry || typeof entry.ga !== 'string' || entry.ga === '') return undefined
234
+ const existing = node.exposedGAsByGa.get(entry.ga)
235
+ if (existing) {
236
+ Object.assign(existing, entry)
237
+ return existing
238
+ }
239
+ node.exposedGAs.push(entry)
240
+ node.exposedGAsByGa.set(entry.ga, entry)
241
+ return entry
242
+ }
243
+
244
+ node.removeExposedGAEntry = (ga) => {
245
+ if (typeof ga !== 'string' || ga === '') return
246
+ node.exposedGAsByGa.delete(ga)
247
+ const index = node.exposedGAs.findIndex((item) => item.ga === ga)
248
+ if (index > -1) node.exposedGAs.splice(index, 1)
249
+ }
211
250
 
212
251
  // 12/11/2021 Connect at start delay
213
252
  node.autoReconnect = true // 20/03/2022 Default
@@ -764,10 +803,12 @@ module.exports = (RED) => {
764
803
  const sFile = path.join(node.userDir, 'knxpersistvalues', 'knxpersist' + node.id + '.json')
765
804
  try {
766
805
  node.exposedGAs = JSON.parse(fs.readFileSync(sFile, 'utf8'))
806
+ if (!Array.isArray(node.exposedGAs)) node.exposedGAs = []
767
807
  } catch (err) {
768
808
  node.exposedGAs = []
769
809
  node.sysLogger?.info('unable to read peristent file ' + sFile + ' ' + err.message)
770
810
  }
811
+ node.rebuildExposedGAIndex()
771
812
  }
772
813
 
773
814
  // ************************
@@ -856,7 +897,7 @@ module.exports = (RED) => {
856
897
  } else {
857
898
  try {
858
899
  if (node.exposedGAs.length > 0) {
859
- const oExposedGA = node.exposedGAs.find((a) => a.ga === _oClient.topic)
900
+ const oExposedGA = node.getExposedGAEntry(_oClient.topic)
860
901
  if (oExposedGA !== undefined) {
861
902
  // Retrieve the value from exposedGAs
862
903
  const msg = buildInputMessage({
@@ -885,7 +926,7 @@ module.exports = (RED) => {
885
926
  if (msg.payload === null) {
886
927
  _oClient._hasCurrentPayload = false
887
928
  // Delete the exposedGA
888
- node.exposedGAs = node.exposedGAs.filter((item) => item.ga !== _oClient.topic)
929
+ node.removeExposedGAEntry(_oClient.topic)
889
930
  _oClient.setNodeStatus({
890
931
  fill: 'yellow',
891
932
  shape: 'dot',
@@ -1384,16 +1425,14 @@ module.exports = (RED) => {
1384
1425
  if (typeof _dest === 'string' && _rawValue !== undefined && (_evt === 'GroupValue_Write' || _evt === 'GroupValue_Response')) {
1385
1426
  try {
1386
1427
  const ret = { ga: _dest, rawValue: _rawValue, dpt: undefined, devicename: undefined, updatedAt: Date.now() }
1387
- node.exposedGAs = node.exposedGAs.filter((item) => item.ga !== _dest) // Remove previous
1388
- if (node.csv !== undefined && node.csv !== '' && node.csv.length !== 0) {
1389
- // Add the dpt
1390
- const found = node.csv.find(a => a.ga === _dest)
1428
+ if (node.csvByGa.size > 0) {
1429
+ const found = node.csvByGa.get(_dest)
1391
1430
  if (found !== undefined) {
1392
1431
  ret.dpt = found.dpt
1393
1432
  ret.devicename = found.devicename
1394
1433
  }
1395
1434
  }
1396
- node.exposedGAs.push(ret) // add the new
1435
+ node.upsertExposedGAEntry(ret)
1397
1436
  } catch (error) { }
1398
1437
  }
1399
1438
 
@@ -271,10 +271,10 @@
271
271
  const knxFunctionHelperItems = [
272
272
  {
273
273
  id: 'getGAValue',
274
- label: 'getGAValue(address, dpt?)',
275
- aceValue: "getGAValue('1/1/1', '1.001')",
276
- snippet: "getGAValue('${1:1/1/1}', '${2:1.001}')",
277
- doc: 'Read the cached value of another group address. Provide the datapoint if the ETS import is not available.'
274
+ label: 'getGAValue(address, dpt?, readIfMissing?)',
275
+ aceValue: "await getGAValue('1/1/1', '1.001', false)",
276
+ snippet: "await getGAValue('${1:1/1/1}', '${2:1.001}', ${3:false})",
277
+ doc: 'Read another group address. This helper is async, so use await to get the real value. By default, if the value is not cached, a KNX read is sent. Pass false as third parameter to use cache-only mode.'
278
278
  },
279
279
  {
280
280
  id: 'setGAValue',
@@ -465,8 +465,8 @@ return msg;`
465
465
  });
466
466
 
467
467
  monaco.languages.typescript.javascriptDefaults.addExtraLib([
468
- '/** Read the cached value of a KNX group address. */',
469
- 'declare function getGAValue(address: string, dpt?: string): any;',
468
+ '/** Read a KNX group address. This helper is async, so use await to get the real value. By default, if the value is not cached, a KNX read is sent. Pass false as third parameter to use cache-only mode. */',
469
+ 'declare function getGAValue(address: string, dptOrReadIfMissing?: string | boolean, readIfMissing?: boolean): Promise<any>;',
470
470
  '/** Send a value to a KNX group address. */',
471
471
  'declare function setGAValue(address: string, value: any, dpt?: string): void;',
472
472
  '/** Toggle (invert) this node\'s current value on the KNX bus. */',
@@ -505,13 +505,33 @@ module.exports = function (RED) {
505
505
  }
506
506
  }
507
507
 
508
+ const pendingGAReads = new Map()
509
+ const findExposedGA = (ga) => {
510
+ if (!node.serverKNX) return undefined
511
+ if (typeof node.serverKNX.getExposedGAEntry === 'function') return node.serverKNX.getExposedGAEntry(ga)
512
+ return Array.isArray(node.serverKNX.exposedGAs) ? node.serverKNX.exposedGAs.find(a => a.ga === ga) : undefined
513
+ }
514
+
508
515
  // Used in the KNX Function TAB
509
- const getGAValue = async function getGAValue(_ga = undefined, _dpt = undefined) {
516
+ const getGAValue = async function getGAValue(_ga = undefined, _dpt = undefined, _requestReadIfMissing = undefined) {
510
517
  if (_ga === undefined) return null
511
518
  // Strip devicename if present (e.g. "1/1/1 Light name" → "1/1/1")
512
519
  const blankSpacePosition = _ga.indexOf(' ')
513
520
  if (blankSpacePosition > -1) _ga = _ga.substring(0, blankSpacePosition)
514
521
 
522
+ // Overloads:
523
+ // - getGAValue('1/1/1')
524
+ // - getGAValue('1/1/1', '1.001')
525
+ // - getGAValue('1/1/1', '1.001', false)
526
+ // - getGAValue('1/1/1', false)
527
+ let dptRequested = _dpt
528
+ let requestReadIfMissing = _requestReadIfMissing
529
+ if (typeof dptRequested === 'boolean' && requestReadIfMissing === undefined) {
530
+ requestReadIfMissing = dptRequested
531
+ dptRequested = undefined
532
+ }
533
+ if (requestReadIfMissing === undefined) requestReadIfMissing = true
534
+
515
535
  const tryDecode = (rawValue, dpt) => {
516
536
  try {
517
537
  if (rawValue === null || rawValue === undefined) return undefined
@@ -531,30 +551,43 @@ module.exports = function (RED) {
531
551
  }
532
552
 
533
553
  // Check cache first
534
- let found = node.serverKNX.exposedGAs.find(a => a.ga === _ga)
554
+ let found = findExposedGA(_ga)
535
555
  if (found !== undefined) {
536
- const dptFinal = _dpt || found.dpt
556
+ const dptFinal = dptRequested || found.dpt
537
557
  if (!dptFinal) {
538
558
  RED.log.error('getGAValue: no DPT for ' + _ga + '. Provide it as second parameter.')
539
559
  return null
540
560
  }
541
561
  const val = tryDecode(found.rawValue, dptFinal)
542
562
  if (val !== undefined) return val
543
- // rawValue present but decode failed (bad format) — fall through to read
563
+ // rawValue present but decode failed (bad format) — fall through to read, unless cache-only mode is requested.
564
+ if (requestReadIfMissing === false) return undefined
544
565
  RED.log.warn('getGAValue: cached rawValue for ' + _ga + ' could not be decoded, sending read request')
545
566
  }
546
567
 
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)
552
- if (found !== undefined) {
553
- const dptFinal = _dpt || found.dpt
554
- if (!dptFinal) return null
555
- const val = tryDecode(found.rawValue, dptFinal)
556
- if (val !== undefined) return val
557
- }
568
+ if (requestReadIfMissing === false) return undefined
569
+
570
+ // Not cached or decode failed: send read and poll up to 3 seconds.
571
+ // Deduplicate concurrent reads to the same GA to avoid repeated bus reads and repeated polling loops.
572
+ let pendingRead = pendingGAReads.get(_ga)
573
+ if (!pendingRead) {
574
+ pendingRead = (async () => {
575
+ sendRead()
576
+ for (let i = 0; i < 30; i++) {
577
+ await new Promise(resolve => setTimeout(resolve, 100))
578
+ const updated = findExposedGA(_ga)
579
+ if (updated !== undefined) return updated
580
+ }
581
+ return null
582
+ })().finally(() => pendingGAReads.delete(_ga))
583
+ pendingGAReads.set(_ga, pendingRead)
584
+ }
585
+ found = await pendingRead
586
+ if (found !== null && found !== undefined) {
587
+ const dptFinal = dptRequested || found.dpt
588
+ if (!dptFinal) return null
589
+ const val = tryDecode(found.rawValue, dptFinal)
590
+ if (val !== undefined) return val
558
591
  }
559
592
  RED.log.warn('getGAValue: no response from KNX bus for ' + _ga + ' within 3 seconds')
560
593
  return null
@@ -569,7 +602,7 @@ module.exports = function (RED) {
569
602
  if (blankSpacePosition > -1) _ga = _ga.substring(0, blankSpacePosition)
570
603
  if (_dpt === undefined) {
571
604
  // Try getting dpt from ETS CSV
572
- const found = node.serverKNX.exposedGAs.find(a => a.ga === _ga)
605
+ const found = findExposedGA(_ga)
573
606
  if (found === undefined || found.dpt === undefined) {
574
607
  const errM = 'setGAValue: node ID:' + node.id + ' ' + 'No CSV file imported. Please provide the dpt manually'
575
608
  RED.log.error(errM)
@@ -71,7 +71,7 @@ Der Code läuft bei jeder Eingangs-Nachricht und bei jedem BUS-Telegramm. Bei Ak
71
71
  |Objekt/Funktion|Beschreibung|
72
72
  |--|--|
73
73
  | `msg` | Aktuelle Nachricht. |
74
- | `getGAValue(GA, DPT?)` | Liest den Wert einer GA, z. B. `'1/0/1'` oder `'1/0/1 Bed table light'` (Text nach Leerzeichen wird ignoriert). DPT nur ohne ETS erforderlich. |
74
+ | `getGAValue(GA, DPT?, readIfMissing?)` | Liest den Wert einer GA, z. B. `'1/0/1'` oder `'1/0/1 Bed table light'` (Text nach Leerzeichen wird ignoriert). Mit importierter ETS ist DPT optional; sonst muss es angegeben werden. Standardmäßig sendet die Funktion bei fehlendem Cache-Wert ein `GroupValue_Read` und wartet auf die Antwort. Mit `false` als drittem Parameter, oder als zweitem wenn kein DPT nötig ist, arbeitet die Funktion nur mit dem Cache und liefert `undefined`, wenn lokal kein Wert vorhanden ist. `getGAValue` ist asynchron, daher solltest du `await getGAValue(...)` verwenden, wenn du den echten Wert brauchst. Auch im Cache-only-Modus bleibt `await` erforderlich, weil der Helper immer eine Promise zurückgibt. |
75
75
  | `setGAValue(GA, value, DPT?)` | Setzt den Wert der GA; DPT wie oben. |
76
76
  | `self(value)` | Setzt den eigenen Node-Wert und sendet ihn an den BUS (Achtung Schleifen). |
77
77
  | `toggle()` | Toggeln wie `self`. |
@@ -81,7 +81,7 @@ Der Code läuft bei jeder Eingangs-Nachricht und bei jedem BUS-Telegramm. Bei Ak
81
81
 
82
82
  ```javascript
83
83
 
84
- const statusGA = getGAValue('0/0/09','1.001');
84
+ const statusGA = await getGAValue('0/0/09','1.001');
85
85
  if (msg.payload !== statusGA){ return msg; } else { return; }
86
86
  ```
87
87
 
@@ -97,12 +97,13 @@ return msg;
97
97
 
98
98
  ```javascript
99
99
 
100
- msg.externalTemperature = getGAValue('0/0/10'); // ohne ETS: getGAValue('0/0/10','9.001')
100
+ msg.externalTemperature = await getGAValue('0/0/10'); // ohne ETS: await getGAValue('0/0/10','9.001')
101
101
  return msg;
102
102
  ```
103
103
 
104
104
  ```javascript
105
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){ return; } else { return msg; }
105
+ const otherGA = await getGAValue('0/0/11','1.001');
106
+ if (msg.payload === false && otherGA === false){ return; } else { return msg; }
106
107
  ```
107
108
 
108
109
  ### Inputs
@@ -77,7 +77,7 @@ If enabled, "f(x)" indication will be added to node's name.
77
77
  |Object or Function|Description|
78
78
  |--|--|
79
79
  | msg (object) | The current msg object received by the node. |
80
- | getGAValue (string GA, optional string DPT) | Get the specified GA's value, for example **'1/0/1'**, or also **'1/0/1 Bed table light'** (all text after a blank space will be ignored by the function). With the ETS file imported, you can also copy and paste the GA and GA Name directly from the **Search GA** field. **DPT** is optional if you've imported the ETS file, otherwise you must specify it (for example '1.001'). |
80
+ | getGAValue (string GA, optional string DPT, optional bool readIfMissing) | Get the specified GA's value, for example **'1/0/1'**, or also **'1/0/1 Bed table light'** (all text after a blank space will be ignored by the function). With the ETS file imported, you can also copy and paste the GA and GA Name directly from the **Search GA** field. **DPT** is optional if you've imported the ETS file, otherwise you must specify it (for example '1.001'). By default, if the value is not cached, the function sends a `GroupValue_Read` and waits for the reply. Pass `false` as the third parameter, or as the second one when no DPT is needed, to use cache-only mode and return `undefined` when the value is not available locally. `getGAValue` is async, so use `await getGAValue(...)` whenever you need the actual value. Even in cache-only mode, keep `await`, because the helper always returns a Promise. |
81
81
  | setGAValue (string GA, any value, optional string DPT) | Set the specified GA's value. The GA can be wrote for example **'1/0/1'**, or also **'1/0/1 Bed table light'** (all text after a blank space will be ignored by the function). With the ETS file imported, you can also copy and paste the GA and GA Name directly from the **Search GA** field. The **value** is mandatory (boolean/number/string/object). **DPT** is optional if you've imported the ETS file, otherwise you must specify it (for example '1.001'). |
82
82
  | self (any value) | Set the currend node's value and sends the value to the KNX BUS as well. For example, _self(false)_. Caution using **self** function in the _From KNX BUS to node's OUTPUT PIN_ code, because the code will be executed everytime a KNX telegram is received, so you coud have recurrency loops. |
83
83
  | toggle (nothing) | Toggle the currend node's value and sends the value to the KNX BUS as well. For example, _toggle()_. Caution using **toggle** function in the _From KNX BUS to node's OUTPUT PIN_ code, because the code will be executed everytime a KNX telegram is received, so you coud have recurrency loops. |
@@ -92,7 +92,7 @@ We'll turn on the light only if it's status GA is off, and vice versa.
92
92
 
93
93
  ```javascript
94
94
 
95
- const statusGA = getGAValue('0/0/09','1.001');
95
+ const statusGA = await getGAValue('0/0/09','1.001');
96
96
  if (msg.payload !== statusGA){ // "!==" means "not equal"
97
97
  return msg;
98
98
  }else{
@@ -122,7 +122,7 @@ The ouput msg to the flow will have also the external temp in the property `msg.
122
122
  ```javascript
123
123
 
124
124
  // The current msg contains the internal temperature in the "msg.payload" property, but we want to emit the external temperature as well.
125
- msg.externalTemperature = getGAValue('0/0/10'); // In case the ETS file is missing, you must specify the dpt as well: getGAValue('0/0/10','9.001')
125
+ msg.externalTemperature = await getGAValue('0/0/10'); // In case the ETS file is missing, you must specify the dpt as well: await getGAValue('0/0/10','9.001')
126
126
  return msg;
127
127
  ```
128
128
 
@@ -130,7 +130,8 @@ In this other sample, we'll not emit a msg to the flow, in case msg.payload and
130
130
 
131
131
  ```javascript
132
132
 
133
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
133
+ const otherGA = await getGAValue('0/0/11','1.001');
134
+ if (msg.payload === false && otherGA === false){
134
135
  // Both false, don't emit the msg to the flow.
135
136
  return;
136
137
  }else{
@@ -77,7 +77,7 @@ Si está habilitado, la indicación "F (x)" se agregará al nombre del nodo.
77
77
  | Objeto o función | Descripción |
78
78
  |-|-|
79
79
  | msg (objeto) | El objeto MSG actual recibido por el nodo. |
80
- | getGavalue (String ga, string opcional dpt) | Obtenga el valor de GA especificado, por ejemplo **'1/0/1'** o **'1/0/1 Luz mesilla'** (el texto tras un espacio se ignora). Con el archivo ETS importado, también puede copiar y pegar la GA desde el campo **Búsqueda GA**. **DPT** es opcional con ETS; si no, debe indicarlo (por ejemplo `'1.001'`). |
80
+ | getGavalue (String ga, string opcional dpt, bool opcional readIfMissing) | Obtenga el valor de la GA especificada, por ejemplo **'1/0/1'** o **'1/0/1 Luz mesilla'** (el texto tras un espacio se ignora). Con el archivo ETS importado, también puede copiar y pegar la GA desde el campo **Búsqueda GA**. **DPT** es opcional con ETS; si no, debe indicarlo (por ejemplo `'1.001'`). Por defecto, si el valor no está en caché, la función envía un `GroupValue_Read` y espera la respuesta. Pase `false` como tercer parámetro, o como segundo si no necesita DPT, para usar el modo solo caché y devolver `undefined` cuando el valor no esté disponible localmente. `getGAValue` es asíncrona, así que use `await getGAValue(...)` cuando necesite el valor real. Incluso en modo solo caché, mantenga `await`, porque el helper siempre devuelve una Promise. |
81
81
  | setgavalue (cadena ga, cualquier valor, cadena opcional dpt) | Establezca el valor de GA especificado, por ejemplo **'1/0/1'** o **'1/0/1 Luz mesilla'** (el texto tras un espacio se ignora). Con el archivo ETS importado, también puede copiar y pegar la GA desde el campo **Búsqueda GA**. El valor es obligatorio; **dpt** es opcional con ETS. |
82
82
  | yo (cualquier valor) | Establezca el valor del nodo de Currend y envía el valor al bus KNX también. Por ejemplo, _Self (False) _. PRECAUCIÓN Uso de ** Función ** ** En la _From KNX Bus hasta el código PIN_ de salida del nodo, porque el código se ejecutará cada vez que se reciba un telegrama KNX, por lo que usted tiene bucles de recurrencia. |
83
83
  | alternar (nada) | Alterne el valor del nodo de Currend y envía el valor al bus KNX también. Por ejemplo, _Toggle () _. PRECAUCIÓN Uso de la función ** Toggle** En el código _From KNX BUS al Código PIN_ de salida del nodo, porque el código se ejecutará cada vez que se reciba un Telegrama KNX, por lo que usted tiene bucles de recurrencia. |
@@ -92,7 +92,7 @@ Encenderemos la luz solo si su estado GA está apagado y viceversa.
92
92
 
93
93
  ```javascript
94
94
 
95
- const statusGA = getGAValue('0/0/09','1.001');
95
+ const statusGA = await getGAValue('0/0/09','1.001');
96
96
  if (msg.payload !== statusGA){ // "!==" means "not equal"
97
97
  return msg;
98
98
  }else{
@@ -122,7 +122,7 @@ El mensaje de salida al flujo también tendrá la temperatura externa en la prop
122
122
  ```javascript
123
123
 
124
124
  // The current msg contains the internal temperature in the "msg.payload" property, but we want to emit the external temperature as well.
125
- msg.externalTemperature = getGAValue('0/0/10'); // In case the ETS file is missing, you must specify the dpt as well: getGAValue('0/0/10','9.001')
125
+ msg.externalTemperature = await getGAValue('0/0/10'); // Si falta el archivo ETS: await getGAValue('0/0/10','9.001')
126
126
  return msg;
127
127
  ```
128
128
 
@@ -130,7 +130,8 @@ En esta otra muestra, no emitiremos un MSG al flujo, en el caso de MSG.PayLoad y
130
130
 
131
131
  ```javascript
132
132
 
133
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
133
+ const otherGA = await getGAValue('0/0/11','1.001');
134
+ if (msg.payload === false && otherGA === false){
134
135
  // Both false, don't emit the msg to the flow.
135
136
  return;
136
137
  }else{
@@ -77,7 +77,7 @@ S'il est activé, l'indication "f (x)" sera ajoutée au nom du nœud.
77
77
  | Objet ou fonction | Description |
78
78
  |-|-|
79
79
  | msg (objet) | L'objet MSG actuel reçu par le nœud. |
80
- | getGavalue (String GA, String facultatif DPT) | Obtenez la valeur de GA spécifiée, par exemple **'1/0/1'** ou **'1/0/1 Light'** (le texte après un espace est ignoré). Avec le fichier ETS importé, vous pouvez également copier et coller le nom GA et GA directement dans le champ **Recherche GA**. **DPT** est facultatif avec ETS; sinon vous devez le spécifier (par exemple `'1.001'`). |
80
+ | getGavalue (String GA, String DPT facultatif, bool readIfMissing facultatif) | Obtenez la valeur de la GA spécifiée, par exemple **'1/0/1'** ou **'1/0/1 Light'** (le texte après un espace est ignoré). Avec le fichier ETS importé, vous pouvez également copier et coller le nom GA et GA directement dans le champ **Recherche GA**. **DPT** est facultatif avec ETS; sinon vous devez le spécifier (par exemple `'1.001'`). Par défaut, si la valeur n'est pas en cache, la fonction envoie un `GroupValue_Read` et attend la réponse. Passez `false` comme troisième paramètre, ou comme deuxième si aucun DPT n'est nécessaire, pour utiliser le mode cache uniquement et retourner `undefined` si la valeur n'est pas disponible localement. `getGAValue` est asynchrone: utilisez donc `await getGAValue(...)` quand vous avez besoin de la vraie valeur. Même en mode cache uniquement, gardez `await`, car le helper renvoie toujours une Promise. |
81
81
  | setGavalue (String ga, n'importe quelle valeur, chaîne facultative dpt) | Définissez la valeur de GA spécifiée, par exemple **'1/0/1'** ou **'1/0/1 Light'** (le texte après un espace est ignoré). Avec le fichier ETS importé, vous pouvez également copier et coller le nom GA et GA directement depuis le champ **Recherche GA**. La valeur est obligatoire; **dpt** est facultatif avec ETS. |
82
82
  | self (toute valeur) | Définissez la valeur du nœud Currend et envoie également la valeur au bus KNX. Par exemple, _self (false) _. ATTENTION UTILISATION ** Self** Fonction dans le code Pin_ de sortie du bus KNX _From pour le nœud, car le code sera exécuté à chaque fois qu'un télégramme KNX est reçu, vous avez donc Coud ayant des boucles de récurrence. |
83
83
  | basculer (rien) | Basculez la valeur du nœud Currend et envoie également la valeur au bus KNX. Par exemple, _toggle () _. ATTENTION en utilisant ** Toggle** Fonction dans le code Pin_ de sortie du bus KNX _From à la sortie du nœud, car le code sera exécuté à chaque fois qu'un télégramme KNX est reçu, de sorte que vous avez des boucles de récurrence. |
@@ -92,7 +92,7 @@ Nous allons allumer la lumière uniquement si son statut GA est éteint, et vice
92
92
 
93
93
  ```javascript
94
94
 
95
- const statusGA = getGAValue('0/0/09','1.001');
95
+ const statusGA = await getGAValue('0/0/09','1.001');
96
96
  if (msg.payload !== statusGA){ // "!==" means "not equal"
97
97
  return msg;
98
98
  }else{
@@ -122,7 +122,7 @@ Le msg ouput au flux aura également la température externe dans la propriété
122
122
  ```javascript
123
123
 
124
124
  // The current msg contains the internal temperature in the "msg.payload" property, but we want to emit the external temperature as well.
125
- msg.externalTemperature = getGAValue('0/0/10'); // In case the ETS file is missing, you must specify the dpt as well: getGAValue('0/0/10','9.001')
125
+ msg.externalTemperature = await getGAValue('0/0/10'); // En l'absence du fichier ETS: await getGAValue('0/0/10','9.001')
126
126
  return msg;
127
127
  ```
128
128
 
@@ -130,7 +130,8 @@ Dans cet autre échantillon, nous n'émettons pas de msg au flux, au cas où msg
130
130
 
131
131
  ```javascript
132
132
 
133
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
133
+ const otherGA = await getGAValue('0/0/11','1.001');
134
+ if (msg.payload === false && otherGA === false){
134
135
  // Both false, don't emit the msg to the flow.
135
136
  return;
136
137
  }else{
@@ -76,7 +76,7 @@ Lo script viene eseguito a ogni msg in ingresso o a ogni telegramma ricevuto dal
76
76
  |Oggetto/Funzione|Descrizione|
77
77
  |--|--|
78
78
  | `msg` (object) | Il msg corrente ricevuto dal nodo. |
79
- | `getGAValue` (string GA, string DPT opz.) | Restituisce il valore del GA, ad es. `'1/0/1'` o `'1/0/1 Bed table light'` (tutto dopo lo spazio viene ignorato). Con ETS importato, DPT è opzionale; altrimenti va specificato, per es. `'1.001'`. |
79
+ | `getGAValue` (string GA, string DPT opz., bool readIfMissing opz.) | Restituisce il valore del GA, ad es. `'1/0/1'` o `'1/0/1 Bed table light'` (tutto dopo lo spazio viene ignorato). Con ETS importato, DPT è opzionale; altrimenti va specificato, per es. `'1.001'`. Per default, se il valore non è in cache, viene inviato un `GroupValue_Read` e la funzione attende la risposta. Passando `false` come terzo parametro, oppure come secondo se il DPT non serve, la funzione lavora in modalità solo-cache e restituisce `undefined` se il valore non è disponibile localmente. `getGAValue` è asincrona, quindi quando ti serve il valore reale devi usare `await getGAValue(...)`. Anche in modalità solo-cache va mantenuto `await`, perché l'helper restituisce sempre una Promise. |
80
80
  | `setGAValue` (string GA, any value, string DPT opz.) | Imposta il valore del GA indicato. Valgono le stesse regole di notazione del GA; il valore può essere boolean/number/string; DPT opzionale con ETS, altrimenti obbligatorio. |
81
81
  | `self` (any) | Imposta il valore del nodo corrente e lo invia anche al BUS KNX (es.: `self(false)`). Attenzione nell'handler "From KNX BUS to OUTPUT”, per evitare loop. |
82
82
  | `toggle`() | Inverte il valore del nodo e lo invia al BUS. Stesse cautele di `self`. |
@@ -90,7 +90,7 @@ Invia al BUS solo se un altro GA ha valore opposto:
90
90
 
91
91
  ```javascript
92
92
 
93
- const statusGA = getGAValue('0/0/09','1.001');
93
+ const statusGA = await getGAValue('0/0/09','1.001');
94
94
  if (msg.payload !== statusGA){
95
95
  return msg;
96
96
  } else {
@@ -116,7 +116,7 @@ Allega all'output anche la temperatura esterna letta da un altro GA:
116
116
  ```javascript
117
117
 
118
118
  // msg.payload contiene la temperatura interna; aggiungi la esterna
119
- msg.externalTemperature = getGAValue('0/0/10'); // senza ETS: getGAValue('0/0/10','9.001')
119
+ msg.externalTemperature = await getGAValue('0/0/10'); // senza ETS: await getGAValue('0/0/10','9.001')
120
120
  return msg;
121
121
  ```
122
122
 
@@ -124,7 +124,8 @@ Non emettere nulla se `msg.payload` e un altro GA sono entrambi false:
124
124
 
125
125
  ```javascript
126
126
 
127
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){
127
+ const otherGA = await getGAValue('0/0/11','1.001');
128
+ if (msg.payload === false && otherGA === false){
128
129
  return; // non emettere
129
130
  } else {
130
131
  return msg;
@@ -71,7 +71,7 @@
71
71
  |对象/函数|说明|
72
72
  |--|--|
73
73
  | `msg` | 当前消息对象。|
74
- | `getGAValue(GA, DPT?)` | 读取 GA 值,如 `'1/0/1'` 或 `'1/0/1 Bed table light'`(空格后的文字被忽略)。未导入 ETS 时需提供 DPT。|
74
+ | `getGAValue(GA, DPT?, readIfMissing?)` | 读取 GA 的值,例如 `'1/0/1'` 或 `'1/0/1 Bed table light'`(空格后的文字会被忽略)。导入 ETS 后,DPT 为可选;未导入时则必须提供。默认情况下,如果缓存中没有该值,函数会发送一个 `GroupValue_Read` 并等待响应。若将第三个参数设为 `false`,或者在不需要 DPT 时将第二个参数设为 `false`,则函数只使用缓存;如果本地没有值,则返回 `undefined`。`getGAValue` 是异步的,所以当你需要真实值时,应写成 `await getGAValue(...)`。即使使用只读缓存模式,也要保留 `await`,因为该 helper 始终返回 Promise。|
75
75
  | `setGAValue(GA, value, DPT?)` | 设置 GA 值;DPT 同上。|
76
76
  | `self(value)` | 设置当前节点的值并发送到总线(注意循环)。|
77
77
  | `toggle()` | 切换当前值并发送到总线。|
@@ -81,7 +81,7 @@
81
81
 
82
82
  ```javascript
83
83
 
84
- const statusGA = getGAValue('0/0/09','1.001');
84
+ const statusGA = await getGAValue('0/0/09','1.001');
85
85
  if (msg.payload !== statusGA){ return msg; } else { return; }
86
86
  ```
87
87
 
@@ -97,12 +97,13 @@ return msg;
97
97
 
98
98
  ```javascript
99
99
 
100
- msg.externalTemperature = getGAValue('0/0/10'); // 未导入 ETS:getGAValue('0/0/10','9.001')
100
+ msg.externalTemperature = await getGAValue('0/0/10'); // 未导入 ETS:await getGAValue('0/0/10','9.001')
101
101
  return msg;
102
102
  ```
103
103
 
104
104
  ```javascript
105
- if (msg.payload === false && getGAValue('0/0/11','1.001') === false){ return; } else { return msg; }
105
+ const otherGA = await getGAValue('0/0/11','1.001');
106
+ if (msg.payload === false && otherGA === false){ return; } else { return msg; }
106
107
  ```
107
108
 
108
109
  ### 输入(Inputs)
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=20.18.1"
5
5
  },
6
- "version": "4.3.9",
6
+ "version": "4.3.11",
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/",
@@ -82,7 +82,7 @@
82
82
  "IOT",
83
83
  "philips hue"
84
84
  ],
85
- "author": "Supergiovane",
85
+ "author": "Massimo Saccani (Supergiovane)",
86
86
  "license": "MIT",
87
87
  "scripts": {
88
88
  "build": "npm run knx-ai:build && npm run knx-viewer:build",
@@ -117,4 +117,4 @@
117
117
  "vite": "^7.1.3",
118
118
  "vue": "^3.5.21"
119
119
  }
120
- }
120
+ }