iobroker.openknx 0.4.4 → 0.5.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.
package/README.md CHANGED
@@ -50,14 +50,22 @@ This settings protects the KNX bus from data flooding by limiting data frames to
50
50
  Not sent frames are delay until the delay time since last send on bus is elapsed. If more send requests are waiting, send order is random.
51
51
  If you experience disconnects from your KNX IP Gateway in the log then increase this number.
52
52
 
53
+ ### use common.type boolean for 1 bit enum instead of number
54
+
55
+ Use in IOB Object common.type boolean for 1 bit enum instead of number.
56
+
53
57
  ### readout values of autoread iob objects on startup
54
58
 
55
59
  All IOB objects that are configured with the autoread flag are requested on the bus to be synchronized with IOB.
56
60
 
57
- ### Add only new Objects
61
+ ### import only GAs that do not exist in IOB objects
58
62
 
59
63
  If checked, the import will skip overwriting existing communication objects.
60
64
 
65
+ ### remove existing IOB objects thtat are not in import file
66
+
67
+ To clean up object tree
68
+
61
69
  ### Import XML from ETS
62
70
 
63
71
  ![ETS export](docs/pictures/exportGA.png)
@@ -162,12 +170,14 @@ The whole name including path is used to check for similarity.
162
170
 
163
171
  # howto use the adapter & basic concept
164
172
 
165
- ### ACK flags
173
+ ### ACK flags with tunneling connections
166
174
 
167
- Applications shall never set ack flags, application is notified from this adapter by the ack flag if data is updated.
175
+ Applications shall not set the ack flag, application is notified from this adapter by the ack flag if data is updated.
168
176
  KNX Stack sets the ack flag of the corresponding IoBroker object on receiption of a group address if another knx host writes to the bus.
169
- Sent frames on KNX triggered by application writing to a object does not result into an immediate acknowledgement message to that object.
170
- If the write is coming from this adapter, the the ack flag is generated on postivive confirmance in tunneling mode.
177
+
178
+ |GA is | connected to device with a R flag | connected to devices with no R flag | unconnected
179
+ |Application issues GroupValue_Write | ack | ack | no ack
180
+ |Application issues GroupValue_Read | ack | no ack | no ack
171
181
 
172
182
  ### Node Red complex datatype example
173
183
 
@@ -211,7 +221,7 @@ Autoread is set to false where it is clear from the DPT that this is a trigger s
211
221
  "bitlength": 1, // size ob knx data, derived from dpt
212
222
  "dpt": "DPT1.001", // DPT
213
223
  "encoding": {
214
- // informative
224
+ // values of the interface if it is an enum DPT type
215
225
  "0": "Off",
216
226
  "1": "On"
217
227
  },
@@ -267,7 +277,7 @@ GroupValue_Read comment does not work for javascript adapter. Use qualityAsNumbe
267
277
 
268
278
  | KNX DPT | javascript datatype | special values | value range | remark |
269
279
  | --------- | ---------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------- |
270
- | DPT-1 | boolean | | false, true | |
280
+ | DPT-1 | number enum | | false, true | |
271
281
  | DPT-2 | object | {"priority":1 bit,"data":1 bit} | - | |
272
282
  | DPT-3 | object | {"decr_incr":1 bit,"data":2 bit} | - | |
273
283
  | DPT-18 | object | {"save_recall":0,"scenenumber":0} | - | Datapoint Type DPT_SceneControl removed from autoread |
@@ -359,16 +369,17 @@ Data is sent to Iobroker Sentry server hosted in Germany. If you have allowed io
359
369
  - create joint alias objects that react on status inputs
360
370
  - supports project of all possible group address styles
361
371
 
362
- # Known Problems
363
-
364
- - none
365
-
366
372
  # Limitations
367
373
 
368
374
  - ETS 4 export file format is not supported
369
375
  - KNX secure is not supported
370
376
  - only IPv4 supported
371
377
 
378
+ # FAQ
379
+
380
+ - Autoread trigger actors on the bus to react
381
+ Check in ETS if group objects of certain devices that are connected to the suspicious GA have the R/L flag configured. This should not be the case if te device is a consumer of the signal. If the signal has an event character, a groupValueRead would trigger that event. Change configuration in ETS or disable autoread for this object.
382
+
372
383
  ## Changelog
373
384
 
374
385
  <!--
@@ -377,7 +388,13 @@ Data is sent to Iobroker Sentry server hosted in Germany. If you have allowed io
377
388
  * .... -> this is used by script to generate a new entry, copy after a new release
378
389
  * npm run release major/minor/patch major.minor.patch
379
390
  -->
380
- ### 0.4.4 (2022-12-18)
391
+ ### 0.5.0 (2022-12-30)
392
+
393
+ - feature: use common.type boolean for 1 bit enum instead of number
394
+ import enum with one bit as common.type mixed and not strict as number
395
+ - handling of iob ack improved for tunneling connections, see description
396
+
397
+ ### 0.4.5 (2022-12-19)
381
398
 
382
399
  - bugfix in knx lib: make dpt2 not an enum datatype
383
400
 
@@ -66,6 +66,7 @@
66
66
  if (isAlive && !window.isProcessingRequest) {
67
67
  $("#onlyAddNewObjects").removeClass("disabled");
68
68
  $("#autoreadEnabled").removeClass("disabled");
69
+ $("#useBoolean").removeClass("disabled");
69
70
  $("#removeUnusedObjects").removeClass("disabled");
70
71
  $("#setAckOnWrite").removeClass("disabled");
71
72
  $(".file-field .btn").removeClass("disabled");
@@ -83,6 +84,7 @@
83
84
  if (settings.minimumDelay === undefined) settings.minimumDelay = 50;
84
85
  if (settings.onlyAddNewObjects === undefined) settings.onlyAddNewObjects = false;
85
86
  if (settings.autoreadEnabled === undefined) settings.autoreadEnabled = false;
87
+ if (settings.useBoolean === undefined) settings.useBoolean = false;
86
88
  if (settings.removeUnusedObjects === undefined) settings.removeUnusedObjects = false;
87
89
  if (settings.localInterface === undefined) settings.localInterface = "";
88
90
  if (settings.setAckOnWrite === undefined) settings.setAckOnWrite = false;
@@ -382,12 +384,16 @@
382
384
  <div class="row">
383
385
  <div class="col s12 input-field">
384
386
  <input class="value" id="onlyAddNewObjects" type="checkbox" />
385
- <label class="translate" for="onlyAddNewObjects">import only GAs that do not exist already in IOB objects</label>
387
+ <label class="translate" for="onlyAddNewObjects">import only GAs that do not exist in IOB objects</label>
386
388
  </div>
387
389
  <div class="col s12 input-field">
388
390
  <input class="value" id="removeUnusedObjects" type="checkbox" />
389
391
  <label class="translate" for="removeUnusedObjects">remove existing IOB objects that are not in import file</label>
390
392
  </div>
393
+ <div class="col s12 input-field">
394
+ <input class="value" id="useBoolean" type="checkbox" />
395
+ <label class="translate" for="useBoolean">use common.type boolean for 1 bit enum instead of number</label>
396
+ </div>
391
397
 
392
398
  <div class="col s12 m12 l6 file-field input-field">
393
399
  <div class="btn" id="fileUploadButton"><i class="material-icons right">input</i>
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "openknx",
4
- "version": "0.4.4",
4
+ "version": "0.5.0",
5
5
  "news": {
6
+ "0.5.0": {
7
+ "en": "- feature: use common.type boolean for 1 bit enum instead of number\n import enum with one bit as common.type mixed and not strict as number\n- handling of iob ack improved for tunneling connections, see description",
8
+ "de": "- feature: verwenden sie gemeinsam. typ boolean für 1 bit enum statt anzahl\nimport enum mit ein bisschen wie üblich. typ gemischt und nicht streng als zahl\n- handhabung von iob ack verbessert für tunnelverbindungen, siehe beschreibung",
9
+ "ru": "- функция: использовать общий. тип boolean для 1 бит enum вместо номера\nимпорт enum с одним битом как общий. тип смешанный и не строгий как номер\n- обработка iob ack улучшена для туннелирования соединения, см. описание",
10
+ "pt": "- recurso: use comum. tipo booleano para 1 bit enum em vez de número\nimportação enum com um pouco como comum. tipo misturado e não estrito como número\n- manipulação de iob ack melhorado para conexões de túnel, ver descrição",
11
+ "nl": "gebruik gewoon. typ boolean voor 1 cent in plaats van nummer\nimporteer met een beetje zoals gewoonlijk. gemengd en niet strikt als nummer\n- behandelen met iob ack verbeterd voor tunnelverbindingen",
12
+ "fr": "- caractéristique: utilisation commune. type booléen pour 1 bit enum au lieu du nombre\nimporter enum avec un peu comme commun. type mélangé et non strict\n- le traitement de l'iob ack amélioré pour les connexions de tunnel, voir la description",
13
+ "it": "- caratteristica: utilizzare comune. tipo boolean per 1 bit enum invece del numero\nimportare enum con un bit come comune. tipo misto e non rigoroso come numero\n- la gestione dell'attacco iob migliorata per i collegamenti di tunneling, vedi descrizione",
14
+ "es": "- función: usar común. tipo booleano por 1 bit enum en lugar de número\nimporta enum con un poco como común. tipo mixto y no estricto como número\n- manejo de iob ack mejorado para conexiones de túnel, ver descripción",
15
+ "pl": "wykorzystywanie: wspólne. bulean na 1 bit enum zamiast numeru\nzawartość z jednym bitem jako powszechna. mieszany i nie ścisły\n- obsługa iob udoskonalona dla połączeń tunelowych, zobacz opis iob",
16
+ "uk": "- функція: використання загального. тип булеан для 1 біт енму замість кількості\nімпортувати енму з одним бітом, як поширений. тип змішаних і не строгий як номер\n- обробка iob ack поліпшена для зв'язків з тунелями, див. опис",
17
+ "zh-cn": "- 特征:共同使用。 第1条的诱惑而不是数量\n进口有一处是普通的。 类型混杂不清\n- 对隧道的处理改良了碘包,见说明"
18
+ },
19
+ "0.4.5": {
20
+ "en": "bugfix in knx lib: make dpt2 not an enum datatype",
21
+ "de": "bugfix in knx lib: dpt2 nicht enum datatype",
22
+ "ru": "bugfix в knx lib: сделать dpt2 не enum datatype",
23
+ "pt": "bugfix em knx lib: fazer dpt2 não um tipo de dados enum",
24
+ "nl": "maak dpt2 geen enum datat",
25
+ "fr": "bugfix in knx lib: make dpt2 not an enum datatype",
26
+ "it": "bugfix in knx lib: make dpt2 not an enum datatype",
27
+ "es": "bugfix en knx lib: make dpt2 not an enum datatype",
28
+ "pl": "uszkodzenia w knx lib: czynić dpt2 nie enum datatype",
29
+ "uk": "виправлення помилок в knx lib: зробити dpt2 нетипові дані",
30
+ "zh-cn": "b knx lib的ugfix:d2不含一种全局数据。"
31
+ },
6
32
  "0.4.4": {
7
33
  "en": "bugfix in knx lib: make dpt2 not an enum datatype",
8
34
  "de": "bugfix in knx lib: dpt2 nicht enum datatype",
@@ -67,19 +93,6 @@
67
93
  "pl": "wygląd: wspieranie Free and Two Level Group Address Style, oprócz istniejącego Trzech poziomów, 320\ncechą charakterystyczną mapy knx jest konsekracja obiektu wspólnego. #313\ndebug message",
68
94
  "uk": "функція: підтримка безкоштовного та дворівневого групового стилю, крім існуючої трирівневої підтримки #320\nфункція: кодування типу кнкс для об'єкта спільного. стани #313\ndebug повідомлення для відправки розміру черги",
69
95
  "zh-cn": "特点:除了现有三级支助第320号行动之外,支持自由和二级组处理Style。\n特征:图千x数据点类型与目标一致。 第313号声明\nd. 发出呼吁规模的信息"
70
- },
71
- "0.3.2": {
72
- "en": "feature: sync knx library\nfeature: sync with create adapter 0.2.3\nfeature: update to newer versions of dependant packages\nfeature: setting autoreadEnabled autoread\nbugfix: allow alias generation with missing gateway configuration\nbugfix in knx lib: keep correct order of send datagrams in case of burst write",
73
- "de": "feature: sync knx bibliothek\nfeature: sync mit adapter erstellen 0.2.3\nfeature: update auf neuere versionen von abhängigen paketen\nfunktion: autoread einstellen Autoread aktivieren\nbugfix: alias-generation mit fehlender gateway-konfiguration erlauben\nbugfix in knx lib: korrekte reihenfolge des sendens von datengrammen bei burst-schreiben",
74
- "ru": "функция: синхронизация knx библиотека\nособенность: синхронизация с созданием адаптера 0.2.3\nфункция: обновление до новых версий зависящих пакетов\nфункция: настройка autoread Включенный автопрочитан\nbugfix: позвольте псевдониму генерации с недостающей конфигурацией шлюза\nbugfix в knx lib: держите правильный порядок отправки данных в случае захоронения напишите",
75
- "pt": "característica: biblioteca de sincronização knx\ncaracterística: sincronização com criar adaptador 0.2.3\nrecurso: atualização para versões mais recentes de pacotes dependentes\ncaracterística: definição de autoread Autoread habilitado\nbugfix: permitir a geração de alias com configuração de gateway ausente\nbugfix em knx lib: manter a ordem correta de enviar datagramas em caso de explosão escrever",
76
- "nl": "vertaling:\nvertaling:\nvertaling:\nvertaling: Geautoread\nvertaling:\nbewaar de juiste volgorde van het versturen van gegevens in geval van ontploffing",
77
- "fr": "fonction: bibliothèque sync knx\nfonction: synchronisation avec adaptateur de création 0.2.3\nfonction: mise à jour de nouvelles versions de paquets dépendants\nfonction: réglage automatique Autoread activé\nbugfix: permettre la génération d'alias avec la configuration de passerelle manquante\nbugfix in knx lib: garder l'ordre correct d'envoyer des datagrammes en cas d'éclatement écrire",
78
- "it": "caratteristica: sync knx library\nfunzione: sincronizzare con creare adattatore 0.2.3\nfunzione: aggiornamento alle versioni più recenti dei pacchetti dipendenti\ncaratteristica: impostazione autoread Abilitato autoread\nbugfix: consentire la generazione di alias con la configurazione di gateway mancante\nbugfix in knx lib: mantenere l'ordine corretto di inviare i datagrams in caso di esplosione di scrittura",
79
- "es": "función: biblioteca de knx de sincronización\nfunción: sincronización con el adaptador 0,2.3\nfunción: actualización a nuevas versiones de paquetes dependientes\nfunción: ajuste automático Autoread habilitado\nbugfix: permitir la generación de alias con configuración de entrada perdida\nbugfix en knx lib: mantener el orden correcto de enviar datagramas en caso de explosión escribir",
80
- "pl": "funkcja: sync knx library\nfunkcja: synchronizacja z tworzeniem adaptatora 0.2.3\ndostępne są nowe wersje pakietów zależnych\nustawa Autoready\ntrąbka: pozwalać aliasowi pokolenie z brakami konfiguracji bramy\nuszkodzenia w knx lib: zachować poprawny porządek wysyłania danych w przypadku rozbłysku",
81
- "uk": "русскийукраїнськабеларускаяoʻzbek tilienglish\nфункція: синхронізація з створення адаптера 0.2.3\nфункція: оновлення до нових версій пакетів залежностей\nфункція: налаштування автопродавця Увімкнути автоматичний\nвиправлення помилок: генерація псевдонімів з відсутністю конфігурації шлюзу\nвиправлення помилок в knx lib: зберегти правильний порядок відправки графіків у разі лопу писати",
82
- "zh-cn": "特色:复合克日图书馆\n特征:与创造适应力的结合\n特点:更新依赖一揽子计划的新版本\n特征:建立汽车 便携式汽车\nb ugfix:允许产生离散的门户组合\nb knx lib的ugfix:在开火时保持正确的发送数据图。"
83
96
  }
84
97
  },
85
98
  "title": "Open KNX",
@@ -153,11 +166,12 @@
153
166
  },
154
167
  "native": {
155
168
  "autoreadEnabled": true,
169
+ "useBoolean": true,
156
170
  "gwip": "",
157
171
  "gwipport": "3671",
158
172
  "localInterface": "",
159
173
  "minimumDelay": 50,
160
- "aliassRegexp": "stat(e|us)(\\.|$)|rm(\\.|$)|r(ü|ue)ckmeldung(\\.|$)|\\svalue(\\.|$)",
174
+ "aliassRegexp": "stat(e|us)(\\.|$)|rm(\\.|$)|r(ü|ue)ckmeldung(\\.|$)",
161
175
  "aliasSimilarity": 0.9,
162
176
  "aliasPath": "alias.0",
163
177
  "aliasRange": false
@@ -195,6 +195,7 @@ FSM.prototype.write = function(grpaddr, value, dptid, callback) {
195
195
  KnxLog.get().warn('write: You must supply both grpaddr and value!');
196
196
  return;
197
197
  }
198
+ sendConfNotification(callback, grpaddr, this);
198
199
  try {
199
200
  // outbound request onto the state machine
200
201
  const serviceType = this.useTunneling ?
@@ -203,7 +204,7 @@ FSM.prototype.write = function(grpaddr, value, dptid, callback) {
203
204
  this.Request(serviceType, function(datagram) {
204
205
  DPTLib.populateAPDU(value, datagram.cemi.apdu, dptid);
205
206
  datagram.cemi.dest_addr = grpaddr;
206
- }, callback);
207
+ });
207
208
  } catch (e) {
208
209
  KnxLog.get().warn(e);
209
210
  }
@@ -235,6 +236,7 @@ FSM.prototype.writeRaw = function(grpaddr, value, bitlength, callback) {
235
236
  KnxLog.get().warn('Value must be a buffer!');
236
237
  return;
237
238
  }
239
+ sendConfNotification(callback, grpaddr, this);
238
240
  // outbound request onto the state machine
239
241
  const serviceType = this.useTunneling ?
240
242
  KnxConstants.SERVICE_TYPE.TUNNELING_REQUEST :
@@ -243,7 +245,7 @@ FSM.prototype.writeRaw = function(grpaddr, value, bitlength, callback) {
243
245
  datagram.cemi.apdu.data = value;
244
246
  datagram.cemi.apdu.bitlength = bitlength ? bitlength : (value.byteLength * 8);
245
247
  datagram.cemi.dest_addr = grpaddr;
246
- }, callback);
248
+ });
247
249
  }
248
250
 
249
251
  // send a READ request to the bus
@@ -365,4 +367,27 @@ function Connection(options) {
365
367
  return conn;
366
368
  };
367
369
 
370
+ //notify sending application if timeout or if confirmation received
371
+ function sendConfNotification(callback, grpaddr, fsm) {
372
+ if (typeof callback == 'function') {
373
+ // when the confirmation arrives:
374
+ const responseEvent = 'GroupValue_Write_confirmed_' + grpaddr;
375
+ KnxLog.get().trace('Binding connection to ' + responseEvent);
376
+ const binding = (confirmed) => {
377
+ // unbind the event handler
378
+ fsm.off(responseEvent, binding);
379
+ // fire the callback
380
+ callback(grpaddr, confirmed);
381
+ clearTimeout(fsm.confirmtimer);
382
+ }
383
+ // prepare for the response
384
+ fsm.on(responseEvent, binding);
385
+ // clean up after 3 seconds just in case no one confirms
386
+ fsm.confirmtimer = setTimeout( () => {
387
+ fsm.off(responseEvent, binding);
388
+ callback(grpaddr, false);
389
+ }, 3000);
390
+ }
391
+ }
392
+
368
393
  module.exports = Connection;
@@ -377,10 +377,17 @@ module.exports = machina.Fsm.extend({
377
377
  ['inbound_TUNNELING_REQUEST_L_Data.con'](datagram) {
378
378
  if (this.useTunneling) {
379
379
  const confirmed = this.sentTunnRequests[datagram.cemi.dest_addr] && ! /*cofirmation error*/ datagram.cemi.ctrl.confirm;
380
+ const evtName = datagram.cemi.apdu.apci;
380
381
 
381
382
  delete this.sentTunnRequests[datagram.cemi.dest_addr];
382
383
  this.emit('confirmed', datagram.cemi.dest_addr, confirmed);
383
384
 
385
+ // event name for example GroupValue_Write_1/2/3
386
+ this.emit(
387
+ util.format('%s_confirmed_%s', evtName, datagram.cemi.dest_addr),
388
+ confirmed
389
+ );
390
+
384
391
  KnxLog.get().trace(
385
392
  '(%s): %s %s',
386
393
  this.compositeState(),
@@ -8,7 +8,7 @@ const log = require('log-driver').logger;
8
8
  const custom_truthiness = (value) => {
9
9
  const f = parseFloat(value);
10
10
  return !isNaN(f) && isFinite(f) ?
11
- f == 1.0 : // numeric values (in native and string form) are truthy if NOT zero
11
+ f != 0.0 : // numeric values (in native and string form) are truthy if NOT zero
12
12
  value === true || value === 'true';// non-numeric value truthiness is Boolean true or the string 'true'.
13
13
  };
14
14
 
@@ -36,6 +36,7 @@ exports.subtypes = {
36
36
  use: 'G',
37
37
  name: 'DPT_Switch',
38
38
  desc: 'switch',
39
+ range: [0, 1],
39
40
  enc: { 0: 'Off', 1: 'On' },
40
41
  },
41
42
 
@@ -43,6 +44,7 @@ exports.subtypes = {
43
44
  use: 'G',
44
45
  name: 'DPT_Bool',
45
46
  desc: 'bool',
47
+ range: [0, 1],
46
48
  enc: { 0: 'False', 1: 'True' },
47
49
  },
48
50
 
@@ -50,6 +52,7 @@ exports.subtypes = {
50
52
  use: 'G',
51
53
  name: 'DPT_Enable',
52
54
  desc: 'enable',
55
+ range: [0, 1],
53
56
  enc: { 0: 'Disable', 1: 'Enable' },
54
57
  },
55
58
 
@@ -57,6 +60,7 @@ exports.subtypes = {
57
60
  use: 'FB',
58
61
  name: 'DPT_Ramp',
59
62
  desc: 'ramp',
63
+ range: [0, 1],
60
64
  enc: { 0: 'No ramp', 1: 'Ramp' },
61
65
  },
62
66
 
@@ -64,6 +68,7 @@ exports.subtypes = {
64
68
  use: 'FB',
65
69
  name: 'DPT_Alarm',
66
70
  desc: 'alarm',
71
+ range: [0, 1],
67
72
  enc: { 0: 'No alarm', 1: 'Alarm' },
68
73
  },
69
74
 
@@ -71,6 +76,7 @@ exports.subtypes = {
71
76
  use: 'FB',
72
77
  name: 'DPT_BinaryValue',
73
78
  desc: 'binary value',
79
+ range: [0, 1],
74
80
  enc: { 0: 'Low', 1: 'High' },
75
81
  },
76
82
 
@@ -78,6 +84,7 @@ exports.subtypes = {
78
84
  use: 'FB',
79
85
  name: 'DPT_Step',
80
86
  desc: 'step',
87
+ range: [0, 1],
81
88
  enc: { 0: 'Decrease', 1: 'Increase' },
82
89
  },
83
90
 
@@ -85,6 +92,7 @@ exports.subtypes = {
85
92
  use: 'G',
86
93
  name: 'DPT_UpDown',
87
94
  desc: 'up/down',
95
+ range: [0, 1],
88
96
  enc: { 0: 'Up', 1: 'Down' },
89
97
  },
90
98
 
@@ -92,6 +100,7 @@ exports.subtypes = {
92
100
  use: 'G',
93
101
  name: 'DPT_OpenClose',
94
102
  desc: 'open/close',
103
+ range: [0, 1],
95
104
  enc: { 0: 'Open', 1: 'Close' },
96
105
  },
97
106
 
@@ -99,6 +108,7 @@ exports.subtypes = {
99
108
  use: 'G',
100
109
  name: 'DPT_Start',
101
110
  desc: 'start/stop',
111
+ range: [0, 1],
102
112
  enc: { 0: 'Stop', 1: 'Start' },
103
113
  },
104
114
 
@@ -113,6 +123,7 @@ exports.subtypes = {
113
123
  use: 'FB',
114
124
  name: 'DPT_Invert',
115
125
  desc: 'invert',
126
+ range: [0, 1],
116
127
  enc: { 0: 'Not inverted', 1: 'Inverted' },
117
128
  },
118
129
 
@@ -120,6 +131,7 @@ exports.subtypes = {
120
131
  use: 'FB',
121
132
  name: 'DPT_DimSendStyle',
122
133
  desc: 'dim send style',
134
+ range: [0, 1],
123
135
  enc: { 0: 'Start/stop', 1: 'Cyclically' },
124
136
  },
125
137
 
@@ -127,6 +139,7 @@ exports.subtypes = {
127
139
  use: 'FB',
128
140
  name: 'DPT_InputSource',
129
141
  desc: 'input source',
142
+ range: [0, 1],
130
143
  enc: { 0: 'Fixed', 1: 'Calculated' },
131
144
  },
132
145
 
@@ -134,55 +147,83 @@ exports.subtypes = {
134
147
  use: 'G',
135
148
  name: 'DPT_Reset',
136
149
  desc: 'reset',
137
- enc: { 0: 'no action(dummy)', 1: 'reset command(trigger)' },
150
+ range: [0, 1],
151
+ enc: {
152
+ 0: 'no action(dummy)',
153
+ 1: 'reset command(trigger)'
154
+ },
138
155
  },
139
156
 
140
157
  '016': {
141
158
  use: 'G',
142
159
  name: 'DPT_Ack',
143
160
  desc: 'ack',
144
- enc: { 0: 'no action(dummy)', 1: 'acknowledge command(trigger)' },
161
+ range: [0, 1],
162
+ enc: {
163
+ 0: 'no action(dummy)',
164
+ 1: 'acknowledge command(trigger)' },
145
165
  },
146
166
 
147
167
  '017': {
148
168
  use: 'G',
149
169
  name: 'DPT_Trigger',
150
170
  desc: 'trigger',
151
- enc: { 0: 'trigger', 1: 'trigger' },
171
+ range: [0, 1],
172
+ enc: {
173
+ 0: 'trigger',
174
+ 1: 'trigger'
175
+ },
152
176
  },
153
177
 
154
178
  '018': {
155
179
  use: 'G',
156
180
  name: 'DPT_Occupancy',
157
181
  desc: 'occupancy',
158
- enc: { 0: 'not occupied', 1: 'occupied' },
182
+ range: [0, 1],
183
+ enc: {
184
+ 0: 'not occupied',
185
+ 1: 'occupied'
186
+ },
159
187
  },
160
188
 
161
189
  '019': {
162
190
  use: 'G',
163
191
  name: 'DPT_WindowDoor',
164
192
  desc: 'open window/door',
165
- enc: { 0: 'closed', 1: 'open' },
193
+ range: [0, 1],
194
+ enc: {
195
+ 0: 'closed',
196
+ 1: 'open'
197
+ },
166
198
  },
167
199
 
168
200
  '021': {
169
201
  use: 'FB',
170
202
  name: 'DPT_LogicalFunction',
171
203
  desc: 'and/or',
172
- enc: { 0: 'logical function OR', 1: 'logical function AND' },
204
+ range: [0, 1],
205
+ enc: {
206
+ 0: 'logical function OR',
207
+ 1: 'logical function AND'
208
+ },
173
209
  },
174
210
 
175
211
  '022': {
176
212
  use: 'FB',
177
213
  name: 'DPT_Scene_AB',
178
214
  desc: 'scene A/B',
179
- enc: { 0: 'scene A', 1: 'scene B' },
215
+ range: [0, 1],
216
+ enc: {
217
+ 0: 'scene A',
218
+ 1: 'scene B'
219
+ },
180
220
  },
181
221
 
182
222
  '023': {
183
223
  use: 'FB',
184
224
  name: 'DPT_ShutterBlinds_Mode',
185
225
  desc: 'shutter/blinds mode',
226
+ range: [0, 1],
186
227
  enc: {
187
228
  0: 'only move Up/Down mode (shutter)',
188
229
  1: 'move Up/Down + StepStop mode (blind)',
@@ -193,6 +234,7 @@ exports.subtypes = {
193
234
  use: 'G',
194
235
  name: 'DPT_DayNight',
195
236
  desc: 'Day Night',
237
+ range: [0, 1],
196
238
  enc: {
197
239
  0: 'Day',
198
240
  1: 'Night',
@@ -13,7 +13,8 @@ exports.basetype = {
13
13
  bitlength: 32,
14
14
  signedness: "unsigned",
15
15
  valuetype: "basic",
16
- desc: "4-byte unsigned value"
16
+ desc: "4-byte unsigned value",
17
+ "range" : [0, Math.pow(2, 32)-1]
17
18
  }
18
19
 
19
20
  // DPT12 subtype info
@@ -13,7 +13,8 @@
13
13
  exports.basetype = {
14
14
  bitlength : 8,
15
15
  valuetype : 'basic',
16
- desc : "scene number"
16
+ desc : "scene number",
17
+ "range" : [0, 255]
17
18
  }
18
19
 
19
20
  // DPT17 subtypes
@@ -47,6 +47,7 @@ exports.subtypes = {
47
47
  use: 'G',
48
48
  name: 'DPT_Switch_Control',
49
49
  desc: 'switch with priority',
50
+ range: [0, 3],
50
51
  //enc: { 0: 'Off', 1: 'On' }, binary datatype, not enum
51
52
  },
52
53
  // 2.002 boolean control
@@ -54,6 +55,7 @@ exports.subtypes = {
54
55
  use: 'G',
55
56
  name: 'DPT_Bool_Control',
56
57
  desc: 'boolean with priority',
58
+ range: [0, 3],
57
59
  //enc: { 0: 'false', 1: 'true' },
58
60
  },
59
61
  // 2.003 enable control
@@ -61,6 +63,7 @@ exports.subtypes = {
61
63
  use: 'FB',
62
64
  name: 'DPT_Emable_Control',
63
65
  desc: 'enable with priority',
66
+ range: [0, 3],
64
67
  //enc: { 0: 'Disabled', 1: 'Enabled' },
65
68
  },
66
69
 
@@ -69,6 +72,7 @@ exports.subtypes = {
69
72
  use: 'FB',
70
73
  name: 'DPT_Ramp_Control',
71
74
  desc: 'ramp with priority',
75
+ range: [0, 3],
72
76
  //enc: { 0: 'No ramp', 1: 'Ramp' },
73
77
  },
74
78
 
@@ -77,6 +81,7 @@ exports.subtypes = {
77
81
  use: 'FB',
78
82
  name: 'DPT_Alarm_Control',
79
83
  desc: 'alarm with priority',
84
+ range: [0, 3],
80
85
  //enc: { 0: 'No alarm', 1: 'Alarm' },
81
86
  },
82
87
 
@@ -85,6 +90,7 @@ exports.subtypes = {
85
90
  use: 'FB',
86
91
  name: 'DPT_BinaryValue_Control',
87
92
  desc: 'binary value with priority',
93
+ range: [0, 3],
88
94
  //enc: { 0: 'Off', 1: 'On' },
89
95
  },
90
96
 
@@ -93,6 +99,7 @@ exports.subtypes = {
93
99
  use: 'FB',
94
100
  name: 'DPT_Step_Control',
95
101
  desc: 'step with priority',
102
+ range: [0, 3],
96
103
  //enc: { 0: 'Off', 1: 'On' },
97
104
  },
98
105
 
@@ -101,6 +108,7 @@ exports.subtypes = {
101
108
  use: 'FB',
102
109
  name: 'DPT_Direction1_Control',
103
110
  desc: 'direction 1 with priority',
111
+ range: [0, 3],
104
112
  //enc: { 0: 'Off', 1: 'On' },
105
113
  },
106
114
 
@@ -109,6 +117,7 @@ exports.subtypes = {
109
117
  use: 'FB',
110
118
  name: 'DPT_Direction2_Control',
111
119
  desc: 'direction 2 with priority',
120
+ range: [0, 3],
112
121
  //enc: { 0: 'Off', 1: 'On' },
113
122
  },
114
123
 
@@ -117,6 +126,7 @@ exports.subtypes = {
117
126
  use: 'FB',
118
127
  name: 'DPT_Start_Control',
119
128
  desc: 'start with priority',
129
+ range: [0, 3],
120
130
  //enc: { 0: 'Off', 1: 'On' },
121
131
  },
122
132
 
@@ -125,6 +135,7 @@ exports.subtypes = {
125
135
  use: 'FB',
126
136
  name: 'DPT_State_Control',
127
137
  desc: 'switch',
138
+ range: [0, 3],
128
139
  //enc: { 0: 'Off', 1: 'On' },
129
140
  },
130
141
 
@@ -133,6 +144,7 @@ exports.subtypes = {
133
144
  use: 'FB',
134
145
  name: 'DPT_Invert_Control',
135
146
  desc: 'switch',
147
+ range: [0, 3],
136
148
  //enc: { 0: 'Off', 1: 'On' },
137
149
  },
138
150
  };
@@ -63,18 +63,18 @@ exports.basetype = {
63
63
  exports.subtypes = {
64
64
  // 21.001 status - 5 bits
65
65
  "001" : {
66
- "name" : "DPT_StatusGen",
66
+ "name" : "DPT_StatusGen",
67
67
  "desc" : "General Status",
68
- "unit" : "",
68
+ "unit" : "",
69
69
  "scalar_range" : [ , ],
70
- "range" : [ , ]
71
- },
70
+ "range" : [ , ]
71
+ },
72
72
  // 21.002 control - 3 bits
73
73
  "002" : {
74
- "name" : "DPT_Device_Control",
75
- "desc" : "Device Control",
76
- "unit" : "",
74
+ "name" : "DPT_Device_Control",
75
+ "desc" : "Device Control",
76
+ "unit" : "",
77
77
  "scalar_range" : [ , ],
78
- "range" : [ , ]
79
- }
78
+ "range" : [ , ]
79
+ }
80
80
  }
@@ -41,7 +41,7 @@ exports.basetype = {
41
41
  };
42
42
 
43
43
  exports.subtypes = {
44
- 600: {
44
+ '600': {
45
45
  name: 'RGB',
46
46
  desc: 'RGB color triplet',
47
47
  unit: '',
@@ -43,11 +43,13 @@ exports.subtypes = {
43
43
  '007': {
44
44
  name: 'DPT_Control_Dimming',
45
45
  desc: 'dimming control',
46
+ range: [0, 15],
46
47
  },
47
48
 
48
49
  // 3.008 blind control
49
50
  '008': {
50
51
  name: 'DPT_Control_Blinds',
51
52
  desc: 'blinds control',
53
+ range: [0, 15],
52
54
  },
53
55
  };
@@ -44,6 +44,7 @@ exports.subtypes = {
44
44
  '002': {
45
45
  name: 'DPT_Char_8859_1',
46
46
  desc: 'ISO-8859-1 character (0..255)',
47
+ range: [0, 255],
47
48
  use: 'G',
48
49
  },
49
50
  };
@@ -19,13 +19,15 @@ exports.subtypes = {
19
19
  // 5.001 percentage (0=0..ff=100%)
20
20
  "001" : {
21
21
  "name" : "DPT_Scaling", "desc" : "percent",
22
- "unit" : "%", "scalar_range" : [0, 100]
22
+ "unit" : "%",
23
+ "scalar_range" : [0, 100]
23
24
  },
24
25
 
25
26
  // 5.003 angle (degrees 0=0, ff=360)
26
27
  "003" : {
27
28
  "name" : "DPT_Angle", "desc" : "angle degrees",
28
- "unit" : "°", "scalar_range" : [0, 360]
29
+ "unit" : "°",
30
+ "scalar_range" : [0, 360]
29
31
  },
30
32
 
31
33
  // 5.004 percentage (0..255%)
@@ -10,7 +10,8 @@ exports.basetype = {
10
10
  "bitlength" : 16,
11
11
  "signedness": "unsigned",
12
12
  "valuetype" : "basic",
13
- "desc" : "16-bit unsigned value"
13
+ "desc" : "16-bit unsigned value",
14
+ "range" : [0, 65535]
14
15
  }
15
16
 
16
17
  // DPT subtypes info
@@ -10,6 +10,7 @@ const select = require("xpath");
10
10
  const dom = require("xmldom").DOMParser;
11
11
  const tools = require("./tools.js");
12
12
  const similarity = require("similarity");
13
+ const { basetype } = require("./knx/src/dptlib/dpt1.js");
13
14
 
14
15
  module.exports = {
15
16
  // get xml Files from index.html via adapter.on 'message' and put it into file Obj
@@ -82,7 +83,7 @@ module.exports = {
82
83
  //allowed state, unresolveable dpt means raw interface
83
84
  }
84
85
 
85
- ({ range, type, role } = identifyRangeRoleType(dptObj, range, dpt, type, role));
86
+ ({ range, type, role } = identifyRangeRoleType(adapter, dptObj, range, dpt, type, role));
86
87
 
87
88
  //if dpt is for sure not a status, remove from autoread
88
89
  autoread = !tools.isTriggerDPT(dpt);
@@ -165,7 +166,7 @@ module.exports = {
165
166
  return fullPath + tools.formatGaNameForIob(gaName);
166
167
  }
167
168
 
168
- function identifyRangeRoleType(dptObj, range, dpt, type, role) {
169
+ function identifyRangeRoleType(ad, dptObj, range, dpt, type, role) {
169
170
  // is there a scalar range? eg. DPT5.003 angle degrees (0=0, ff=360)
170
171
  if (dptObj?.subtype?.scalar_range) {
171
172
  range = dptObj.subtype.scalar_range;
@@ -184,7 +185,15 @@ module.exports = {
184
185
  //unknown dpt, range unknown
185
186
  range = [];
186
187
  }
187
- if (dptObj?.subtype?.enc) type = "enum";
188
+
189
+ if (dptObj?.subtype?.enc) {
190
+ if (basetype.bitlength == 1) {
191
+ //treat enum with one bit as mixed
192
+ if (ad.config.useBoolean) {
193
+ type = "boolean";
194
+ } else type = "mixed";
195
+ } else type = "number";
196
+ }
188
197
 
189
198
  //correct range, set type and role based on dpt
190
199
  //obj.common.min is only allowed on obj.common.type "number" or "mixed"
@@ -204,7 +213,7 @@ module.exports = {
204
213
  console.assert(type == "", "object " + type);
205
214
  type = "object";
206
215
  } else if (!!dptObj && !tools.isEmptyObject(dptObj) && dptObj.basetype && dptObj.basetype.bitlength == 1) {
207
- if (type != "enum") type = "boolean";
216
+ if (type != "number") type = "boolean";
208
217
  role = "switch";
209
218
  range[0] = false;
210
219
  range[1] = true;
package/lib/tools.js CHANGED
@@ -103,7 +103,7 @@ async function translateGoogle(text, targetLang) {
103
103
  }
104
104
 
105
105
  /*
106
- * convert dpt into DPT1 or DPT1.001 format, input format is like DPT-275 or DPST-275-100
106
+ * convert dpt into DPT1 or DPT1.001 notation when input is like DPT-1 or DPST-1-001
107
107
  */
108
108
  function formatDpt(dpt) {
109
109
  if (dpt.indexOf("-") != -1) {
@@ -177,6 +177,10 @@ function isDateDPT(dpt) {
177
177
  );
178
178
  }
179
179
 
180
+ function isBitDPT(dpt) {
181
+ return dpt == "DPT1" || dpt.startsWith("DPT1.");
182
+ }
183
+
180
184
  function isFloatDPT(dpt) {
181
185
  return dpt == "DPT9" || dpt.startsWith("DPT9.") || dpt == "DPT14" || dpt.startsWith("DPT14.");
182
186
  }
@@ -227,6 +231,7 @@ module.exports = {
227
231
  formatGaNameForIob,
228
232
  isStringDPT,
229
233
  isDateDPT,
234
+ isBitDPT,
230
235
  isUnknownDPT,
231
236
  isFloatDPT,
232
237
  isTriggerDPT,
package/main.js CHANGED
@@ -19,6 +19,7 @@ const DoubleKeyedMap = require("./lib/doubleKeyedMap.js");
19
19
  const detect = require("./lib/openknx.js");
20
20
  const os = require("os");
21
21
  const exitHook = require("async-exit-hook");
22
+ const { dpt1_control_ga } = require("./lib/knx/test/wiredtests/wiredtest-options.js");
22
23
 
23
24
  class openknx extends utils.Adapter {
24
25
  /**
@@ -336,6 +337,7 @@ class openknx extends utils.Adapter {
336
337
  //keep string, boolean and number
337
338
  ret = val;
338
339
  }
340
+
339
341
  return ret;
340
342
  }
341
343
 
@@ -426,24 +428,39 @@ class openknx extends utils.Adapter {
426
428
 
427
429
  // @ts-ignore
428
430
  if (state.c == "GroupValue_Read" || state.q == 0x10) {
429
- //interface to trigger GrouValue_Read is this comment or null
430
- this.log.debug("Outbound GroupValue_Read to " + ga);
431
- this.knxConnection.read(ga);
431
+ //interface to trigger GrouValue_Read is this object comment or StateQuality 16
432
+ this.log.debug("Outbound GroupValue_Read to GA " + ga);
433
+ this.knxConnection.read(ga, () => {
434
+ //ack is generated with GroupValue_Response
435
+ });
432
436
  return "read";
433
437
  } else if (this.gaList.getDataById(id).common.write) {
434
438
  this.log.debug(
435
- "Outbound GroupValue_Write to " +
436
- ga +
437
- " val: " +
438
- (isRaw ? rawVal : JSON.stringify(knxVal)) +
439
- " from " +
440
- id,
439
+ `Outbound GroupValue_Write to " ${ga} val: ${isRaw ? rawVal : JSON.stringify(knxVal)} from ${id}`,
441
440
  );
442
441
  if (isRaw) {
443
- this.knxConnection.writeRaw(ga, rawVal, () => {});
442
+ this.knxConnection.writeRaw(ga, rawVal, (grpaddr, confirmed) => {
443
+ //l_data.con confirmation set by any receiver connected to the ga
444
+ if (confirmed) {
445
+ //set iob ack when value sent successful on the bus otherwise keep unset
446
+ this.setState(id, {
447
+ ack: true,
448
+ });
449
+ this.log.debug(`GroupValue_Write confirmation ${confirmed} received for ${grpaddr} ${id}`);
450
+ } else this.log.info(`GroupValue_Write confirmation ${confirmed} received for ${grpaddr} ${id}`);
451
+ });
444
452
  return "write raw";
445
453
  } else {
446
- this.knxConnection.write(ga, knxVal, dpt, () => {});
454
+ this.knxConnection.write(ga, knxVal, dpt, (grpaddr, confirmed) => {
455
+ //l_data.con confirmation set by any receiver connected to the ga
456
+ if (confirmed) {
457
+ //set iob ack when value sent successful on the bus otherwise keep unset
458
+ this.setState(id, {
459
+ ack: true,
460
+ });
461
+ this.log.debug(`GroupValue_Write confirmation ${confirmed} received for ${grpaddr} ${id}`);
462
+ } else this.log.info(`GroupValue_Write confirmation ${confirmed} received for ${grpaddr} ${id}`);
463
+ });
447
464
  return "write";
448
465
  }
449
466
  } else {
@@ -493,11 +510,11 @@ class openknx extends utils.Adapter {
493
510
  this.gaList.setDpById(key, datapoint);
494
511
  cnt_withDPT++;
495
512
  this.log.debug(
496
- `Datapoint ${
497
- this.gaList.getDataById(key).native.autoread ? "autoread " : ""
498
- }created and GroupValueWrite sent: ${
499
- this.gaList.getDataById(key).native.address
500
- } ${key}`,
513
+ `Datapoint ${
514
+ this.gaList.getDataById(key).native.autoread
515
+ ? "autoread created and GroupValueRead sent"
516
+ : "created"
517
+ } ${this.gaList.getDataById(key).native.address} ${key}`,
501
518
  );
502
519
  } catch (e) {
503
520
  this.log.error("could not create KNX Datapoint for " + key + " with error: " + e);
@@ -522,21 +539,21 @@ class openknx extends utils.Adapter {
522
539
  this.log.warn(connstatus);
523
540
  },
524
541
 
525
- //l_data.con, confirmation set receiver with set ga s flag
542
+ //l_data.con, confirmation set by a receiver which has the sending flag
526
543
  confirmed: (dest, confirmed) => {
527
544
  for (const id of this.gaList.getIdsByGa(dest)) {
528
- if (confirmed)
529
- //if unconfirmed keep ack at false
530
- this.setState(id, {
531
- ack: true,
532
- });
533
- if (confirmed) this.log.debug(`confirmation true received for ${dest} ${id}`);
534
- else this.log.info(`confirmation false received for ${dest} ${id}`);
545
+ if (confirmed) {
546
+ this.log.debug(`confirmation true received for ${dest} ${id}`);
547
+ } else {
548
+ //otherwise keep unset
549
+ this.log.info(`confirmation false received for ${dest} ${id}`);
550
+ }
535
551
  }
536
552
  },
537
553
 
538
554
  //KNX Bus event received
539
- //src: KnxDeviceAddress, dest: KnxGroupAddress, val: raw value not used, using dp interface instead
555
+ //src: KnxDeviceAddress, dest: KnxGroupAddress,
556
+ //val: raw value not used, using dp interface instead
540
557
  // @ts-ignore
541
558
  event: (
542
559
  /** @type {string} */ evt,
@@ -624,17 +641,7 @@ class openknx extends utils.Adapter {
624
641
  break;
625
642
 
626
643
  default:
627
- this.log.debug(
628
- "received unhandeled event " +
629
- " " +
630
- evt +
631
- " " +
632
- src +
633
- " " +
634
- dest +
635
- " " +
636
- convertedVal,
637
- );
644
+ this.log.debug(`received unhandeled event " ${evt} ${src} ${dest} ${convertedVal}`);
638
645
  ret = "unhandeled";
639
646
  }
640
647
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "ioBroker knx Adapter",
5
5
  "author": "boellner",
6
6
  "homepage": "https://github.com/iobroker-community-adapters/ioBroker.openknx.git",