iobroker.openknx 1.1.0 → 1.1.2

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
@@ -49,7 +49,7 @@ ioBroker adapter for KNX IP communication, powered by [KNXUltimate](https://gith
49
49
  Placeholder for the next version (at the beginning of the line):
50
50
  ### **WORK IN PROGRESS**
51
51
  -->
52
- ### 1.1.0 (2026-03-17)
52
+ ### 1.1.2 (2026-03-22)
53
53
 
54
54
  - (TA2k) **breaking:** KNX communication switched to KNXUltimate
55
55
  - (TA2k) **breaking:** DPT21 property names changed (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), values must be boolean
@@ -60,6 +60,7 @@ ioBroker adapter for KNX IP communication, powered by [KNXUltimate](https://gith
60
60
  - (TA2k) feature: Improved connection stability
61
61
  - (TA2k) feature: Improved role detection (switch, level, value, text, date) based on DPT type
62
62
  - (TA2k) feature: Direct Link all iobroker states to a KNX state with a conversion mode
63
+ - (TA2k) feature: GA-Tools: all GA properties editable (DPT, type, role, flags) with compact layout
63
64
 
64
65
  ### 0.9.1 (2026-03-12)
65
66
  - bugfix: Fixing increased delay in knx commands after several days
@@ -517,6 +517,7 @@
517
517
  var selectedGaId = null;
518
518
  var gaNamespace = '';
519
519
  var gaStateSubscribed = false;
520
+ var cachedDptList = null;
520
521
 
521
522
  function loadGaTree() {
522
523
  var targetNs = ($('#targetNamespace').val() || '').trim();
@@ -627,28 +628,56 @@
627
628
  if (!obj) { $('#gaPropContent').html('<p style="color:#999">GA not found</p>'); return; }
628
629
  var n = obj.native || {};
629
630
  var c = obj.common || {};
630
- var rows = [];
631
- rows.push(['Name', c.name || '']);
632
- rows.push(['Address', n.address || '']);
633
- rows.push(['DPT', n.dpt || '']);
634
- rows.push(['Type', c.type || '']);
635
- if (n.desc) rows.push(['Description', n.desc]);
636
- rows.push(['Read', c.read ? 'yes' : 'no']);
637
- rows.push(['Write', c.write ? 'yes' : 'no']);
638
- rows.push(['Autoread', n.autoread ? 'yes' : 'no']);
639
- rows.push(['Response', n.answer_groupValueResponse ? 'yes' : 'no']);
640
- if (n.bitlength != null) rows.push(['Bitlength', n.bitlength]);
641
- if (n.valuetype) rows.push(['Valuetype', n.valuetype]);
642
- if (n.encoding) rows.push(['Encoding', JSON.stringify(n.encoding)]);
643
- if (n.min != null) rows.push(['Min', n.min]);
644
- if (n.max != null) rows.push(['Max', n.max]);
645
- if (n.linkedState) rows.push(['Linked State', n.linkedState + ' (' + (n.linkedStateMode || 'direct') + ')']);
646
- rows.push(['Object ID', '<span style="font-size:0.8em;word-break:break-all">' + id + '</span>']);
647
- var html = '<table class="ga-prop-table">';
648
- for (var i = 0; i < rows.length; i++) {
649
- html += '<tr><td>' + rows[i][0] + '</td><td>' + rows[i][1] + '</td></tr>';
631
+ // GA Name + Address in one line
632
+ var html = '<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px">';
633
+ html += '<span style="font-size:1.3em;font-weight:500">' + (c.name || '') + '</span>';
634
+ html += '<span style="font-size:1.3em;font-weight:500;color:#666">' + (n.address || '') + '</span>';
635
+ html += '</div>';
636
+ html += '<div style="margin-bottom:4px;font-size:0.95em;color:#888;word-break:break-all">' + id + '</div>';
637
+ // Editable checkboxes (2x2 grid)
638
+ html += '<div style="display:grid;grid-template-columns:1fr 1fr;gap:2px 12px;margin-bottom:8px">';
639
+ html += '<label style="margin:0;font-size:12px"><input type="checkbox" id="gaWriteChk"' + (c.write ? ' checked' : '') + ' onchange="gaSaveCheckboxProp(\'common\',\'write\',\'gaWriteChk\')"/> <span style="font-size:12px">' + _('Write') + ' (write)</span></label>';
640
+ html += '<label style="margin:0;font-size:12px"><input type="checkbox" id="gaReadChk"' + (c.read ? ' checked' : '') + ' onchange="gaSaveCheckboxProp(\'common\',\'read\',\'gaReadChk\')"/> <span style="font-size:12px">' + _('Read') + ' (read)</span></label>';
641
+ html += '<label style="margin:0;font-size:12px"><input type="checkbox" id="gaAutoreadChk"' + (n.autoread ? ' checked' : '') + ' onchange="gaSaveCheckboxProp(\'native\',\'autoread\',\'gaAutoreadChk\')"/> <span style="font-size:12px">' + _('Autoread') + ' (autoread)</span></label>';
642
+ html += '<label style="margin:0;font-size:12px"><input type="checkbox" id="gaResponseChk"' + (n.answer_groupValueResponse ? ' checked' : '') + ' onchange="gaSaveCheckboxProp(\'native\',\'answer_groupValueResponse\',\'gaResponseChk\')"/> <span style="font-size:12px">' + _('Response') + ' (response)</span></label>';
643
+ html += '</div>';
644
+ // KNX DPT alone
645
+ html += '<div style="margin-bottom:8px">';
646
+ html += '<label style="font-size:0.85em">KNX DPT</label>';
647
+ html += '<select id="gaDptSelect" style="display:block;width:100%;margin:0" onchange="gaSaveDpt()"><option value="">...</option></select>';
648
+ html += '</div>';
649
+ // ioBroker Type + Role side by side
650
+ var typeOptions = ['boolean', 'number', 'string', 'object', 'mixed'];
651
+ html += '<div style="display:flex;gap:8px;margin-bottom:8px">';
652
+ html += '<div style="flex:1">';
653
+ html += '<label style="font-size:0.85em">ioBroker Type</label>';
654
+ html += '<select id="gaTypeSelect" style="display:block;width:100%;margin:0" onchange="gaSaveCommonProp(\'type\',\'gaTypeSelect\')">';
655
+ for (var t = 0; t < typeOptions.length; t++) {
656
+ html += '<option value="' + typeOptions[t] + '"' + ((c.type === typeOptions[t]) ? ' selected' : '') + '>' + typeOptions[t] + '</option>';
657
+ }
658
+ html += '</select>';
659
+ html += '</div>';
660
+ html += '<div style="flex:1">';
661
+ html += '<label style="font-size:0.85em">ioBroker Role</label>';
662
+ html += '<select id="gaRoleSelect" style="display:block;width:100%;margin:0" onchange="gaSaveCommonProp(\'role\',\'gaRoleSelect\')"><option value="">...</option></select>';
663
+ html += '</div>';
664
+ html += '</div>';
665
+ // Collapsible details
666
+ var detailRows = [];
667
+ if (n.desc) detailRows.push(['Description', n.desc]);
668
+ if (n.bitlength != null) detailRows.push(['Bitlength', n.bitlength]);
669
+ if (n.valuetype) detailRows.push(['Valuetype', n.valuetype]);
670
+ if (n.encoding) detailRows.push(['Encoding', JSON.stringify(n.encoding)]);
671
+ if (n.min != null) detailRows.push(['Min', n.min]);
672
+ if (n.max != null) detailRows.push(['Max', n.max]);
673
+ if (detailRows.length > 0) {
674
+ html += '<details style="margin-bottom:8px;font-size:0.95em"><summary style="cursor:pointer;color:#666">Details</summary>';
675
+ html += '<table class="ga-prop-table">';
676
+ for (var i = 0; i < detailRows.length; i++) {
677
+ html += '<tr><td>' + detailRows[i][0] + '</td><td>' + detailRows[i][1] + '</td></tr>';
678
+ }
679
+ html += '</table></details>';
650
680
  }
651
- html += '</table>';
652
681
  // Editable Status GA / Act GA
653
682
  html += '<div style="margin-top:8px">';
654
683
  html += '<label style="font-size:0.85em">Status GA</label>';
@@ -665,6 +694,34 @@
665
694
  html += '</div>';
666
695
  html += '</div>';
667
696
  $('#gaPropContent').html(html);
697
+ // Populate DPT dropdown async
698
+ gaLoadDptList(function (list) {
699
+ var sel = $('#gaDptSelect');
700
+ var currentDpt = n.dpt || '';
701
+ var optHtml = '<option value="">...</option>';
702
+ for (var j = 0; j < list.length; j++) {
703
+ var selected = (list[j].value === currentDpt) ? ' selected' : '';
704
+ optHtml += '<option value="' + list[j].value + '"' + selected + '>' + list[j].label + '</option>';
705
+ }
706
+ sel.html(optHtml);
707
+ });
708
+ // Populate Role dropdown async (filtered by read/write)
709
+ var rw = (c.read && c.write) ? 'both' : (c.read ? 'read' : (c.write ? 'write' : 'both'));
710
+ gaLoadRoleList(function (roles) {
711
+ var sel = $('#gaRoleSelect');
712
+ var currentRole = c.role || 'state';
713
+ var optHtml = '';
714
+ var roleFound = false;
715
+ for (var j = 0; j < roles.length; j++) {
716
+ var selected = (roles[j] === currentRole) ? ' selected' : '';
717
+ if (selected) roleFound = true;
718
+ optHtml += '<option value="' + roles[j] + '"' + selected + '>' + roles[j] + '</option>';
719
+ }
720
+ if (!roleFound) {
721
+ optHtml = '<option value="' + currentRole + '" selected>' + currentRole + '</option>' + optHtml;
722
+ }
723
+ sel.html(optHtml);
724
+ }, rw);
668
725
  }
669
726
 
670
727
  function updateGaAccess(id) {
@@ -885,6 +942,99 @@
885
942
  });
886
943
  }
887
944
 
945
+ function gaSaveCheckboxProp(objPath, prop, checkboxId) {
946
+ if (!selectedGaId) return;
947
+ var val = $('#' + checkboxId).is(':checked');
948
+ socket.emit('getObject', selectedGaId, function (err, gaObj) {
949
+ if (err || !gaObj) return;
950
+ gaObj[objPath] = gaObj[objPath] || {};
951
+ gaObj[objPath][prop] = val;
952
+ socket.emit('setObject', selectedGaId, gaObj, function () {
953
+ gaObjects[selectedGaId] = gaObj;
954
+ showToast(null, prop + ' saved', null, 2000);
955
+ if (prop === 'read' || prop === 'write') refreshRoleDropdown();
956
+ });
957
+ });
958
+ }
959
+
960
+ function gaSaveCommonProp(prop, selectId) {
961
+ if (!selectedGaId) return;
962
+ var val = ($('#' + selectId).val() || '').trim();
963
+ socket.emit('getObject', selectedGaId, function (err, gaObj) {
964
+ if (err || !gaObj) return;
965
+ gaObj.common = gaObj.common || {};
966
+ gaObj.common[prop] = val;
967
+ socket.emit('setObject', selectedGaId, gaObj, function () {
968
+ gaObjects[selectedGaId] = gaObj;
969
+ showToast(null, prop + ' saved', null, 2000);
970
+ });
971
+ });
972
+ }
973
+
974
+ function gaSaveDpt() {
975
+ if (!selectedGaId) return;
976
+ var val = ($('#gaDptSelect').val() || '').trim();
977
+ if (!val) return;
978
+ socket.emit('getObject', selectedGaId, function (err, gaObj) {
979
+ if (err || !gaObj) return;
980
+ gaObj.native.dpt = val;
981
+ socket.emit('setObject', selectedGaId, gaObj, function () {
982
+ gaObjects[selectedGaId] = gaObj;
983
+ showToast(null, 'DPT saved', null, 2000);
984
+ updateGaProperties(selectedGaId);
985
+ });
986
+ });
987
+ }
988
+
989
+ function gaLoadDptList(callback) {
990
+ if (cachedDptList) { callback(cachedDptList); return; }
991
+ sendTo(adapter + '.' + instance, 'getDptList', {}, function (list) {
992
+ if (list && Array.isArray(list)) {
993
+ cachedDptList = list;
994
+ } else {
995
+ cachedDptList = [];
996
+ }
997
+ callback(cachedDptList);
998
+ });
999
+ }
1000
+
1001
+ var cachedRolesData = null;
1002
+ function gaLoadRoleList(callback, readWrite) {
1003
+ function filter(data) {
1004
+ if (Array.isArray(data)) return data; // legacy flat array
1005
+ if (readWrite === 'read') return data.read.concat(data.both);
1006
+ if (readWrite === 'write') return data.write.concat(data.both);
1007
+ return data.read.concat(data.write).concat(data.both);
1008
+ }
1009
+ if (cachedRolesData) { callback(filter(cachedRolesData)); return; }
1010
+ $.getJSON('roles.json', function (data) {
1011
+ cachedRolesData = data;
1012
+ callback(filter(data));
1013
+ }).fail(function () {
1014
+ cachedRolesData = ['state', 'switch', 'value', 'level', 'indicator', 'sensor', 'text', 'button', 'date', 'json'];
1015
+ callback(cachedRolesData);
1016
+ });
1017
+ }
1018
+
1019
+ function refreshRoleDropdown() {
1020
+ var r = $('#gaReadChk').is(':checked'), w = $('#gaWriteChk').is(':checked');
1021
+ var rw = (r && w) ? 'both' : (r ? 'read' : (w ? 'write' : 'both'));
1022
+ var currentRole = $('#gaRoleSelect').val() || '';
1023
+ gaLoadRoleList(function (roles) {
1024
+ var optHtml = '';
1025
+ var roleFound = false;
1026
+ for (var j = 0; j < roles.length; j++) {
1027
+ var selected = (roles[j] === currentRole) ? ' selected' : '';
1028
+ if (selected) roleFound = true;
1029
+ optHtml += '<option value="' + roles[j] + '"' + selected + '>' + roles[j] + '</option>';
1030
+ }
1031
+ if (!roleFound && currentRole) {
1032
+ optHtml = '<option value="' + currentRole + '" selected>' + currentRole + '</option>' + optHtml;
1033
+ }
1034
+ $('#gaRoleSelect').html(optHtml);
1035
+ }, rw);
1036
+ }
1037
+
888
1038
  function gaBrowseStates() {
889
1039
  var current = ($('#gaLinkInput').val() || '').trim();
890
1040
  showSelectIdDialog(current, function (newId) {
@@ -898,18 +1048,26 @@
898
1048
  function filterGaTree() {
899
1049
  var filter = ($('#gaFilter').val() || '').toLowerCase();
900
1050
  if (!filter) {
1051
+ // Reset: show all, collapse all folders
901
1052
  $('.ga-leaf, .ga-folder').show();
1053
+ $('.ga-children').addClass('collapsed');
1054
+ $('.ga-folder > span > i').text('chevron_right');
902
1055
  return;
903
1056
  }
1057
+ // First expand all so :visible checks work correctly
1058
+ $('.ga-children').removeClass('collapsed');
1059
+ // Filter leaves
904
1060
  $('.ga-leaf').each(function () {
905
1061
  var text = $(this).text().toLowerCase();
906
1062
  $(this).toggle(text.indexOf(filter) !== -1);
907
1063
  });
908
- // Show folders that have visible children
909
- $('.ga-folder').each(function () {
1064
+ // Hide folders without visible leaves, process innermost first
1065
+ $($('.ga-folder').get().reverse()).each(function () {
910
1066
  var hasVisible = $(this).find('.ga-leaf:visible').length > 0;
911
1067
  $(this).toggle(hasVisible);
912
- if (hasVisible) $(this).find('> .ga-children').removeClass('collapsed');
1068
+ if (hasVisible) {
1069
+ $(this).find('> span > i').text('expand_more');
1070
+ }
913
1071
  });
914
1072
  }
915
1073
  </script>
@@ -0,0 +1,109 @@
1
+ {
2
+ "read": [
3
+ "value", "value.temperature", "value.temperature.dewpoint", "value.temperature.feelslike",
4
+ "value.temperature.max", "value.temperature.min", "value.temperature.windchill",
5
+ "value.humidity", "value.co2", "value.brightness",
6
+ "value.min", "value.max", "value.default",
7
+ "value.battery", "value.valve", "value.time", "value.timer", "value.interval",
8
+ "value.gps.longitude", "value.gps.latitude", "value.gps.elevation", "value.gps", "value.gps.accuracy", "value.gps.radius",
9
+ "value.energy", "value.energy.active", "value.energy.reactive", "value.energy.consumed", "value.energy.produced",
10
+ "value.power", "value.power.active", "value.power.reactive", "value.power.consumed", "value.power.produced",
11
+ "value.direction", "value.curtain", "value.blind", "value.tilt", "value.lock",
12
+ "value.speed", "value.pressure", "value.distance", "value.distance.visibility",
13
+ "value.severity", "value.warning",
14
+ "value.sun.elevation", "value.sun.azimuth",
15
+ "value.voltage", "value.current", "value.frequency", "value.fill",
16
+ "value.blood.sugar", "value.window", "value.state", "value.position", "value.gate",
17
+ "value.water", "value.waste",
18
+ "value.direction.wind", "value.speed.wind", "value.speed.wind.gust",
19
+ "value.speed.max.wind", "value.speed.min.wind",
20
+ "value.precipitation", "value.precipitation.chance", "value.precipitation.hour", "value.precipitation.today",
21
+ "value.rain", "value.rain.hour", "value.rain.today",
22
+ "value.snow", "value.snow.hour", "value.snow.today", "value.snowline",
23
+ "value.radiation", "value.uv",
24
+ "value.clouds",
25
+ "value.health.fat", "value.health.weight", "value.health.bmi", "value.health.calories", "value.health.steps", "value.health.bpm",
26
+ "value.mode.airconditioner",
27
+
28
+ "indicator", "indicator.working", "indicator.reachable", "indicator.connected",
29
+ "indicator.direction", "indicator.error",
30
+ "indicator.maintenance", "indicator.maintenance.lowbat", "indicator.maintenance.unreach",
31
+ "indicator.maintenance.alarm", "indicator.maintenance.waste",
32
+ "indicator.lowbat",
33
+ "indicator.alarm", "indicator.alarm.fire", "indicator.alarm.flood",
34
+ "indicator.alarm.secure", "indicator.alarm.health",
35
+
36
+ "sensor", "sensor.contact", "sensor.window", "sensor.door",
37
+ "sensor.alarm", "sensor.alarm.flood", "sensor.alarm.fire", "sensor.alarm.secure", "sensor.alarm.power",
38
+ "sensor.light", "sensor.lock", "sensor.motion", "sensor.rain", "sensor.noise", "sensor.switch"
39
+ ],
40
+ "write": [
41
+ "switch", "switch.lock", "switch.lock.door", "switch.lock.window",
42
+ "switch.mode.boost", "switch.mode.party", "switch.power", "switch.light",
43
+ "switch.comfort", "switch.enable", "switch.mode", "switch.mode.auto",
44
+ "switch.mode.manual", "switch.mode.silent", "switch.mode.moonlight",
45
+ "switch.mode.color", "switch.gate", "switch.power.zone", "switch.pause",
46
+ "switch.setting",
47
+
48
+ "button", "button.long", "button.stop", "button.stop.tilt",
49
+ "button.start", "button.resume", "button.open.door", "button.open.window",
50
+ "button.open.blind", "button.open.tilt", "button.close.blind", "button.close.tilt",
51
+ "button.mode", "button.mode.auto", "button.mode.manual", "button.mode.silent",
52
+ "button.press", "button.play", "button.next", "button.prev", "button.pause",
53
+ "button.forward", "button.reverse", "button.fastforward", "button.fastreverse",
54
+ "button.volume.up", "button.volume.down"
55
+ ],
56
+ "both": [
57
+ "state",
58
+
59
+ "level", "level.dimmer", "level.blind", "level.curtain", "level.tilt",
60
+ "level.temperature", "level.humidity", "level.valve",
61
+ "level.pressure", "level.pressure.min", "level.pressure.max",
62
+ "level.voltage", "level.voltage.min", "level.voltage.max",
63
+ "level.current", "level.current.min", "level.current.max",
64
+ "level.frequency", "level.frequency.min", "level.frequency.max",
65
+ "level.battery", "level.battery.min", "level.battery.max",
66
+ "level.fill", "level.brightness",
67
+ "level.min", "level.max", "level.default",
68
+ "level.color.red", "level.color.green", "level.color.blue", "level.color.white",
69
+ "level.color.hue", "level.color.saturation", "level.color.rgb", "level.color.rgbw",
70
+ "level.color.cie", "level.color.luminance", "level.color.temperature",
71
+ "level.effect", "level.timer", "level.timer.sleep",
72
+ "level.volume", "level.volume.group",
73
+ "level.speed", "level.bass", "level.treble",
74
+ "level.mode.fan", "level.mode.swing", "level.mode.airconditioner", "level.mode.thermostat",
75
+ "level.mode.cleanup", "level.mode.work",
76
+ "level.setting.color.temperature",
77
+
78
+ "text", "text.url", "text.phone",
79
+ "html", "json", "list", "date",
80
+ "date.start", "date.end", "date.forecast.1", "date.sunrise", "date.sunset",
81
+ "dayofweek", "location",
82
+ "url", "url.icon", "url.cam", "url.blank", "url.same", "url.audio",
83
+
84
+ "media.state", "media.mute", "media.mute.group", "media.seek",
85
+ "media.mode.shuffle", "media.mode.repeat",
86
+ "media.artist", "media.album", "media.title", "media.title.next",
87
+ "media.cover", "media.cover.big", "media.cover.small",
88
+ "media.duration", "media.duration.text", "media.elapsed", "media.elapsed.text",
89
+ "media.broadcastDate", "media.season", "media.episode",
90
+ "media.tts", "media.bitrate", "media.genre", "media.date", "media.track",
91
+ "media.playid", "media.add", "media.clear", "media.playlist",
92
+ "media.url", "media.url.announcement", "media.jump", "media.content",
93
+ "media.link", "media.input", "media.browser",
94
+
95
+ "weather.title", "weather.title.short", "weather.title.forecast.0",
96
+ "weather.state", "weather.state.forecast.0", "weather.state.forecast.1",
97
+ "weather.icon", "weather.icon.forecast.1", "weather.icon.name", "weather.icon.wind",
98
+ "weather.chart.url", "weather.chart.url.forecast",
99
+ "weather.html", "weather.json", "weather.type",
100
+ "weather.direction.wind", "weather.direction.wind.forecast.0",
101
+
102
+ "info.ip", "info.mac", "info.name", "info.address", "info.serial",
103
+ "info.firmware", "info.hardware", "info.port", "info.standby",
104
+ "info.status", "info.display", "info.model",
105
+
106
+ "time.span", "time.interval", "time.timeout",
107
+ "chart", "adapter.messagebox", "adapter.wakeup"
108
+ ]
109
+ }
package/admin/words.js CHANGED
@@ -11,6 +11,7 @@ systemDictionary = {
11
11
  " so you get read + write in a single object.": { "en": " so you get read + write in a single object.", "de": ", sodass Lesen + Schreiben in einem Objekt möglich ist.", "ru": ", чтобы чтение + запись были в одном объекте.", "pt": " para que leitura + escrita fiquem em um único objeto.", "nl": " zodat lezen + schrijven in één object beschikbaar is.", "fr": " pour que lecture + écriture soient dans un seul objet.", "it": " in modo che lettura + scrittura siano in un unico oggetto.", "es": " para que lectura + escritura estén en un solo objeto.", "pl": ", aby odczyt + zapis były w jednym obiekcie.", "uk": ", щоб читання + запис були в одному об'єкті.", "zh-cn": ",以便在单个对象中实现读 + 写。"},
12
12
  "Adapter is not running": { "en": "Adapter is not running", "de": "Adapter läuft nicht", "ru": "Адаптер не работает", "pt": "O adaptador não está funcionando", "nl": "Adapter werkt niet", "fr": "L'adaptateur ne fonctionne pas", "it": "L'adattatore non è in funzione", "es": "El adaptador no está funcionando", "pl": "Adapter nie działa", "uk": "Адаптер не працює", "zh-cn": "适配器未运行"},
13
13
  "Alias path": { "en": "Alias path", "de": "Alias-Pfad", "ru": "Путь псевдонима", "pt": "Caminho do alias", "nl": "Alias-pad", "fr": "Chemin d'alias", "it": "Percorso dell'alias", "es": "Ruta de alias", "pl": "Ścieżka aliasu", "uk": "Псевдонім шляху", "zh-cn": "别名路径"},
14
+ "Autoread": { "en": "Autoread", "de": "Aktualisieren", "ru": "Автоматическое чтение", "pt": "Autoread", "nl": "Autoread", "fr": "Autoread", "it": "Autoread", "es": "Autoread", "pl": "Autoread", "uk": "Autoread", "zh-cn": "Autoread"},
14
15
  "Browse": { "en": "Browse", "de": "Durchsuchen", "ru": "Обзор", "pt": "Browse", "nl": "Browse", "fr": "Browse", "it": "Browse", "es": "Browse", "pl": "Browse", "uk": "Browse", "zh-cn": "Browse"},
15
16
  "Connect any ioBroker state to a KNX Group Address": {"en": "Connect any ioBroker state to a KNX Group Address", "de": "Beliebigen ioBroker-State mit einer KNX-Gruppenadresse verbinden", "ru": "Связать любое состояние ioBroker с групповым адресом KNX", "pt": "Connect any ioBroker state to a KNX Group Address", "nl": "Connect any ioBroker state to a KNX Group Address", "fr": "Connect any ioBroker state to a KNX Group Address", "it": "Connect any ioBroker state to a KNX Group Address", "es": "Connect any ioBroker state to a KNX Group Address", "pl": "Connect any ioBroker state to a KNX Group Address", "uk": "Connect any ioBroker state to a KNX Group Address", "zh-cn": "Connect any ioBroker state to a KNX Group Address"},
16
17
  "Convert": { "en": "Convert", "de": "Konvertierung", "ru": "Convert", "pt": "Convert", "nl": "Convert", "fr": "Convert", "it": "Convert", "es": "Convert", "pl": "Convert", "uk": "Convert", "zh-cn": "Convert"},
@@ -62,6 +63,7 @@ systemDictionary = {
62
63
  "Regex to identify Status GAs": { "en": "Regex to identify Status GAs", "de": "Regex zum Erkennen von Status-GAs", "ru": "Regex для определения статуса GA", "pt": "Regex para identificar GAs de status", "nl": "Regex om Status GA's te identificeren", "fr": "Regex pour identifier les GA de statut", "it": "Regex per identificare gli Status GA", "es": "Regex para identificar GA de estado", "pl": "Regex do identyfikacji statusów GA", "uk": "Регулярне вираз для ідентифікації статусу GA", "zh-cn": "用于识别状态 GA 的正则表达式"},
63
64
  "Regex to identify Status GAs (ending with status, rm, rückmeldung..).": {"en": "Regex to identify Status GAs (ending with status, rm, rückmeldung..)", "de": "Regex zum Erkennen von Status-GAs (Endung mit status, rm, rückmeldung..)", "ru": "Регулярное выражение для определения GA состояния (заканчивающееся на status, rm, rückmeldung ..). ", "pt": "Regex para identificar Status GAs (terminando com status, rm, rückmeldung ..). ", "nl": "Regex om Status GA's te identificeren (eindigend met status, rm, rückmeldung..). ", "fr": "Regex pour identifier les Status GA (se terminant par status, rm, rückmeldung..). ", "it": "Regex per identificare gli Status GA (che termina con status, rm, rückmeldung..). ", "es": "Regex para identificar estados GA (terminando con status, rm, rückmeldung ..). ", "pl": "Regex do identyfikacji statusów GA (kończących się na status, rm, rückmeldung...). ", "uk": "Регулярне вираз для ідентифікації статусу GA (закінчується на status, rm, rückmeldung..)", "zh-cn": "用于识别状态 GA 的正则表达式(以状态、rm、rückmeldung 结尾...)。"},
64
65
  "Remove Link": { "en": "Remove Link", "de": "Verbindung entfernen", "ru": "Удалить связь", "pt": "Remove Link", "nl": "Remove Link", "fr": "Remove Link", "it": "Remove Link", "es": "Remove Link", "pl": "Remove Link", "uk": "Remove Link", "zh-cn": "Remove Link"},
66
+ "Response": { "en": "Response", "de": "Rückmeldung", "ru": "Ответ", "pt": "Response", "nl": "Response", "fr": "Response", "it": "Response", "es": "Response", "pl": "Response", "uk": "Response", "zh-cn": "Response"},
65
67
  "Restarting adapter": { "en": "Restarting adapter", "de": "Adapter wird neu gestartet", "ru": "Перезапуск адаптера", "pt": "Reiniciando o adaptador", "nl": "Adapter opnieuw opstarten", "fr": "Redémarrage de l'adaptateur", "it": "Adattatore di riavvio", "es": "Adaptador de reinicio", "pl": "Ponowne uruchamianie adaptera", "uk": "Перезапуск адаптера", "zh-cn": "重新启动适配器"},
66
68
  "Save": { "en": "Save", "de": "Speichern", "ru": "Save", "pt": "Save", "nl": "Save", "fr": "Save", "it": "Save", "es": "Save", "pl": "Save", "uk": "Save", "zh-cn": "Save"},
67
69
  "Select a GA first": { "en": "Select a GA first", "de": "Zuerst eine GA auswählen", "ru": "Select a GA first", "pt": "Select a GA first", "nl": "Select a GA first", "fr": "Select a GA first", "it": "Select a GA first", "es": "Select a GA first", "pl": "Select a GA first", "uk": "Select a GA first", "zh-cn": "Select a GA first"},
package/io-package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "openknx",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "news": {
6
- "1.1.0": {
7
- "en": "**breaking:** KNX communication switched to KNXUltimate\n**breaking:** DPT21 property names changed (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), values must be boolean\n**breaking:** DPT237 property names changed to camelCase\nfeature: KNX Secure support\nfeature: Extended DPT coverage (9 additional DPTs, including DPT-22, 213, 222, 235, 242, 249, 251)\nfeature: Improved connection stability\nfeature: Improved role detection (switch, level, value, text, date) based on DPT type\nfeature: Direct Link all iobroker states to a KNX state",
8
- "de": "**breaking:** KNX-Kommunikation auf KNXUltimate umgestellt\n**breaking:** DPT21 Eigenschaftsnamen geändert (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), Werte müssen boolean sein\n**breaking:** DPT237 Eigenschaftsnamen auf camelCase geändert\nFeature: KNX Secure Unterstützung\nFeature: Erweiterte DPT-Abdeckung (9 zusätzliche DPTs, u.a. DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Verbesserte Verbindungsstabilität\nFeature: Verbesserte Rollenerkennung (switch, level, value, text, date) basierend auf DPT-Typ\nFeature: Direct Link – beliebige ioBroker-States mit KNX-Gruppenadressen verknüpfen",
9
- "ru": "**breaking:** KNX-коммуникация переключена на KNXUltimate\n**breaking:** Имена свойств DPT21 изменены (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), значения должны быть boolean\n**breaking:** Имена свойств DPT237 изменены на camelCase\nFeature: Поддержка KNX Secure\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",
10
- "pt": "**breaking:** Comunicação KNX alterada para KNXUltimate\n**breaking:** Nomes de propriedades DPT21 alterados (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), valores devem ser boolean\n**breaking:** Nomes de propriedades DPT237 alterados para camelCase\nFeature: Suporte KNX Secure\nFeature: Cobertura DPT ampliada (9 DPTs adicionais, incluindo DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Estabilidade de conexão melhorada\nFeature: Deteção de papéis melhorada (switch, level, value, text, date) com base no tipo DPT\nFeature: Direct Link – associar qualquer estado ioBroker a endereços de grupo KNX",
11
- "nl": "**breaking:** KNX-communicatie overgeschakeld naar KNXUltimate\n**breaking:** DPT21 eigenschapsnamen gewijzigd (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), waarden moeten boolean zijn\n**breaking:** DPT237 eigenschapsnamen gewijzigd naar camelCase\nFeature: KNX Secure ondersteuning\nFeature: Uitgebreide DPT-dekking (9 extra DPTs, waaronder DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Verbeterde verbindingsstabiliteit\nFeature: Verbeterde roldetectie (switch, level, value, text, date) op basis van DPT-type\nFeature: Direct Link – koppel willekeurige ioBroker-states aan KNX-groepsadressen",
12
- "fr": "**breaking:** Communication KNX basculée sur KNXUltimate\n**breaking:** Noms de propriétés DPT21 modifiés (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), les valeurs doivent être boolean\n**breaking:** Noms de propriétés DPT237 modifiés en camelCase\nFeature: Support KNX Secure\nFeature: Couverture DPT étendue (9 DPTs supplémentaires, dont DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Stabilité de connexion améliorée\nFeature: Détection des rôles améliorée (switch, level, value, text, date) basée sur le type DPT\nFeature: Direct Link – associer n'importe quel état ioBroker à des adresses de groupe KNX",
13
- "it": "**breaking:** Comunicazione KNX passata a KNXUltimate\n**breaking:** Nomi proprietà DPT21 modificati (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), i valori devono essere boolean\n**breaking:** Nomi proprietà DPT237 modificati in camelCase\nFeature: Supporto KNX Secure\nFeature: Copertura DPT estesa (9 DPT aggiuntivi, tra cui DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Stabilità di connessione migliorata\nFeature: Rilevamento ruoli migliorato (switch, level, value, text, date) basato sul tipo DPT\nFeature: Direct Link – collegare qualsiasi stato ioBroker a indirizzi di gruppo KNX",
14
- "es": "**breaking:** Comunicación KNX cambiada a KNXUltimate\n**breaking:** Nombres de propiedades DPT21 cambiados (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), los valores deben ser boolean\n**breaking:** Nombres de propiedades DPT237 cambiados a camelCase\nFeature: Soporte KNX Secure\nFeature: Cobertura DPT ampliada (9 DPTs adicionales, incluyendo DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Estabilidad de conexión mejorada\nFeature: Detección de roles mejorada (switch, level, value, text, date) basada en el tipo DPT\nFeature: Direct Link – vincular cualquier estado ioBroker a direcciones de grupo KNX",
15
- "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: 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",
16
- "uk": "**breaking:** KNX-комунікацію переключено на KNXUltimate\n**breaking:** Назви властивостей DPT21 змінено (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), значення мають бути boolean\n**breaking:** Назви властивостей DPT237 змінено на camelCase\nFeature: Підтримка KNX Secure\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",
17
- "zh-cn": "**breaking:** KNX通信切换至KNXUltimate\n**breaking:** DPT21属性名已更改(outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck),值必须为boolean\n**breaking:** DPT237属性名已更改为camelCase\nFeature: KNX Secure支持\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组地址"
6
+ "1.1.2": {
7
+ "en": "**breaking:** KNX communication switched to KNXUltimate\n**breaking:** DPT21 property names changed (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), values must be boolean\n**breaking:** DPT237 property names changed to camelCase\nfeature: KNX Secure support\nfeature: Native .knxproj import (ETS4/5/6, password-protected) with flags, DPT inference, room assignment\nfeature: Extended DPT coverage (9 additional DPTs, including DPT-22, 213, 222, 235, 242, 249, 251)\nfeature: Improved connection stability\nfeature: Improved role detection (switch, level, value, text, date) based on DPT type\nfeature: Direct Link all iobroker states to a KNX state\nfeature: GA-Tools: all GA properties editable (DPT, type, role, flags) with compact layout",
8
+ "de": "**breaking:** KNX-Kommunikation auf KNXUltimate umgestellt\n**breaking:** DPT21 Eigenschaftsnamen geändert (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), Werte müssen boolean sein\n**breaking:** DPT237 Eigenschaftsnamen auf camelCase geändert\nFeature: KNX Secure Unterstützung\nFeature: Nativer .knxproj Import (ETS4/5/6, passwortgeschützt) mit Flags, DPT-Erkennung, Raumzuordnung\nFeature: Erweiterte DPT-Abdeckung (9 zusätzliche DPTs, u.a. DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Verbesserte Verbindungsstabilität\nFeature: Verbesserte Rollenerkennung (switch, level, value, text, date) basierend auf DPT-Typ\nFeature: Direct Link – beliebige ioBroker-States mit KNX-Gruppenadressen verknüpfen\nFeature: GA-Tools: alle GA-Eigenschaften editierbar (DPT, Typ, Rolle, Flags) mit kompaktem Layout",
9
+ "ru": "**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, тип, роль, флаги) с компактным макетом",
10
+ "pt": "**breaking:** Comunicação KNX alterada para KNXUltimate\n**breaking:** Nomes de propriedades DPT21 alterados (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), valores devem ser boolean\n**breaking:** Nomes de propriedades DPT237 alterados para camelCase\nFeature: Suporte KNX Secure\nFeature: Importação nativa .knxproj (ETS4/5/6, protegido por senha) com flags, inferência DPT, atribuição de salas\nFeature: Cobertura DPT ampliada (9 DPTs adicionais, incluindo DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Estabilidade de conexão melhorada\nFeature: Deteção de papéis melhorada (switch, level, value, text, date) com base no tipo DPT\nFeature: Direct Link – associar qualquer estado ioBroker a endereços de grupo KNX\nFeature: GA-Tools: todas as propriedades GA editáveis (DPT, tipo, papel, flags) com layout compacto",
11
+ "nl": "**breaking:** KNX-communicatie overgeschakeld naar KNXUltimate\n**breaking:** DPT21 eigenschapsnamen gewijzigd (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), waarden moeten boolean zijn\n**breaking:** DPT237 eigenschapsnamen gewijzigd naar camelCase\nFeature: KNX Secure ondersteuning\nFeature: Native .knxproj import (ETS4/5/6, wachtwoord-beveiligd) met vlaggen, DPT-detectie, kamertoewijzing\nFeature: Uitgebreide DPT-dekking (9 extra DPTs, waaronder DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Verbeterde verbindingsstabiliteit\nFeature: Verbeterde roldetectie (switch, level, value, text, date) op basis van DPT-type\nFeature: Direct Link – koppel willekeurige ioBroker-states aan KNX-groepsadressen\nFeature: GA-Tools: alle GA-eigenschappen bewerkbaar (DPT, type, rol, vlaggen) met compact layout",
12
+ "fr": "**breaking:** Communication KNX basculée sur KNXUltimate\n**breaking:** Noms de propriétés DPT21 modifiés (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), les valeurs doivent être boolean\n**breaking:** Noms de propriétés DPT237 modifiés en camelCase\nFeature: Support KNX Secure\nFeature: Import natif .knxproj (ETS4/5/6, protégé par mot de passe) avec drapeaux, inférence DPT, attribution de pièces\nFeature: Couverture DPT étendue (9 DPTs supplémentaires, dont DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Stabilité de connexion améliorée\nFeature: Détection des rôles améliorée (switch, level, value, text, date) basée sur le type DPT\nFeature: Direct Link – associer n'importe quel état ioBroker à des adresses de groupe KNX\nFeature: GA-Tools: toutes les propriétés GA modifiables (DPT, type, rôle, drapeaux) avec mise en page compacte",
13
+ "it": "**breaking:** Comunicazione KNX passata a KNXUltimate\n**breaking:** Nomi proprietà DPT21 modificati (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), i valori devono essere boolean\n**breaking:** Nomi proprietà DPT237 modificati in camelCase\nFeature: Supporto KNX Secure\nFeature: Importazione nativa .knxproj (ETS4/5/6, protetto da password) con flag, inferenza DPT, assegnazione stanze\nFeature: Copertura DPT estesa (9 DPT aggiuntivi, tra cui DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Stabilità di connessione migliorata\nFeature: Rilevamento ruoli migliorato (switch, level, value, text, date) basato sul tipo DPT\nFeature: Direct Link – collegare qualsiasi stato ioBroker a indirizzi di gruppo KNX\nFeature: GA-Tools: tutte le proprietà GA modificabili (DPT, tipo, ruolo, flag) con layout compatto",
14
+ "es": "**breaking:** Comunicación KNX cambiada a KNXUltimate\n**breaking:** Nombres de propiedades DPT21 cambiados (outofservice → outOfService, inalarm → inAlarm, alarmeunack → alarmUnAck), los valores deben ser boolean\n**breaking:** Nombres de propiedades DPT237 cambiados a camelCase\nFeature: Soporte KNX Secure\nFeature: Importación nativa .knxproj (ETS4/5/6, protegido con contraseña) con flags, inferencia DPT, asignación de habitaciones\nFeature: Cobertura DPT ampliada (9 DPTs adicionales, incluyendo DPT-22, 213, 222, 235, 242, 249, 251)\nFeature: Estabilidad de conexión mejorada\nFeature: Detección de roles mejorada (switch, level, value, text, date) basada en el tipo DPT\nFeature: Direct Link – vincular cualquier estado ioBroker a direcciones de grupo KNX\nFeature: GA-Tools: todas las propiedades GA editables (DPT, tipo, rol, flags) con diseño compacto",
15
+ "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",
16
+ "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, тип, роль, прапорці) з компактним макетом",
17
+ "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、类型、角色、标志)紧凑布局"
18
18
  },
19
19
  "0.9.1": {
20
20
  "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",
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ const { expect } = require("chai");
4
+ const { dptlib } = require("knxultimate");
5
+
6
+ /**
7
+ * Standalone version of openknx.getDptList() for testing without js-controller.
8
+ * Must be kept in sync with main.js getDptList().
9
+ */
10
+ function getDptList() {
11
+ const list = [];
12
+ for (const dptKey of Object.keys(dptlib.dpts).sort((a, b) => {
13
+ const na = parseInt(a.replace("DPT", ""));
14
+ const nb = parseInt(b.replace("DPT", ""));
15
+ return na - nb;
16
+ })) {
17
+ const dptObj = dptlib.dpts[dptKey];
18
+ const baseNum = dptKey.replace("DPT", "");
19
+ list.push({ value: dptKey, label: `${baseNum} - ${dptObj.basetype?.desc || dptKey}` });
20
+ if (dptObj.subtypes) {
21
+ for (const sub of Object.keys(dptObj.subtypes).sort()) {
22
+ const st = dptObj.subtypes[sub];
23
+ list.push({ value: `${dptKey}.${sub}`, label: `${baseNum}.${sub} - ${st.name || st.desc || ""}` });
24
+ }
25
+ }
26
+ }
27
+ return list;
28
+ }
29
+
30
+ describe("getDptList", () => {
31
+ it("returns a non-empty array", () => {
32
+ const list = getDptList();
33
+ expect(list).to.be.an("array");
34
+ expect(list.length).to.be.greaterThan(0);
35
+ });
36
+
37
+ it("each entry has value and label strings", () => {
38
+ const list = getDptList();
39
+ for (const entry of list) {
40
+ expect(entry).to.have.property("value").that.is.a("string");
41
+ expect(entry).to.have.property("label").that.is.a("string");
42
+ }
43
+ });
44
+
45
+ it("contains base DPTs like DPT1, DPT9", () => {
46
+ const list = getDptList();
47
+ const values = list.map(e => e.value);
48
+ expect(values).to.include("DPT1");
49
+ expect(values).to.include("DPT9");
50
+ });
51
+
52
+ it("contains subtypes like DPT1.001, DPT9.001", () => {
53
+ const list = getDptList();
54
+ const values = list.map(e => e.value);
55
+ expect(values).to.include("DPT1.001");
56
+ expect(values).to.include("DPT9.001");
57
+ });
58
+
59
+ it("is sorted numerically by base DPT number", () => {
60
+ const list = getDptList();
61
+ const baseEntries = list.filter(e => !e.value.includes("."));
62
+ for (let i = 1; i < baseEntries.length; i++) {
63
+ const prev = parseInt(baseEntries[i - 1].value.replace("DPT", ""));
64
+ const curr = parseInt(baseEntries[i].value.replace("DPT", ""));
65
+ expect(curr).to.be.greaterThan(prev);
66
+ }
67
+ });
68
+
69
+ it("subtypes follow their base DPT", () => {
70
+ const list = getDptList();
71
+ const baseDpt1Idx = list.findIndex(e => e.value === "DPT1");
72
+ const subDpt1Idx = list.findIndex(e => e.value === "DPT1.001");
73
+ expect(baseDpt1Idx).to.be.lessThan(subDpt1Idx);
74
+ const baseDpt2Idx = list.findIndex(e => e.value === "DPT2");
75
+ expect(subDpt1Idx).to.be.lessThan(baseDpt2Idx);
76
+ });
77
+
78
+ it("labels contain human-readable descriptions", () => {
79
+ const list = getDptList();
80
+ const dpt1_001 = list.find(e => e.value === "DPT1.001");
81
+ expect(dpt1_001).to.exist;
82
+ expect(dpt1_001.label).to.include("1.001");
83
+ expect(dpt1_001.label).to.include("Switch");
84
+ });
85
+
86
+ it("no entry has empty value", () => {
87
+ const list = getDptList();
88
+ for (const entry of list) {
89
+ expect(entry.value).to.not.be.empty;
90
+ }
91
+ });
92
+
93
+ it("no duplicate values", () => {
94
+ const list = getDptList();
95
+ const values = list.map(e => e.value);
96
+ const unique = new Set(values);
97
+ expect(unique.size).to.equal(values.length);
98
+ });
99
+ });
package/lib/tools.js CHANGED
@@ -102,12 +102,12 @@ function isTriggerDPT(dpt) {
102
102
  //used to exclude from autoread
103
103
  //scene is a trigger, do not request trigger on start, more to add
104
104
  return (
105
- dpt.startsWith("1.001") || //DPT_Switch
106
- dpt.startsWith("1.007") || //DPT_Step
107
- dpt.startsWith("1.008") || //DPT_UpDown
108
- dpt.startsWith("1.010") || //DPT_Start
109
- dpt.startsWith("1.017") || //DPT_Trigger
110
- dpt.startsWith("2.001") || //Switch_Control
105
+ dpt.startsWith("DPT1.001") || //DPT_Switch
106
+ dpt.startsWith("DPT1.007") || //DPT_Step
107
+ dpt.startsWith("DPT1.008") || //DPT_UpDown
108
+ dpt.startsWith("DPT1.010") || //DPT_Start
109
+ dpt.startsWith("DPT1.017") || //DPT_Trigger
110
+ dpt.startsWith("DPT2.001") || //Switch_Control
111
111
  dpt == "DPT17" ||
112
112
  dpt.startsWith("DPT17.") ||
113
113
  dpt == "DPT18" ||
package/main.js CHANGED
@@ -168,6 +168,26 @@ class openknx extends utils.Adapter {
168
168
 
169
169
  // New message arrived. obj is array with current messages
170
170
  // triggered from admin page read in knx project
171
+ getDptList() {
172
+ const list = [];
173
+ for (const dptKey of Object.keys(dptlib.dpts).sort((a, b) => {
174
+ const na = parseInt(a.replace("DPT", ""));
175
+ const nb = parseInt(b.replace("DPT", ""));
176
+ return na - nb;
177
+ })) {
178
+ const dptObj = dptlib.dpts[dptKey];
179
+ const baseNum = dptKey.replace("DPT", "");
180
+ list.push({ value: dptKey, label: `${baseNum} - ${dptObj.basetype?.desc || dptKey}` });
181
+ if (dptObj.subtypes) {
182
+ for (const sub of Object.keys(dptObj.subtypes).sort()) {
183
+ const st = dptObj.subtypes[sub];
184
+ list.push({ value: `${dptKey}.${sub}`, label: `${baseNum}.${sub} - ${st.name || st.desc || ""}` });
185
+ }
186
+ }
187
+ }
188
+ return list;
189
+ }
190
+
171
191
  onMessage(obj) {
172
192
  if (typeof obj === "object") {
173
193
  switch (obj.command) {
@@ -289,6 +309,13 @@ class openknx extends utils.Adapter {
289
309
  }
290
310
  });
291
311
  break;
312
+ case "getDptList": {
313
+ const list = this.getDptList();
314
+ if (obj.callback) {
315
+ this.sendTo(obj.from, obj.command, list, obj.callback);
316
+ }
317
+ break;
318
+ }
292
319
  case "restart":
293
320
  this.log.info("Restarting...");
294
321
  this.restart();
@@ -827,6 +854,11 @@ class openknx extends utils.Adapter {
827
854
  // Clean up previous connection (reconnect case)
828
855
  if (this.knxConnection) {
829
856
  this.knxConnection.removeAllListeners();
857
+ try {
858
+ this.knxConnection.Disconnect();
859
+ } catch (e) {
860
+ // ignore - old connection may already be in broken state
861
+ }
830
862
  this.knxConnection = undefined;
831
863
  }
832
864
 
@@ -1080,6 +1112,8 @@ class openknx extends utils.Adapter {
1080
1112
  `Could not decode GA ${dest} (${data.native.dpt}), raw=[${rawData.toString("hex")}]. Check DPT configuration in ETS (buffer length mismatch?).`,
1081
1113
  );
1082
1114
  convertedVal = rawData.toString("hex");
1115
+ } else if (typeof jsValue === "bigint") {
1116
+ convertedVal = jsValue.toString();
1083
1117
  } else if (tools.isStringDPT(data.native.dpt)) {
1084
1118
  convertedVal = jsValue;
1085
1119
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "ioBroker knx Adapter",
5
5
  "author": "boellner",
6
6
  "contributors": [