iobroker.openknx 1.1.11 → 1.1.12

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
@@ -32,7 +32,7 @@ ioBroker adapter for KNX IP communication, powered by [KNXUltimate](https://gith
32
32
  - KNX Secure (IP Secure tunneling via .knxkeys keyfile or password)
33
33
  - Tunneling (UDP/TCP) and Routing (Multicast) protocols
34
34
  - GA-Tools: edit all GA properties (DPT, type, role, read/write flags) with filter and tree view
35
- - Direct Link: connect any ioBroker state to a KNX group address (1:1, trigger, toggle modes)
35
+ - Direct Link: connect any ioBroker state to a KNX group address (1:1, trigger, toggle modes, cyclic send)
36
36
  - Alias generation to merge action and status GAs into a single ioBroker object
37
37
  - Status/Act GA linking without alias mode for migration
38
38
  - Automatic gateway discovery
@@ -52,6 +52,10 @@ ioBroker adapter for KNX IP communication, powered by [KNXUltimate](https://gith
52
52
  Placeholder for the next version (at the beginning of the line):
53
53
  ### **WORK IN PROGRESS**
54
54
  -->
55
+ ### 1.1.12 (2026-05-19)
56
+ - feat: cyclic sending for Direct Links (periodically re-send current value to KNX bus)
57
+ - fix: DPT19 accepts numeric timestamps and ISO strings
58
+
55
59
  ### 1.1.11 (2026-05-08)
56
60
  - fix for UDP interface connection
57
61
 
@@ -65,9 +69,6 @@ ioBroker adapter for KNX IP communication, powered by [KNXUltimate](https://gith
65
69
  - add compatiblity mode für raw DPT5.*
66
70
  - reduce default delay to 25ms
67
71
 
68
- ### 1.1.7 (2026-04-29)
69
- - improve Umlaute handling for DPT-16
70
-
71
72
  ### initial version
72
73
 
73
74
  - initial version from https://www.npmjs.com/package/iobroker.knx/v/0.8.3
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Beliebigen ioBroker-State mit einer KNX-Gruppenadresse verbinden",
7
7
  "Convert": "Konvertierung",
8
8
  "Current Value": "Aktueller Wert",
9
+ "Cyclic send": "Zyklisches Senden",
9
10
  "Direct (1:1)": "Direkt (1:1)",
10
11
  "Direct Access": "Direktzugriff",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Modus",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "Bei jedem EIN-Wert wird der aktuelle KNX-Zustand gelesen und invertiert gesendet. Für Taster, die ein KNX-Licht ein-/ausschalten sollen.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Nur EIN-Werte werden weitergeleitet, AUS wird ignoriert. Für Szenenauslöser oder Türöffner bei denen die Quelle EIN/AUS (Drücken/Loslassen) sendet.",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Aktuellen Wert periodisch auf den KNX-Bus senden. Leer lassen zum Deaktivieren.",
44
46
  "Option A: Aliases": "Option A: Aliase",
45
47
  "Option B: knx Adapter Migration": "Option B: knx-Adapter-Migration",
46
48
  "Physical KNX address": "Physikalische KNX-Adresse",
@@ -7,6 +7,7 @@
7
7
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
8
8
  "Convert": "Convert",
9
9
  "Current Value": "Current Value",
10
+ "Cyclic send": "Cyclic send",
10
11
  "delete all existing KNX objects before import (clean re-import)": "delete all existing KNX objects before import (clean re-import)",
11
12
  "detect KNX gateway": "detect KNX gateway",
12
13
  "Direct (1:1)": "Direct (1:1)",
@@ -60,6 +61,7 @@
60
61
  "no KNX gateway found": "no KNX gateway found",
61
62
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
62
63
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
64
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
63
65
  "Option A: Aliases": "Option A: Aliases",
64
66
  "Option B: knx Adapter Migration": "Option B: knx Adapter Migration",
65
67
  "override object path": "override object path",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Opción A: Alias",
45
47
  "Option B: knx Adapter Migration": "Opción B: migración del adaptador knx",
46
48
  "Physical KNX address": "Dirección física KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Option A : pseudonymes",
45
47
  "Option B: knx Adapter Migration": "Option B : migration de l'adaptateur knx",
46
48
  "Physical KNX address": "Adresse physique KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Opzione A: Alias",
45
47
  "Option B: knx Adapter Migration": "Opzione B: migrazione adattatore knx",
46
48
  "Physical KNX address": "Indirizzo fisico KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Optie A: Aliassen",
45
47
  "Option B: knx Adapter Migration": "Optie B: knx-adaptermigratie",
46
48
  "Physical KNX address": "Fysiek KNX-adres",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Opcja A: Aliasy",
45
47
  "Option B: knx Adapter Migration": "Opcja B: migracja adaptera knx",
46
48
  "Physical KNX address": "Fizyczny adres KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Opção A: Aliases",
45
47
  "Option B: knx Adapter Migration": "Opção B: migração do adaptador knx",
46
48
  "Physical KNX address": "Endereço KNX físico",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Связать любое состояние ioBroker с групповым адресом KNX",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Текущее значение",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Прямой (1:1)",
10
11
  "Direct Access": "Прямой доступ",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Режим",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "При каждом значении ВКЛ текущее состояние KNX считывается и отправляется инвертированное. Для кнопок, которые должны переключать свет KNX.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Только значения ВКЛ передаются, ВЫКЛ игнорируется. Для триггеров сцен или открывателей дверей, где источник отправляет ВКЛ/ВЫКЛ (нажать/отпустить).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Вариант А: псевдонимы",
45
47
  "Option B: knx Adapter Migration": "Вариант Б: миграция адаптера knx",
46
48
  "Physical KNX address": "Физический адрес KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "Варіант A: псевдоніми",
45
47
  "Option B: knx Adapter Migration": "Варіант Б: міграція адаптера knx",
46
48
  "Physical KNX address": "Фізична адреса KNX",
@@ -6,6 +6,7 @@
6
6
  "Connect any ioBroker state to a KNX Group Address": "Connect any ioBroker state to a KNX Group Address",
7
7
  "Convert": "Convert",
8
8
  "Current Value": "Current Value",
9
+ "Cyclic send": "Cyclic send",
9
10
  "Direct (1:1)": "Direct (1:1)",
10
11
  "Direct Access": "Direct Access",
11
12
  "Direct Link": "Direct Link",
@@ -41,6 +42,7 @@
41
42
  "Mode": "Mode",
42
43
  "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.": "On each ON value the current KNX state is read and the inverse is sent. Use for buttons that should toggle a KNX light on/off.",
43
44
  "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).": "Only ON values are forwarded, OFF is ignored. Use for scene triggers or door openers where the source sends ON/OFF (press/release).",
45
+ "Periodically re-send current value to KNX bus. Leave empty to disable.": "Periodically re-send current value to KNX bus. Leave empty to disable.",
44
46
  "Option A: Aliases": "选项 A:别名",
45
47
  "Option B: knx Adapter Migration": "选项 B:knx 适配器迁移",
46
48
  "Physical KNX address": "物理 KNX 地址",
@@ -795,15 +795,23 @@
795
795
  + '</p>';
796
796
  html += '<p><b>' + _('Convert') + ':</b> '
797
797
  + '<input type="text" id="gaLinkConvert" style="display:inline-block;width:200px;margin-left:8px" placeholder="' + _('e.g. !!value, value*100') + '" value="' + (obj.native.linkedStateConvert || '') + '" />'
798
- + ' <a class="waves-effect waves-light btn-small" onclick="gaSaveLinkSettings()">' + _('Save') + '</a>'
799
798
  + '<p style="color:#666;font-size:0.85em;margin-top:2px">' + _('JS expression. Variable: value. Examples: !!value, value*100, value>0?100:0, Math.round(value)') + '</p>'
800
799
  + '</p>';
800
+ html += '<p id="gaLinkCyclicRow"><b>' + _('Cyclic send') + ':</b> '
801
+ + '<input type="number" id="gaLinkCyclic" style="display:inline-block;width:80px;margin-left:8px" step="1" min="1" value="' + (obj.native.linkedStateCyclic || '') + '" />'
802
+ + ' <span style="color:#666;font-size:0.85em">min</span>'
803
+ + '<p style="color:#666;font-size:0.85em;margin-top:2px">' + _('Periodically re-send current value to KNX bus. Leave empty to disable.') + '</p>'
804
+ + '</p>';
805
+ html += '<div style="margin-top:16px"><a class="waves-effect waves-light btn-small" onclick="gaSaveLinkSettings()">' + _('Save') + '</a></div>';
801
806
  html += '<div style="margin-top:24px"><a class="waves-effect waves-light btn-small red" onclick="gaRemoveLink()">' + _('Remove Link') + '</a></div>';
802
807
  $('#gaLinkContent').html(html);
803
808
  $('#gaLinkMode').val(mode);
804
809
  $('#gaLinkModeDesc').text(modeDescs[mode] || '');
810
+ $('#gaLinkCyclicRow').toggle(mode === 'direct');
805
811
  $('#gaLinkMode').on('change', function () {
806
- $('#gaLinkModeDesc').text(modeDescs[$(this).val()] || '');
812
+ var m = $(this).val();
813
+ $('#gaLinkModeDesc').text(modeDescs[m] || '');
814
+ $('#gaLinkCyclicRow').toggle(m === 'direct');
807
815
  gaUpdateMode();
808
816
  });
809
817
  return;
@@ -908,6 +916,9 @@
908
916
  socket.emit('getObject', selectedGaId, function (err, gaObj) {
909
917
  if (err || !gaObj) return;
910
918
  gaObj.native.linkedStateMode = mode;
919
+ if (mode !== 'direct') {
920
+ delete gaObj.native.linkedStateCyclic;
921
+ }
911
922
  socket.emit('setObject', selectedGaId, gaObj, function () {
912
923
  gaObjects[selectedGaId] = gaObj;
913
924
  showToast(null, 'Mode changed to: ' + mode, null, 2000);
@@ -920,6 +931,8 @@
920
931
  var thresholdVal = $('#gaLinkThreshold').val();
921
932
  var threshold = thresholdVal ? parseFloat(thresholdVal) : 0;
922
933
  var convert = $('#gaLinkConvert').val().trim();
934
+ var cyclicVal = $('#gaLinkCyclic').val();
935
+ var cyclic = cyclicVal ? parseInt(cyclicVal, 10) : 0;
923
936
  socket.emit('getObject', selectedGaId, function (err, gaObj) {
924
937
  if (err || !gaObj) return;
925
938
  if (threshold > 0) {
@@ -932,6 +945,11 @@
932
945
  } else {
933
946
  delete gaObj.native.linkedStateConvert;
934
947
  }
948
+ if (cyclic > 0) {
949
+ gaObj.native.linkedStateCyclic = cyclic;
950
+ } else {
951
+ delete gaObj.native.linkedStateCyclic;
952
+ }
935
953
  socket.emit('setObject', selectedGaId, gaObj, function () {
936
954
  gaObjects[selectedGaId] = gaObj;
937
955
  showToast(null, _('Settings saved'), null, 2000);
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "openknx",
4
- "version": "1.1.11",
4
+ "version": "1.1.12",
5
5
  "news": {
6
+ "1.1.12": {
7
+ "en": "feat: cyclic sending for Direct Links (periodically re-send current value to KNX bus)\nfix: DPT19 accepts numeric timestamps and ISO strings",
8
+ "de": "feat: zyklisches Senden für Direkte Links (periodisch wieder aktuellen Wert auf KNX-Bus)\nfix: DPT19 akzeptiert numerische Zeitstempel und ISO-Strings",
9
+ "ru": "подвиг: циклическая отправка для прямых ссылок (периодически пересылать текущее значение на шину KNX)\nисправление: DPT19 принимает числовые временные метки и строки ISO",
10
+ "pt": "feat: envio cíclico para Ligações Diretas (reenviar periodicamente valor atual para barramento KNX)\ncorreção: DPT19 aceita timestamps numéricos e cadeias ISO",
11
+ "nl": "feat: cyclisch verzenden voor Direct Links (periodieke re-send huidige waarde naar KNX bus)\nfix: DPT19 accepteert numerieke tijdstempels en ISO strings",
12
+ "fr": "prouesse : envoi cyclique pour les liaisons directes (réenvoyez périodiquement la valeur actuelle au bus KNX)\ncorrection : DPT19 accepte les horodatages numériques et les chaînes ISO",
13
+ "it": "feat: invio ciclico per Link diretti (periodicamente ri-send valore corrente al bus KNX)\nfix: DPT19 accetta timestamp numerici e stringhe ISO",
14
+ "es": "feat: envío cíclico para enlaces directos (valor actual de reenviar experimentalmente al autobús KNX)\nfijado: DPT19 acepta timetamps numéricos y cadenas ISO",
15
+ "pl": "feat: cyclic sending for Direct Links (okresowo resent current value to KNX bus)\nfix: DPT19 akceptuje numeryczne znaczniki czasu i ciągi ISO",
16
+ "uk": "feat: cyclic відправлення для Direct Links (періодично re-send поточного значення до автобуса KNX)\nфіксатор: DPT19 приймає неоднорідні часові та ISO-струми",
17
+ "zh-cn": "功率: 直接链接的循环发送( 定期将当前值重新发送到 KNX 总线)\n固定: DPT19 接受数字时间戳和 ISO 字符串"
18
+ },
6
19
  "1.1.11": {
7
20
  "en": "fix for UDP interface connection",
8
21
  "de": "Fix für UDP-Interface Verbindung",
@@ -80,19 +93,6 @@
80
93
  "pl": "**breaking:** Komunikacja KNX przełączona na KNXUltimate\n**breaking:** Nazwy właściwości DPT21 zmienione (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), wartości muszą być boolean\n**breaking:** Nazwy właściwości DPT237 zmienione na camelCase\nFeature: Obsługa KNX Secure\nFeature: Natywny import .knxproj (ETS4/5/6, chroniony hasłem) z flagami, wykrywaniem DPT, przypisaniem pomieszczeń\nFeature: Rozszerzone pokrycie DPT (9 dodatkowych DPT, w tym DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Poprawiona stabilność połączenia\nFeature: Ulepszone wykrywanie ról (switch, level, value, text, date) na podstawie typu DPT\nFeature: Direct Link – powiązanie dowolnych stanów ioBroker z adresami grupowymi KNX\nFeature: GA-Tools: wszystkie właściwości GA edytowalne (DPT, typ, rola, flagi) z kompaktowym układem",
81
94
  "uk": "**breaking:** KNX-комунікацію переключено на KNXUltimate\n**breaking:** Назви властивостей DPT21 змінено (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), значення мають бути boolean\n**breaking:** Назви властивостей DPT237 змінено на camelCase\nFeature: Підтримка KNX Secure\nFeature: Нативний імпорт .knxproj (ETS4/5/6, захищений паролем) з прапорцями, визначенням DPT, призначенням кімнат\nFeature: Розширене покриття DPT (9 додаткових DPT, включаючи DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Покращена стабільність з'єднання\nFeature: Покращене визначення ролей (switch, level, value, text, date) на основі типу DPT\nFeature: Direct Link – прив'язка будь-яких станів ioBroker до групових адрес KNX\nFeature: GA-Tools: усі властивості GA редаговані (DPT, тип, роль, прапорці) з компактним макетом",
82
95
  "zh-cn": "**breaking:** KNX通信切换至KNXUltimate\n**breaking:** DPT21属性名已更改(outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck),值必须为boolean\n**breaking:** DPT237属性名已更改为camelCase\nFeature: KNX Secure支持\nFeature: 原生.knxproj导入(ETS4/5/6,支持密码保护)包含标志、DPT推断、房间分配\nFeature: 扩展DPT覆盖范围(新增9个DPT,包括DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: 改善连接稳定性\nFeature: 改进角色检测(switch, level, value, text, date)基于DPT类型\nFeature: Direct Link – 将任意ioBroker状态关联到KNX组地址\nFeature: GA-Tools: 所有GA属性可编辑(DPT、类型、角色、标志)紧凑布局"
83
- },
84
- "0.9.1": {
85
- "en": "bugfix: Fixing increased delay in knx commands after several days\nAdapter requires admin >= 7.7.22 now\nAdapter requires js-controller >= 6.0.11 now\nAdapter requires admin >= 7.6.17 now\n\nAdapter requires node.js >= 20 and js-controller >= 6 now",
86
- "de": "bugfix: Erhöhte Verzögerung in knx-Befehlen nach mehreren Tagen behoben\nAdapter benötigt admin >= 7.7.22 jetzt\nAdapter benötigt jetzt js-controller >= 6.0.11\nAdapter benötigt admin >= 7.6.17 jetzt\n\nAdapter benötigt node.js >= 20 und js-controller >= 6 jetzt",
87
- "ru": "bugfix: Исправление повышенной задержки в командах knx через несколько дней\nАдаптер требует администратора >= 7.7.22\nАдаптер требует js-контроллер >= 6.0.11\nАдаптер требует администратора >= 7.6.17\n\nАдаптер требует node.js >= 20 и js-контроллер >= 6 сейчас",
88
- "pt": "correcção de erros: Corrigindo o atraso aumentado nos comandos do knx após vários dias\nAdaptador requer admin >= 7.7.22 agora\nAdaptador requer js-controlador >= 6.0.11 agora\nAdaptador requer admin >= 7.6.17 agora\n\nAdaptador requer gnodo.js >= 20 e js-controlador >= 6 agora",
89
- "nl": "bugfix: Verhoogde vertraging in knx commando's na meerdere dagen\nAdapter vereist admin < 7.7.22 nu\nAdapter vereist js-controller > 6,0.11 nu\nVoor adapter is admin < 7.6.17 nu nodig\n\nVoor adapters zijn node.js < 20 en js-controller > 20 nodig 6 nu",
90
- "fr": "correction du bug & #160;: Correction d'un retard accru dans les commandes knx après plusieurs jours\nAdaptateur nécessite admin >= 7.7.22 maintenant\nAdaptateur nécessite js-controller >= 6.0.11 maintenant\nAdaptateur nécessite admin >= 7.6.17 maintenant\n\nAdaptateur nécessite node.js >= 20 et js-controller >= 6 maintenant",
91
- "it": "bugfix: Risolvere il ritardo aumentato nei comandi knx dopo diversi giorni\nAdattatore richiede admin >= 7.7.22 ora\nAdattatore richiede js-controller >= 6.0.11 ora\nAdattatore richiede admin >= 7.6.17 ora\n\nAdattatore richiede node.js >= 20 e js-controller >= 6 ora",
92
- "es": "bugfix: Fijar un mayor retraso en comandos knx después de varios días\nEl adaptador requiere admin= 7.7.22 ahora\nAdaptador requiere js-controller >= 6.0.11\nAdaptador requiere administración= 7.6.17 ahora\n\nAdaptador requiere node.js √≥= 20 y js-controller 6 ahora",
93
- "pl": "bugfix: Ustawienie zwiększonego opóźnienia w komendach knx po kilku dniach\nAdapter wymaga admin > = 7.7.22\nAdapter wymaga sterownika js- > = 6.0.11\nAdapter wymaga admin > = 7.6.17\n\nAdapter wymaga node.js > = 20 i kontroler js- > = 6 teraz",
94
- "uk": "виправлення помилок: Виправлення підвищеної затримки в командах knx після декількох днів\nАдаптер вимагає адмін >= 7.7.22 тепер\nАдаптер вимагає js-controller >=6.0.11 тепер\nАдаптер вимагає адмін >= 7.6.17 тепер\n\nАдаптер вимагає node.js >= 20 і js-controller >= 6 тепер",
95
- "zh-cn": "错误修正 : 几天后修复 knx 命令中增加的延迟\n适任者需要管理员 \\ 7.7.22 现在\n适配器需要js控制器 现在6.0.11\n适任者需要行政主管 现在7.6.17\n\n适配器需要节点.js QQ 20和js控制器 QQ 现在6个"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/main.js CHANGED
@@ -49,6 +49,9 @@ class openknx extends utils.Adapter {
49
49
  this.reconnectTimer = undefined;
50
50
  this.stopping = false;
51
51
  this.linkedStateMap = {}; // foreignStateId → knxObjectId (reverse lookup for Direct Link)
52
+ this.cyclicTimer = undefined;
53
+ this.cyclicLastSent = new Map(); // knxObjectId → timestamp (ms)
54
+ this.cyclicTimeouts = [];
52
55
 
53
56
  // redirect log from KNXUltimate (winston-based logStream) to adapter log
54
57
  // Collapse multiline messages (stack traces) into a single line
@@ -118,6 +121,7 @@ class openknx extends utils.Adapter {
118
121
  clearInterval(this.interval1);
119
122
  clearInterval(this.autoreadTimer);
120
123
  clearTimeout(this.reconnectTimer);
124
+ clearInterval(this.cyclicTimer);
121
125
  this.stopping = true;
122
126
  this.connected = false;
123
127
 
@@ -686,6 +690,7 @@ class openknx extends utils.Adapter {
686
690
  );
687
691
  try {
688
692
  this.knxConnection.write(gaData.native.address, writeVal, gaData.native.dpt);
693
+ this.cyclicLastSent.set(linkedKnxId, Date.now());
689
694
  } catch (e) {
690
695
  this.log.warn(`Direct Link write failed for ${gaData.native.address}: ${e.message}`);
691
696
  }
@@ -868,6 +873,11 @@ class openknx extends utils.Adapter {
868
873
  const oldLinked = oldData.native?.linkedState;
869
874
  const newLinked = obj?.native?.linkedState;
870
875
 
876
+ // Always update gaList with latest object data (e.g. linkedStateCyclic, threshold, convert)
877
+ if (obj) {
878
+ this.gaList.set(id, obj.native.address, obj);
879
+ }
880
+
871
881
  if (oldLinked === newLinked) {
872
882
  return;
873
883
  }
@@ -876,6 +886,7 @@ class openknx extends utils.Adapter {
876
886
  if (oldLinked) {
877
887
  await this.unsubscribeForeignStatesAsync(oldLinked);
878
888
  delete this.linkedStateMap[oldLinked];
889
+ this.cyclicLastSent.delete(id);
879
890
  this.log.info(`Direct Link removed: ${id} ↔ ${oldLinked}`);
880
891
  }
881
892
 
@@ -885,11 +896,6 @@ class openknx extends utils.Adapter {
885
896
  this.linkedStateMap[newLinked] = id;
886
897
  this.log.info(`Direct Link added: ${id} ↔ ${newLinked}`);
887
898
  }
888
-
889
- // Update gaList with new object data
890
- if (obj) {
891
- this.gaList.set(id, obj.native.address, obj);
892
- }
893
899
  }
894
900
 
895
901
  /**
@@ -906,6 +912,7 @@ class openknx extends utils.Adapter {
906
912
  const gaData = this.gaList.getDataById(knxId);
907
913
  if (gaData?.native?.address && gaData?.native?.dpt) {
908
914
  this.knxConnection.write(gaData.native.address, foreignState.val, gaData.native.dpt);
915
+ this.cyclicLastSent.set(knxId, Date.now());
909
916
  this.log.debug(`Direct Link sync: ${foreignId}=${foreignState.val} → ${gaData.native.address}`);
910
917
  }
911
918
  }
@@ -915,6 +922,82 @@ class openknx extends utils.Adapter {
915
922
  }
916
923
  }
917
924
 
925
+ startCyclicSending() {
926
+ this.stopCyclicSending();
927
+ const CHECK_INTERVAL_MS = 30 * 1000;
928
+
929
+ this.cyclicTimer = setInterval(async () => {
930
+ if (!this.connected || !this.knxConnection) {
931
+ return;
932
+ }
933
+
934
+ const now = Date.now();
935
+ const toSend = [];
936
+
937
+ for (const [foreignId, knxId] of Object.entries(this.linkedStateMap)) {
938
+ const gaData = this.gaList.getDataById(knxId);
939
+ if (!gaData?.native?.linkedStateCyclic || gaData.native.linkedStateCyclic <= 0) {
940
+ continue;
941
+ }
942
+ const mode = gaData.native.linkedStateMode || "direct";
943
+ if (mode !== "direct") {
944
+ continue;
945
+ }
946
+ const intervalMs = gaData.native.linkedStateCyclic * 60 * 1000;
947
+ const lastSent = this.cyclicLastSent.get(knxId) || 0;
948
+ if (now - lastSent >= intervalMs) {
949
+ toSend.push({ foreignId, knxId, gaData });
950
+ }
951
+ }
952
+
953
+ this.cyclicTimeouts = [];
954
+ for (let i = 0; i < toSend.length; i++) {
955
+ const { foreignId, knxId, gaData } = toSend[i];
956
+ this.cyclicLastSent.set(knxId, now);
957
+ const t = setTimeout(async () => {
958
+ if (!this.connected || !this.knxConnection) {
959
+ return;
960
+ }
961
+ try {
962
+ const foreignState = await this.getForeignStateAsync(foreignId);
963
+ if (foreignState?.val == null) {
964
+ return;
965
+ }
966
+ let writeVal = foreignState.val;
967
+ if (gaData.native.linkedStateConvert) {
968
+ try {
969
+ writeVal = new Function("value", `return ${gaData.native.linkedStateConvert}`)(
970
+ writeVal,
971
+ );
972
+ } catch (e) {
973
+ this.log.warn(`Cyclic send convert error for ${gaData.native.address}: ${e.message}`);
974
+ return;
975
+ }
976
+ }
977
+ this.knxConnection.write(gaData.native.address, writeVal, gaData.native.dpt);
978
+ this.log.debug(
979
+ `Cyclic send: ${foreignId}=${JSON.stringify(writeVal)} → ${gaData.native.address}`,
980
+ );
981
+ } catch (e) {
982
+ this.log.warn(`Cyclic send failed for ${gaData.native.address}: ${e.message}`);
983
+ }
984
+ }, i * 200);
985
+ this.cyclicTimeouts.push(t);
986
+ }
987
+ }, CHECK_INTERVAL_MS);
988
+ }
989
+
990
+ stopCyclicSending() {
991
+ if (this.cyclicTimer) {
992
+ clearInterval(this.cyclicTimer);
993
+ this.cyclicTimer = undefined;
994
+ }
995
+ for (const t of this.cyclicTimeouts) {
996
+ clearTimeout(t);
997
+ }
998
+ this.cyclicTimeouts = [];
999
+ }
1000
+
918
1001
  // Reconnect delays in seconds: 10, 30, 60, 120, 120, 120, 120
919
1002
  static reconnectDelays = [10, 30, 60, 120, 120, 120, 120];
920
1003
 
@@ -971,13 +1054,15 @@ class openknx extends utils.Adapter {
971
1054
  if (this.config.localInterface) {
972
1055
  const interfaces = require("os").networkInterfaces();
973
1056
  for (const [name, addrs] of Object.entries(interfaces)) {
974
- if (addrs && addrs.some((a) => a.address === this.config.localInterface)) {
1057
+ if (addrs && addrs.some(a => a.address === this.config.localInterface)) {
975
1058
  ifaceName = name;
976
1059
  break;
977
1060
  }
978
1061
  }
979
1062
  if (!ifaceName) {
980
- this.log.warn(`Configured local interface IP ${this.config.localInterface} not found on this system. Using auto-detection.`);
1063
+ this.log.warn(
1064
+ `Configured local interface IP ${this.config.localInterface} not found on this system. Using auto-detection.`,
1065
+ );
981
1066
  }
982
1067
  }
983
1068
 
@@ -1123,6 +1208,7 @@ class openknx extends utils.Adapter {
1123
1208
 
1124
1209
  // Direct Link: Sync linked foreign state values to KNX bus
1125
1210
  this.syncLinkedStates();
1211
+ this.startCyclicSending();
1126
1212
  });
1127
1213
 
1128
1214
  // Event: disconnected
@@ -1133,6 +1219,7 @@ class openknx extends utils.Adapter {
1133
1219
  this.connected = false;
1134
1220
  this.setState("info.connection", this.connected, true);
1135
1221
  this.setState("info.busload", 0, true);
1222
+ this.stopCyclicSending();
1136
1223
  this.scheduleReconnect();
1137
1224
  });
1138
1225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "1.1.11",
3
+ "version": "1.1.12",
4
4
  "description": "ioBroker knx Adapter",
5
5
  "author": "boellner",
6
6
  "contributors": [
@@ -38,20 +38,20 @@
38
38
  "@xmldom/xmldom": "^0.9.10",
39
39
  "@zip.js/zip.js": "^2.8.26",
40
40
  "ipaddr.js": "^2.4.0",
41
- "knxultimate": "^5.5.6",
41
+ "knxultimate": "^5.5.7",
42
42
  "sax": "^1.6.0",
43
43
  "xpath": "^0.0.34"
44
44
  },
45
45
  "devDependencies": {
46
- "@alcalzone/release-script": "^5.1.1",
47
- "@alcalzone/release-script-plugin-iobroker": "^5.1.2",
48
- "@alcalzone/release-script-plugin-license": "^5.1.1",
49
- "@alcalzone/release-script-plugin-manual-review": "^5.1.1",
46
+ "@alcalzone/release-script": "^5.2.0",
47
+ "@alcalzone/release-script-plugin-iobroker": "^5.2.0",
48
+ "@alcalzone/release-script-plugin-license": "^5.2.0",
49
+ "@alcalzone/release-script-plugin-manual-review": "^5.2.0",
50
50
  "@iobroker/adapter-dev": "^1.5.0",
51
- "@iobroker/eslint-config": "^2.2.0",
51
+ "@iobroker/eslint-config": "^2.3.4",
52
52
  "@iobroker/testing": "^5.2.2",
53
53
  "@tsconfig/node20": "^20.1.9",
54
- "@types/node": "^25.6.0",
54
+ "@types/node": "^25.9.0",
55
55
  "typescript": "~5.9.3"
56
56
  },
57
57
  "main": "main.js",