node-red-contrib-knx-ultimate 6.3.10 → 6.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/nodes/knxUltimateHueController.html +23 -10
- package/nodes/knxUltimateMatterControllerDevice.html +42 -38
- package/nodes/locales/de/knxUltimateHueController.html +3 -1
- package/nodes/locales/de/knxUltimateHueController.json +3 -0
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/en/knxUltimateHueController.json +3 -0
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/es/knxUltimateHueController.html +50 -50
- package/nodes/locales/es/knxUltimateHueController.json +4 -1
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/fr/knxUltimateHueController.html +45 -45
- package/nodes/locales/fr/knxUltimateHueController.json +4 -1
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/it/knxUltimateHueController.html +1 -1
- package/nodes/locales/it/knxUltimateHueController.json +3 -0
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/zh-CN/knxUltimateHueController.json +3 -0
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +1 -1
- package/package.json +1 -1
- package/resources/configNodeEditorSelection.js +32 -0
- package/resources/hueControllerProfiles.js +33 -17
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 6.3.12** - August 2026<br/>
|
|
10
|
+
|
|
11
|
+
- **HUE Controller**: completed the EN, IT, DE, FR, ES and zh-CN editor translations, including Light Level, Locate, Light mapping labels, mapping placeholders, error messages and tutorial links; the embedded profiles and dynamic device labels now follow Node-RED's active locale instead of a potentially stale page-language attribute. Corrected French/Spanish Hue and KNX terminology and realigned the localized help and wiki documentation.<br/>
|
|
12
|
+
|
|
13
|
+
**Version 6.3.11** - August 2026<br/>
|
|
14
|
+
|
|
15
|
+
- **Matter Controller**: fixed KNX GA mapping tabs being hidden by transient empty KNX/Matter config-selector values during Node-RED 5 editor initialization; saved gateways now remain selected until the user explicitly changes them.<br/>
|
|
16
|
+
|
|
9
17
|
**Version 6.3.10** - August 2026<br/>
|
|
10
18
|
|
|
11
19
|
- **Watchdog**: added a default-on option to listen for error states reported by KNX-Ultimate nodes, including KNX Device, through the selected gateway; disabling it suppresses `NodeError` flow messages without affecting the Watchdog's own bus checks or control messages. Existing flows retain the previous enabled behavior.<br/>
|
|
@@ -254,11 +254,18 @@
|
|
|
254
254
|
`);
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
-
const
|
|
257
|
+
const controllerMessage = (key, values) => {
|
|
258
258
|
try {
|
|
259
|
-
const
|
|
260
|
-
|
|
259
|
+
const messageKey = `node-red-contrib-knx-ultimate/knxUltimateHueController:knxUltimateHueController.${key}`;
|
|
260
|
+
const translated = RED._(messageKey, values);
|
|
261
|
+
if (translated && translated !== messageKey) return translated;
|
|
261
262
|
} catch (error) { /* empty */ }
|
|
263
|
+
return undefined;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const migrationText = (_node, key, fallback, values) => {
|
|
267
|
+
const translated = controllerMessage(key, values);
|
|
268
|
+
if (translated) return translated;
|
|
262
269
|
return fallback;
|
|
263
270
|
};
|
|
264
271
|
|
|
@@ -318,7 +325,13 @@
|
|
|
318
325
|
icon: 'node-hue-icon.svg',
|
|
319
326
|
label() {
|
|
320
327
|
const profile = PROFILE_TYPES[this.hueControllerType] || PROFILE_TYPES.light;
|
|
321
|
-
|
|
328
|
+
let typeLabel = profile.label;
|
|
329
|
+
try {
|
|
330
|
+
const translationKey = `node-red-contrib-knx-ultimate/knxUltimateHueController:knxUltimateHueController.types.${this.hueControllerType || 'light'}`;
|
|
331
|
+
const translated = RED._(translationKey);
|
|
332
|
+
if (translated && translated !== translationKey) typeLabel = translated;
|
|
333
|
+
} catch (error) { /* use catalog fallback */ }
|
|
334
|
+
return this.name || `HUE Controller · ${typeLabel}`;
|
|
322
335
|
},
|
|
323
336
|
paletteLabel: 'HUE Controller',
|
|
324
337
|
oneditprepare() {
|
|
@@ -435,10 +448,8 @@
|
|
|
435
448
|
};
|
|
436
449
|
|
|
437
450
|
const controllerText = (key, fallback) => {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (translated && translated !== `knxUltimateHueController.${key}`) return translated;
|
|
441
|
-
} catch (error) { /* empty */ }
|
|
451
|
+
const translated = controllerMessage(key);
|
|
452
|
+
if (translated) return translated;
|
|
442
453
|
return fallback;
|
|
443
454
|
};
|
|
444
455
|
|
|
@@ -710,7 +721,9 @@
|
|
|
710
721
|
$container.toggleClass('hue-controller-light-profile', controllerType === 'light');
|
|
711
722
|
|
|
712
723
|
if (!definition || !template) {
|
|
713
|
-
$container.html('<div class="hue-controller-editor-error"
|
|
724
|
+
$container.html($('<div class="hue-controller-editor-error"></div>').text(
|
|
725
|
+
controllerText('editor_unavailable', 'The selected Hue editor is unavailable. Restart Node-RED after updating the package.')
|
|
726
|
+
));
|
|
714
727
|
return;
|
|
715
728
|
}
|
|
716
729
|
|
|
@@ -967,7 +980,7 @@
|
|
|
967
980
|
<script type="text/html" data-template-name="knxUltimateHueController">
|
|
968
981
|
<div class="form-row" style="margin-bottom:14px;">
|
|
969
982
|
<span style="color:#ff0000"><i class="fa fa-youtube"></i></span>
|
|
970
|
-
<a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E"><b
|
|
983
|
+
<a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E"><b data-i18n="knxUltimateHueController.tutorials_link"></b></a>
|
|
971
984
|
</div>
|
|
972
985
|
<div class="form-row" id="hue-controller-migrate-legacy-flow-row" style="display:none; margin-bottom:14px;">
|
|
973
986
|
<a class="hue-legacy-migration-video" href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer" style="display:inline-flex; align-items:center; gap:5px; margin-right:10px;" data-i18n="[title]knxUltimateHueController.migration_video_title">
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
|
|
4
4
|
|
|
5
5
|
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
|
|
6
|
+
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/configNodeEditorSelection.js"></script>
|
|
6
7
|
|
|
7
8
|
<script type="text/javascript">
|
|
8
9
|
(function () {
|
|
@@ -161,16 +162,6 @@
|
|
|
161
162
|
});
|
|
162
163
|
}
|
|
163
164
|
$("#node-input-updateLocalStateFromKNXWrite").prop("checked", node.updateLocalStateFromKNXWrite === true || node.updateLocalStateFromKNXWrite === "true"); // Starting from v 4.1.31
|
|
164
|
-
const ensureConfigSelection = (selector) => {
|
|
165
|
-
if ($(selector).val() !== "_ADD_") return;
|
|
166
|
-
try {
|
|
167
|
-
$(selector).prop("selectedIndex", 0);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
// Ignore UI quirks for legacy Node-RED versions
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
["#node-input-server", "#node-input-serverMatter"].forEach(ensureConfigSelection);
|
|
173
|
-
|
|
174
165
|
function ensureVerticalTabsStyle() {
|
|
175
166
|
if ($('#knxUltimateMatterControllerDeviceVerticalTabs').length) return;
|
|
176
167
|
const style = `
|
|
@@ -261,7 +252,7 @@
|
|
|
261
252
|
function onEditPrepare() {
|
|
262
253
|
ensureVerticalTabsStyle();
|
|
263
254
|
const $knxServerInput = $("#node-input-server");
|
|
264
|
-
const KNX_EMPTY_VALUES = new Set(['', 'none', '
|
|
255
|
+
const KNX_EMPTY_VALUES = new Set(['', 'none', '_add_', '__none__']);
|
|
265
256
|
// Historical variable names are retained for saved-flow compatibility; they point to Matter widgets.
|
|
266
257
|
const $hueServerInput = $("#node-input-serverMatter");
|
|
267
258
|
const $matterControllerModeInput = $("#node-input-matterControllerMode");
|
|
@@ -300,6 +291,21 @@
|
|
|
300
291
|
let matterDevicePickerMouseDown = false;
|
|
301
292
|
let showingNoHueDevicesPlaceholder = false;
|
|
302
293
|
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__', '__null__', 'null', 'undefined']);
|
|
294
|
+
const selectionApi = window.KNXUltimateConfigNodeEditorSelection;
|
|
295
|
+
const normalizeConfigSelection = selectionApi && typeof selectionApi.normalizeSelection === 'function'
|
|
296
|
+
? selectionApi.normalizeSelection
|
|
297
|
+
: (value, emptyValues) => {
|
|
298
|
+
const normalized = value === undefined || value === null ? '' : String(value).trim();
|
|
299
|
+
return emptyValues.has(normalized.toLowerCase()) ? '' : normalized;
|
|
300
|
+
};
|
|
301
|
+
const resolveSelectedOrStoredSelection = selectionApi && typeof selectionApi.resolveSelectedOrStoredSelection === 'function'
|
|
302
|
+
? selectionApi.resolveSelectedOrStoredSelection
|
|
303
|
+
: (selectedValue, storedValue, emptyValues) => normalizeConfigSelection(selectedValue, emptyValues) || normalizeConfigSelection(storedValue, emptyValues);
|
|
304
|
+
const shouldPreserveSelection = selectionApi && typeof selectionApi.shouldPreserveSelection === 'function'
|
|
305
|
+
? selectionApi.shouldPreserveSelection
|
|
306
|
+
: (event, nextValue, currentValue, emptyValues) => (!event || !event.originalEvent) && normalizeConfigSelection(nextValue, emptyValues) === '' && normalizeConfigSelection(currentValue, emptyValues) !== '';
|
|
307
|
+
let activeKnxServerId = resolveSelectedOrStoredSelection($knxServerInput.val(), node.server, KNX_EMPTY_VALUES);
|
|
308
|
+
let activeMatterServerId = resolveSelectedOrStoredSelection($hueServerInput.val(), node.serverMatter, HUE_EMPTY_SERVER_VALUES);
|
|
303
309
|
let locateSessionActive = false;
|
|
304
310
|
let locateAutoResetTimer = null;
|
|
305
311
|
let locatePendingRequest = null;
|
|
@@ -483,14 +489,7 @@
|
|
|
483
489
|
};
|
|
484
490
|
|
|
485
491
|
const resolveKnxServerValue = () => {
|
|
486
|
-
|
|
487
|
-
if (domValue !== undefined && domValue !== null && domValue !== '') {
|
|
488
|
-
return domValue;
|
|
489
|
-
}
|
|
490
|
-
if (node.server !== undefined && node.server !== null) {
|
|
491
|
-
return node.server;
|
|
492
|
-
}
|
|
493
|
-
return '';
|
|
492
|
+
return activeKnxServerId;
|
|
494
493
|
};
|
|
495
494
|
|
|
496
495
|
const hasKnxServerSelected = () => {
|
|
@@ -500,17 +499,7 @@
|
|
|
500
499
|
};
|
|
501
500
|
|
|
502
501
|
const resolveHueServerValue = ({ allowStored = false } = {}) => {
|
|
503
|
-
if (
|
|
504
|
-
const domValue = $hueServerInput.val();
|
|
505
|
-
if (domValue !== undefined && domValue !== null) {
|
|
506
|
-
const trimmed = String(domValue).trim();
|
|
507
|
-
if (trimmed !== '' && !HUE_EMPTY_SERVER_VALUES.has(trimmed.toLowerCase())) return trimmed;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
if (node.serverMatter !== undefined && node.serverMatter !== null) {
|
|
511
|
-
const stored = String(node.serverMatter).trim();
|
|
512
|
-
if (stored !== '' && !HUE_EMPTY_SERVER_VALUES.has(stored.toLowerCase())) return stored;
|
|
513
|
-
}
|
|
502
|
+
if (activeMatterServerId !== '') return activeMatterServerId;
|
|
514
503
|
if (allowStored && node.__locateSessionInfo && node.__locateSessionInfo.serverId) {
|
|
515
504
|
return node.__locateSessionInfo.serverId;
|
|
516
505
|
}
|
|
@@ -1458,8 +1447,20 @@
|
|
|
1458
1447
|
$matterControllerModeInput.on('change.knxUltimateMatterControllerDevice', applyControllerMode);
|
|
1459
1448
|
$universalServiceInput.on('change.knxUltimateMatterControllerDevice', refreshUniversalServiceVisibility);
|
|
1460
1449
|
|
|
1461
|
-
$knxServerInput.on('change.knxUltimateMatterControllerDevice', ()
|
|
1462
|
-
|
|
1450
|
+
$knxServerInput.on('change.knxUltimateMatterControllerDevice', function (event) {
|
|
1451
|
+
const selectedValue = $(this).val();
|
|
1452
|
+
if (shouldPreserveSelection(event, selectedValue, activeKnxServerId, KNX_EMPTY_VALUES)) {
|
|
1453
|
+
// Node-RED 5 can briefly emit an empty placeholder while rebuilding
|
|
1454
|
+
// config-node selectors. Restore the effective saved value so an
|
|
1455
|
+
// untouched save and the mapping tabs both keep the KNX gateway.
|
|
1456
|
+
$knxServerInput.val(activeKnxServerId);
|
|
1457
|
+
updateTabsVisibility();
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
activeKnxServerId = normalizeConfigSelection(selectedValue, KNX_EMPTY_VALUES);
|
|
1461
|
+
// Selecting none only changes visibility. Keep the mounted GA/DPT
|
|
1462
|
+
// controls intact so choosing the gateway again cannot lose values.
|
|
1463
|
+
if (activeKnxServerId !== '') refreshKnxBindings();
|
|
1463
1464
|
updateTabsVisibility();
|
|
1464
1465
|
});
|
|
1465
1466
|
|
|
@@ -1553,7 +1554,15 @@
|
|
|
1553
1554
|
}
|
|
1554
1555
|
|
|
1555
1556
|
if ($hueServerInput.length) {
|
|
1556
|
-
$hueServerInput.off('.knxUltimateMatterControllerDeviceDevices').on('change.knxUltimateMatterControllerDeviceDevices', ()
|
|
1557
|
+
$hueServerInput.off('.knxUltimateMatterControllerDeviceDevices').on('change.knxUltimateMatterControllerDeviceDevices', function (event) {
|
|
1558
|
+
const selectedValue = $(this).val();
|
|
1559
|
+
if (shouldPreserveSelection(event, selectedValue, activeMatterServerId, HUE_EMPTY_SERVER_VALUES)) {
|
|
1560
|
+
$hueServerInput.val(activeMatterServerId);
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
const nextMatterServerId = normalizeConfigSelection(selectedValue, HUE_EMPTY_SERVER_VALUES);
|
|
1564
|
+
if (nextMatterServerId === activeMatterServerId) return;
|
|
1565
|
+
activeMatterServerId = nextMatterServerId;
|
|
1557
1566
|
cachedHueDevices = [];
|
|
1558
1567
|
node._cachedHueLightDevices = cachedHueDevices;
|
|
1559
1568
|
showingNoHueDevicesPlaceholder = false;
|
|
@@ -1772,11 +1781,6 @@
|
|
|
1772
1781
|
|
|
1773
1782
|
// The Matter controller needs no readiness poll here: the light list is fetched
|
|
1774
1783
|
// on demand by the autocomplete. Start the editor immediately.
|
|
1775
|
-
$("#node-input-serverMatter").change(function () {
|
|
1776
|
-
try {
|
|
1777
|
-
node._cachedHueLightDevices = [];
|
|
1778
|
-
} catch (error) { }
|
|
1779
|
-
});
|
|
1780
1784
|
Go();
|
|
1781
1785
|
// ################################################################
|
|
1782
1786
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
|
-
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate
|
|
2
|
+
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate-Videotutorials (YouTube-Playlist)</strong></a> Sehen Sie sich vor dem Start die <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">Videoanleitung auf YouTube</a> an.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> ist der einheitliche Knoten für Hue-API-v2-Ressourcen.</p>
|
|
4
4
|
<p>Wählen Sie ein Hue-Gerät oder eine Hue-Ressource. HUE Controller erkennt den Gerätetyp automatisch und öffnet den passenden Editor für KNX-Zuordnungen, Verhalten und Node-Anschlüsse.</p>
|
|
5
5
|
<p>Geräteauswahl, Aktualisierung und erkannter Typ stehen wie beim Matter Controller in derselben kompakten Zeile.</p>
|
|
@@ -154,6 +154,8 @@ Die Tabelle **Hue-native Effekte** ordnet KNX-Werte den vom Bridge gemeldeten Ef
|
|
|
154
154
|
| Night lighting | Nacht-Profil (Farbe oder Temperatur/Helligkeit) |
|
|
155
155
|
| Day/Night | GA zur Umschaltung Tag/Nacht (_true_ = Tag, _false_ = Nacht) |
|
|
156
156
|
| Invert Day/Night | Wert der Tag/Nacht-GA invertieren |
|
|
157
|
+
| Status beim Start lesen | Status beim Start lesen und bei Start/Wiederverbindung als Ereignis auf den KNX-Bus senden (Standard: „Nein“) |
|
|
158
|
+
| Tagmodus erzwingen | Der Tagmodus kann durch schnelles Aus- und Einschalten dieser Leuchte oder aller Leuchtenknoten aktiviert werden. |
|
|
157
159
|
| Node I/O pins | Ein/Ausblenden der Eingangs/Ausgangs-Pins; Input folgt HUE API v2 (z. B. <code>msg.on = { on: true }</code>) |
|
|
158
160
|
|
|
159
161
|
Hinweis: Start/Stop-Dimmen im KNX-Modus wird über die üblichen Start/Stop-Telegramme gesteuert.
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"hue_bridge": "Hue Bridge"
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
|
+
"locale": "de",
|
|
8
|
+
"tutorials_link": "KNX-Ultimate-Videotutorials (YouTube-Playlist)",
|
|
9
|
+
"editor_unavailable": "Der ausgewählte Hue-Editor ist nicht verfügbar. Starten Sie Node-RED nach der Paketaktualisierung neu.",
|
|
7
10
|
"device_function": "Gerätefunktion",
|
|
8
11
|
"hue_device": "Hue-Gerät",
|
|
9
12
|
"hue_device_placeholder": "Hue-Gerät oder -Ressource suchen",
|
|
@@ -11,7 +11,7 @@ Er ersetzt die unveröffentlichten getrennten Matter-Controller-Nodes und behäl
|
|
|
11
11
|
|
|
12
12
|
|Feld|Beschreibung|
|
|
13
13
|
|--|--|
|
|
14
|
-
| KNX GW | KNX-Gateway zum Schreiben und Beantworten der konfigurierten Gruppenadressen. Kann leer bleiben, wenn nur der Node-RED-Ausgang benötigt wird. |
|
|
14
|
+
| KNX GW | KNX-Gateway zum Schreiben und Beantworten der konfigurierten Gruppenadressen. Kann leer bleiben, wenn nur der Node-RED-Ausgang benötigt wird. Das gespeicherte Gateway bleibt während der Initialisierung des Editors ausgewählt und ändert sich erst nach einer ausdrücklichen Benutzerauswahl. |
|
|
15
15
|
| Matter controller | Matter-Controller-Konfigurationsknoten, in dem das Gerät gekoppelt wurde. |
|
|
16
16
|
| Matter device | Matter-Endpunkt aus den gekoppelten Geräten. Die UI wird aus den echten Fähigkeiten neu aufgebaut. |
|
|
17
17
|
| Switch / Steckdose / Licht On-Off | On/Off-Befehls- und Status-Gruppenadressen, normalerweise DPT `1.001`. |
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"hue_bridge": "Hue Bridge"
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
|
+
"locale": "en",
|
|
8
|
+
"tutorials_link": "KNX-Ultimate video tutorials (YouTube playlist)",
|
|
9
|
+
"editor_unavailable": "The selected Hue editor is unavailable. Restart Node-RED after updating the package.",
|
|
7
10
|
"device_function": "Device function",
|
|
8
11
|
"hue_device": "Hue device",
|
|
9
12
|
"hue_device_placeholder": "Search your Hue device or resource",
|
|
@@ -11,7 +11,7 @@ It replaces the unpublished per-device Matter controller nodes and keeps the ful
|
|
|
11
11
|
|
|
12
12
|
|Field|Description|
|
|
13
13
|
|--|--|
|
|
14
|
-
| KNX GW | KNX gateway used to write and answer the configured group addresses. It can be left empty when only Node-RED output is needed. |
|
|
14
|
+
| KNX GW | KNX gateway used to write and answer the configured group addresses. It can be left empty when only Node-RED output is needed. The saved gateway remains selected while the editor initializes and changes only after an explicit user selection. |
|
|
15
15
|
| Matter controller | Matter controller configuration node where the device has been commissioned. |
|
|
16
16
|
| Matter device | Matter endpoint selected from commissioned devices. The UI is rebuilt from its real capabilities. |
|
|
17
17
|
| Switch / Plug / Light On-Off | On/Off command and status group addresses, usually DPT `1.001`. |
|