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 +2 -1
- package/admin/index_m.html +182 -24
- package/admin/roles.json +109 -0
- package/admin/words.js +2 -0
- package/io-package.json +13 -13
- package/lib/getDptList.test.js +99 -0
- package/lib/tools.js +6 -6
- package/main.js +34 -0
- package/package.json +1 -1
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.
|
|
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
|
package/admin/index_m.html
CHANGED
|
@@ -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
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
//
|
|
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)
|
|
1068
|
+
if (hasVisible) {
|
|
1069
|
+
$(this).find('> span > i').text('expand_more');
|
|
1070
|
+
}
|
|
913
1071
|
});
|
|
914
1072
|
}
|
|
915
1073
|
</script>
|
package/admin/roles.json
ADDED
|
@@ -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.
|
|
4
|
+
"version": "1.1.2",
|
|
5
5
|
"news": {
|
|
6
|
-
"1.1.
|
|
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("
|
|
106
|
-
dpt.startsWith("
|
|
107
|
-
dpt.startsWith("
|
|
108
|
-
dpt.startsWith("
|
|
109
|
-
dpt.startsWith("
|
|
110
|
-
dpt.startsWith("
|
|
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 {
|