node-red-contrib-knx-ultimate 6.3.6 → 6.3.7
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 +5 -0
- package/nodes/hue-config.js +12 -2
- package/nodes/knxUltimateHueController.html +230 -31
- package/nodes/locales/de/knxUltimateHueController.html +7 -1
- package/nodes/locales/de/knxUltimateHueController.json +4 -1
- package/nodes/locales/en/knxUltimateHueController.html +7 -1
- package/nodes/locales/en/knxUltimateHueController.json +4 -1
- package/nodes/locales/es/knxUltimateHueController.html +7 -1
- package/nodes/locales/es/knxUltimateHueController.json +4 -1
- package/nodes/locales/fr/knxUltimateHueController.html +7 -1
- package/nodes/locales/fr/knxUltimateHueController.json +4 -1
- package/nodes/locales/it/knxUltimateHueController.html +7 -1
- package/nodes/locales/it/knxUltimateHueController.json +4 -1
- package/nodes/locales/zh-CN/knxUltimateHueController.html +7 -1
- package/nodes/locales/zh-CN/knxUltimateHueController.json +4 -1
- package/package.json +1 -1
- package/resources/hueControllerProfiles.js +54 -17
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 6.3.7** - August 2026<br/>
|
|
10
|
+
|
|
11
|
+
- **HUE Controller**: improved gateway-dependent editor visibility, preserving all KNX mappings and the correct tab layout when gateways are deselected, reselected or temporarily unavailable; obsolete headings and decorative tab images were removed.<br/>
|
|
12
|
+
- **HUE Controller**: added clearer Hue Bridge loading/offline feedback, kept saved mappings visible while disabling unavailable device discovery, and restored the complete previous configuration when the editor is cancelled after previewing another Hue resource.<br/>
|
|
13
|
+
|
|
9
14
|
**Version 6.3.6** - August 2026<br/>
|
|
10
15
|
|
|
11
16
|
- **HUE Controller**: grouped lights now always show the complete Light mapping editor when KNX is configured, without checking child-light capabilities.<br/>
|
package/nodes/hue-config.js
CHANGED
|
@@ -367,16 +367,26 @@ module.exports = (RED) => {
|
|
|
367
367
|
node.sysLogger?.warn(`KNXUltimateHue: getResources force refresh failed ${error.message}`)
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
|
-
if (node.hueAllResources === undefined) return
|
|
371
370
|
if (_rtype === 'hue_controller') {
|
|
371
|
+
const ready = node.linkStatus === 'connected' && Array.isArray(node.hueAllResources)
|
|
372
|
+
if (!ready) {
|
|
373
|
+
return {
|
|
374
|
+
devices: [],
|
|
375
|
+
ready: false,
|
|
376
|
+
connectionStatus: node.linkStatus
|
|
377
|
+
}
|
|
378
|
+
}
|
|
372
379
|
const resourceResults = await Promise.all(HUE_CONTROLLER_RESOURCE_TYPES.map(async (resourceType) => {
|
|
373
380
|
const result = await node.getResources(resourceType)
|
|
374
381
|
return [resourceType, Array.isArray(result?.devices) ? result.devices : []]
|
|
375
382
|
}))
|
|
376
383
|
return {
|
|
377
|
-
devices: buildHueControllerResourceCatalog(Object.fromEntries(resourceResults))
|
|
384
|
+
devices: buildHueControllerResourceCatalog(Object.fromEntries(resourceResults)),
|
|
385
|
+
ready: true,
|
|
386
|
+
connectionStatus: node.linkStatus
|
|
378
387
|
}
|
|
379
388
|
}
|
|
389
|
+
if (node.hueAllResources === undefined) return
|
|
380
390
|
// Returns capitalized string
|
|
381
391
|
function capStr (s) {
|
|
382
392
|
if (typeof s !== 'string') return ''
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
// gateways exist) and for "Add new..." (when none exist).
|
|
38
38
|
const KNX_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__']);
|
|
39
39
|
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__']);
|
|
40
|
+
const normalizeKnxServerSelection = (value) => {
|
|
41
|
+
const normalized = value === undefined || value === null ? '' : String(value).trim();
|
|
42
|
+
return KNX_EMPTY_SERVER_VALUES.has(normalized.toLowerCase()) ? '' : normalized;
|
|
43
|
+
};
|
|
44
|
+
const normalizeHueServerSelection = (value) => {
|
|
45
|
+
const normalized = value === undefined || value === null ? '' : String(value).trim();
|
|
46
|
+
return HUE_EMPTY_SERVER_VALUES.has(normalized.toLowerCase()) ? '' : normalized;
|
|
47
|
+
};
|
|
40
48
|
const TUTORIALS_URL = 'https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E';
|
|
41
49
|
const profileBundle = window.KNXUltimateHueControllerProfiles;
|
|
42
50
|
|
|
@@ -183,10 +191,20 @@
|
|
|
183
191
|
outline: 3px solid #fbbf24;
|
|
184
192
|
outline-offset: 2px;
|
|
185
193
|
}
|
|
186
|
-
#hue-controller-profile-editor.hue-controller-no-knx .hue-knx-section,
|
|
187
194
|
#hue-controller-profile-editor.hue-controller-light-profile.hue-controller-no-knx #tabs {
|
|
188
195
|
display: none !important;
|
|
189
196
|
}
|
|
197
|
+
#hue-controller-profile-editor:not(.hue-controller-light-profile).hue-controller-no-knx .hue-knx-section {
|
|
198
|
+
display: none !important;
|
|
199
|
+
}
|
|
200
|
+
#hue-controller-profile-editor .hue-controller-knx-flow-only-notice {
|
|
201
|
+
box-sizing: border-box;
|
|
202
|
+
padding: 9px 12px;
|
|
203
|
+
margin: 0 0 12px;
|
|
204
|
+
border-left: 4px solid #d79b00;
|
|
205
|
+
background: #fff8df;
|
|
206
|
+
color: #4d3a00;
|
|
207
|
+
}
|
|
190
208
|
/* Every profile inherited slightly different fixed KNX widths. Mark
|
|
191
209
|
actual DPT rows after mounting and compact only their DPT/Name
|
|
192
210
|
controls. No wrapping keeps one mapping on one visual line, while the
|
|
@@ -299,6 +317,16 @@
|
|
|
299
317
|
let selectedHueResource;
|
|
300
318
|
let activeHueServerId;
|
|
301
319
|
let hueDevicePickerMouseDown = false;
|
|
320
|
+
let hueCatalogReady = false;
|
|
321
|
+
let hueReadinessTimer;
|
|
322
|
+
let hueUnreachableTimer;
|
|
323
|
+
let hueReadinessAttempts = 0;
|
|
324
|
+
let hueReadinessErrorShown = false;
|
|
325
|
+
let hueBridgeUnreachable = false;
|
|
326
|
+
let hueEditorClosed = false;
|
|
327
|
+
const HUE_READINESS_INTERVAL_MS = 2000;
|
|
328
|
+
const HUE_READINESS_MAX_ATTEMPTS = 60;
|
|
329
|
+
const HUE_UNREACHABLE_TIMEOUT_MS = 15000;
|
|
302
330
|
const defaultHueDevicePlaceholder = $('#node-input-name').attr('placeholder') || '';
|
|
303
331
|
|
|
304
332
|
const hasLegacyHueNodes = editorContainsLegacyHueNodes();
|
|
@@ -319,6 +347,24 @@
|
|
|
319
347
|
try { return JSON.parse(JSON.stringify(value)); } catch (error) { return value; }
|
|
320
348
|
};
|
|
321
349
|
|
|
350
|
+
// Several profile editors update the live node object while the dialog
|
|
351
|
+
// is open so switching device functions can mount the correct fields.
|
|
352
|
+
// Node-RED cannot roll those direct mutations back by itself on Cancel,
|
|
353
|
+
// therefore retain the complete persisted defaults before editing.
|
|
354
|
+
const originalEditorState = {};
|
|
355
|
+
Object.keys(buildDefaults()).forEach((key) => {
|
|
356
|
+
originalEditorState[key] = {
|
|
357
|
+
present: Object.prototype.hasOwnProperty.call(controllerNode, key),
|
|
358
|
+
value: cloneEditorValue(controllerNode[key])
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
const restoreOriginalEditorState = () => {
|
|
362
|
+
Object.entries(originalEditorState).forEach(([key, snapshot]) => {
|
|
363
|
+
if (snapshot.present) controllerNode[key] = cloneEditorValue(snapshot.value);
|
|
364
|
+
else delete controllerNode[key];
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
|
|
322
368
|
const collectCurrentEditor = () => {
|
|
323
369
|
// Collect before unmounting. Exact keys come from the selected private
|
|
324
370
|
// definition, avoiding broad DOM scans and widget-added CSS classes.
|
|
@@ -326,7 +372,10 @@
|
|
|
326
372
|
const definition = getProfileDefinition(controllerNode.hueControllerType);
|
|
327
373
|
const draft = {};
|
|
328
374
|
Object.keys(definition?.defaults || {}).forEach((key) => {
|
|
329
|
-
const
|
|
375
|
+
const fieldValue = readFieldValue($(`#node-input-${key}`), controllerNode[key]);
|
|
376
|
+
const value = key === 'server'
|
|
377
|
+
? normalizeKnxServerSelection(fieldValue)
|
|
378
|
+
: (key === 'serverHue' ? normalizeHueServerSelection(fieldValue) : fieldValue);
|
|
330
379
|
controllerNode[key] = value;
|
|
331
380
|
legacyContext[key] = value;
|
|
332
381
|
draft[key] = cloneEditorValue(value);
|
|
@@ -342,7 +391,7 @@
|
|
|
342
391
|
// namespaces that belong specifically to this wrapper.
|
|
343
392
|
if (!activeProfile || !legacyContext) return;
|
|
344
393
|
$('#node-input-server').off('.knxUltimateHueControllerLightVisibility');
|
|
345
|
-
$('#node-input-server')
|
|
394
|
+
$(document).off('change.knxUltimateHueControllerKnxVisibility', '#node-input-server');
|
|
346
395
|
const definition = getProfileDefinition(controllerNode.hueControllerType);
|
|
347
396
|
if (definition && typeof definition.oneditcancel === 'function') {
|
|
348
397
|
definition.oneditcancel.call(legacyContext);
|
|
@@ -395,7 +444,11 @@
|
|
|
395
444
|
const getRawHueResourceId = (value) => String(value || '').split('#')[0];
|
|
396
445
|
|
|
397
446
|
const updateSelectedDevicePresentation = (controllerType = controllerNode.hueControllerType) => {
|
|
398
|
-
|
|
447
|
+
// A persisted device remains visible while its Hue Bridge is offline.
|
|
448
|
+
// Catalog readiness controls discovery and editing of the device
|
|
449
|
+
// selection, not whether saved mappings remain usable in the editor.
|
|
450
|
+
const hueServerSelected = resolveHueServerId({ allowStored: true }) !== '';
|
|
451
|
+
const hasDevice = hueServerSelected && getHueDeviceValue() !== '';
|
|
399
452
|
const profile = PROFILE_TYPES[controllerType] || PROFILE_TYPES.light;
|
|
400
453
|
const typeLabel = controllerText(`types.${controllerType}`, profile.label);
|
|
401
454
|
$('#hue-controller-selected-device-type-value').text(typeLabel);
|
|
@@ -405,6 +458,25 @@
|
|
|
405
458
|
$('#node-input-hueControllerType').val(controllerType);
|
|
406
459
|
};
|
|
407
460
|
|
|
461
|
+
const updateHueDeviceControlVisibility = () => {
|
|
462
|
+
const hueServerSelected = resolveHueServerId({ allowStored: true }) !== '';
|
|
463
|
+
const hasPersistedDevice = getHueDeviceValue() !== '';
|
|
464
|
+
const deviceControlVisible = hueServerSelected && (hueCatalogReady || hasPersistedDevice);
|
|
465
|
+
const deviceCatalogPending = hueServerSelected && !hueCatalogReady;
|
|
466
|
+
$('#hue-controller-device-row').toggle(deviceControlVisible);
|
|
467
|
+
$('#node-input-name').prop('disabled', hueServerSelected && !hueCatalogReady);
|
|
468
|
+
$('#hue-controller-devices-loading').toggle(deviceCatalogPending);
|
|
469
|
+
$('#hue-controller-devices-waiting').toggle(!hueBridgeUnreachable);
|
|
470
|
+
$('#hue-controller-devices-unreachable').toggle(hueBridgeUnreachable);
|
|
471
|
+
if (!deviceControlVisible) {
|
|
472
|
+
$('#hue-controller-selected-device-type').hide();
|
|
473
|
+
$('#hue-controller-locate-device').hide();
|
|
474
|
+
$('#hue-controller-profile-editor').hide();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
updateSelectedDevicePresentation();
|
|
478
|
+
};
|
|
479
|
+
|
|
408
480
|
const filterHueResources = (term) => {
|
|
409
481
|
const search = String(term || '').replace(/exactmatch/gi, '').trim().toLowerCase();
|
|
410
482
|
return cachedHueResources.filter((item) => {
|
|
@@ -441,38 +513,114 @@
|
|
|
441
513
|
const reply = typeof response === 'function' ? response : () => { };
|
|
442
514
|
const hueServerId = resolveHueServerId({ allowStored: true });
|
|
443
515
|
if (hueServerId === '') {
|
|
516
|
+
hueCatalogReady = false;
|
|
517
|
+
updateHueDeviceControlVisibility();
|
|
444
518
|
reply([]);
|
|
445
519
|
return;
|
|
446
520
|
}
|
|
447
521
|
if (!forceRefresh && cachedHueResources.length > 0) {
|
|
522
|
+
hueCatalogReady = true;
|
|
523
|
+
updateHueDeviceControlVisibility();
|
|
448
524
|
reply(filterHueResources(term));
|
|
449
525
|
return;
|
|
450
526
|
}
|
|
451
|
-
$('#hue-controller-devices-loading').show();
|
|
452
527
|
const refreshQuery = forceRefresh ? '&forceRefresh=1' : '';
|
|
453
528
|
$.getJSON(`KNXUltimateGetResourcesHUE?rtype=hue_controller&serverId=${encodeURIComponent(hueServerId)}${refreshQuery}&_=${Date.now()}`, (data) => {
|
|
529
|
+
if (hueEditorClosed) return;
|
|
530
|
+
if (resolveHueServerId({ allowStored: true }) !== hueServerId) {
|
|
531
|
+
reply([]);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
454
534
|
const devices = Array.isArray(data) ? data : (Array.isArray(data?.devices) ? data.devices : []);
|
|
455
535
|
cachedHueResources = devices.filter((item) => item && PROFILE_TYPES[item.controllerType] && item.id && item.hueDevice);
|
|
536
|
+
hueCatalogReady = data?.ready === true;
|
|
456
537
|
$('#node-input-name').attr('placeholder', cachedHueResources.length > 0
|
|
457
538
|
? defaultHueDevicePlaceholder
|
|
458
539
|
: controllerText('no_devices', 'No Hue resources found. Check the Hue Bridge and press refresh.'));
|
|
459
|
-
|
|
540
|
+
updateHueDeviceControlVisibility();
|
|
541
|
+
if (hueCatalogReady) reconcileSavedHueResource();
|
|
460
542
|
reply(filterHueResources(term));
|
|
461
543
|
}).fail(() => {
|
|
544
|
+
if (hueEditorClosed) return;
|
|
462
545
|
cachedHueResources = [];
|
|
546
|
+
hueCatalogReady = false;
|
|
547
|
+
updateHueDeviceControlVisibility();
|
|
463
548
|
$('#node-input-name').attr('placeholder', controllerText('no_devices', 'No Hue resources found. Check the Hue Bridge and press refresh.'));
|
|
464
549
|
reply([]);
|
|
465
|
-
}).always(() => {
|
|
466
|
-
$('#hue-controller-devices-loading').hide();
|
|
467
550
|
});
|
|
468
551
|
};
|
|
469
552
|
|
|
553
|
+
const clearHueReadinessTimer = () => {
|
|
554
|
+
if (hueReadinessTimer !== undefined) clearTimeout(hueReadinessTimer);
|
|
555
|
+
hueReadinessTimer = undefined;
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
const clearHueUnreachableTimer = () => {
|
|
559
|
+
if (hueUnreachableTimer !== undefined) clearTimeout(hueUnreachableTimer);
|
|
560
|
+
hueUnreachableTimer = undefined;
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const probeHueBridgeReadiness = ({ reset = false, openPicker = false } = {}) => {
|
|
564
|
+
clearHueReadinessTimer();
|
|
565
|
+
if (reset) {
|
|
566
|
+
hueReadinessAttempts = 0;
|
|
567
|
+
hueReadinessErrorShown = false;
|
|
568
|
+
hueBridgeUnreachable = false;
|
|
569
|
+
clearHueUnreachableTimer();
|
|
570
|
+
}
|
|
571
|
+
const expectedServerId = resolveHueServerId({ allowStored: true });
|
|
572
|
+
if (expectedServerId === '') {
|
|
573
|
+
clearHueUnreachableTimer();
|
|
574
|
+
hueCatalogReady = false;
|
|
575
|
+
updateHueDeviceControlVisibility();
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (reset) {
|
|
579
|
+
hueUnreachableTimer = setTimeout(() => {
|
|
580
|
+
if (hueEditorClosed) return;
|
|
581
|
+
if (resolveHueServerId({ allowStored: true }) !== expectedServerId || hueCatalogReady) return;
|
|
582
|
+
hueBridgeUnreachable = true;
|
|
583
|
+
updateHueDeviceControlVisibility();
|
|
584
|
+
}, HUE_UNREACHABLE_TIMEOUT_MS);
|
|
585
|
+
}
|
|
586
|
+
updateHueDeviceControlVisibility();
|
|
587
|
+
hueReadinessAttempts += 1;
|
|
588
|
+
fetchHueResources('', (items) => {
|
|
589
|
+
if (resolveHueServerId({ allowStored: true }) !== expectedServerId) return;
|
|
590
|
+
if (hueCatalogReady) {
|
|
591
|
+
clearHueUnreachableTimer();
|
|
592
|
+
hueBridgeUnreachable = false;
|
|
593
|
+
updateHueDeviceControlVisibility();
|
|
594
|
+
if (openPicker && items.length) $('#node-input-name').autocomplete('search', '');
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (hueReadinessAttempts >= HUE_READINESS_MAX_ATTEMPTS) {
|
|
598
|
+
if (!hueReadinessErrorShown) {
|
|
599
|
+
hueReadinessErrorShown = true;
|
|
600
|
+
updateHueDeviceControlVisibility();
|
|
601
|
+
RED.notify(controllerText(
|
|
602
|
+
'hue_bridge_not_ready',
|
|
603
|
+
'The Hue Bridge did not become ready. Check its connection and reopen the editor.'
|
|
604
|
+
), { type: 'error', fixed: true });
|
|
605
|
+
}
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
hueReadinessTimer = setTimeout(() => {
|
|
609
|
+
probeHueBridgeReadiness({ openPicker });
|
|
610
|
+
}, HUE_READINESS_INTERVAL_MS);
|
|
611
|
+
}, { forceRefresh: true });
|
|
612
|
+
};
|
|
613
|
+
|
|
470
614
|
const configureUnifiedDeviceControl = () => {
|
|
471
615
|
const $deviceName = $('#node-input-name');
|
|
472
616
|
if (!$deviceName.length) return;
|
|
473
617
|
$deviceName.autocomplete({
|
|
474
618
|
minLength: 0,
|
|
475
619
|
source(request, response) {
|
|
620
|
+
if (!hueCatalogReady) {
|
|
621
|
+
response([]);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
476
624
|
fetchHueResources(request.term, response);
|
|
477
625
|
},
|
|
478
626
|
select(event, ui) {
|
|
@@ -615,6 +763,15 @@
|
|
|
615
763
|
// out of the embedded editor makes every device profile more compact.
|
|
616
764
|
$container.find('.form-tips').remove();
|
|
617
765
|
|
|
766
|
+
const $knxFlowOnlyNotice = $container.find('.hue-controller-knx-flow-only-notice');
|
|
767
|
+
let knxFlowOnlyNoticeText = 'To display the group-address mappings, select a KNX gateway. Without one, this node can only be controlled by input messages from the flow.';
|
|
768
|
+
try {
|
|
769
|
+
const noticeKey = 'node-red-contrib-knx-ultimate/knxUltimateHueController:knxUltimateHueController.knx_gateway_required_for_mappings';
|
|
770
|
+
const translatedNotice = RED._(noticeKey);
|
|
771
|
+
if (translatedNotice && translatedNotice !== noticeKey) knxFlowOnlyNoticeText = translatedNotice;
|
|
772
|
+
} catch (error) { /* use English fallback */ }
|
|
773
|
+
$knxFlowOnlyNotice.text(knxFlowOnlyNoticeText);
|
|
774
|
+
|
|
618
775
|
const updateControllerKnxVisibility = () => {
|
|
619
776
|
// Node-RED has used several sentinel values for an empty config-node
|
|
620
777
|
// selector. During editor bootstrap the DOM can temporarily expose
|
|
@@ -631,11 +788,21 @@
|
|
|
631
788
|
const hasStoredServer = !KNX_EMPTY_SERVER_VALUES.has(storedServerValue);
|
|
632
789
|
const knxDisabled = !hasDomServer && !hasStoredServer;
|
|
633
790
|
$container.toggleClass('hue-controller-no-knx', knxDisabled);
|
|
791
|
+
$knxFlowOnlyNotice.toggle(controllerType === 'light' && knxDisabled);
|
|
792
|
+
};
|
|
793
|
+
const handleControllerKnxSelection = function () {
|
|
794
|
+
// An explicit user choice must supersede the persisted bootstrap
|
|
795
|
+
// fallback. Node-RED can represent "none" as `_ADD_`, so normalize
|
|
796
|
+
// it before both editors recalculate their visibility.
|
|
797
|
+
const selectedServer = normalizeKnxServerSelection($(this).val());
|
|
798
|
+
controllerNode.server = selectedServer;
|
|
799
|
+
if (legacyContext) legacyContext.server = selectedServer;
|
|
800
|
+
updateControllerKnxVisibility();
|
|
634
801
|
};
|
|
635
802
|
updateControllerKnxVisibility();
|
|
636
|
-
$(
|
|
637
|
-
.off('.knxUltimateHueControllerKnxVisibility')
|
|
638
|
-
.on('change.knxUltimateHueControllerKnxVisibility',
|
|
803
|
+
$(document)
|
|
804
|
+
.off('change.knxUltimateHueControllerKnxVisibility', '#node-input-server')
|
|
805
|
+
.on('change.knxUltimateHueControllerKnxVisibility', '#node-input-server', handleControllerKnxSelection);
|
|
639
806
|
updateSelectedDevicePresentation(controllerType);
|
|
640
807
|
focusProfileHelp(controllerType);
|
|
641
808
|
};
|
|
@@ -648,34 +815,49 @@
|
|
|
648
815
|
.off('.knxUltimateHueControllerDevice')
|
|
649
816
|
.on('click.knxUltimateHueControllerDevice', () => {
|
|
650
817
|
cachedHueResources = [];
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}, { forceRefresh: true });
|
|
818
|
+
hueCatalogReady = false;
|
|
819
|
+
probeHueBridgeReadiness({ reset: true, openPicker: true });
|
|
654
820
|
});
|
|
655
|
-
$(
|
|
656
|
-
.off('.
|
|
657
|
-
.on('change.
|
|
658
|
-
const nextServerId =
|
|
821
|
+
$(document)
|
|
822
|
+
.off('change.knxUltimateHueControllerHueServer', '#node-input-serverHue')
|
|
823
|
+
.on('change.knxUltimateHueControllerHueServer', '#node-input-serverHue', () => {
|
|
824
|
+
const nextServerId = normalizeHueServerSelection($('#node-input-serverHue').val());
|
|
659
825
|
if (nextServerId === activeHueServerId) return;
|
|
660
826
|
activeHueServerId = nextServerId;
|
|
827
|
+
controllerNode.serverHue = nextServerId;
|
|
828
|
+
if (legacyContext) legacyContext.serverHue = nextServerId;
|
|
829
|
+
clearHueReadinessTimer();
|
|
830
|
+
clearHueUnreachableTimer();
|
|
661
831
|
cachedHueResources = [];
|
|
832
|
+
hueCatalogReady = false;
|
|
833
|
+
hueReadinessErrorShown = false;
|
|
834
|
+
hueBridgeUnreachable = false;
|
|
662
835
|
selectedHueResource = undefined;
|
|
663
836
|
controllerNode.name = '';
|
|
664
837
|
controllerNode.hueDevice = '';
|
|
665
838
|
$('#node-input-name, #node-input-hueDevice').val('');
|
|
839
|
+
updateHueDeviceControlVisibility();
|
|
666
840
|
updateSelectedDevicePresentation();
|
|
841
|
+
if (nextServerId !== '') probeHueBridgeReadiness({ reset: true });
|
|
667
842
|
});
|
|
668
843
|
|
|
669
844
|
this._hueControllerCollectEditor = collectCurrentEditor;
|
|
670
845
|
this._hueControllerGetLegacyContext = () => legacyContext;
|
|
671
846
|
this._hueControllerGetProfile = () => activeProfile;
|
|
672
847
|
this._hueControllerCloseMigration = () => closeMigrationDialog();
|
|
848
|
+
this._hueControllerRestoreOriginalState = restoreOriginalEditorState;
|
|
673
849
|
this._hueControllerCleanupDevice = () => {
|
|
850
|
+
hueEditorClosed = true;
|
|
851
|
+
clearHueReadinessTimer();
|
|
852
|
+
clearHueUnreachableTimer();
|
|
674
853
|
$('#hue-controller-refresh-devices, #node-input-serverHue, #node-input-name').off('.knxUltimateHueControllerDevice');
|
|
854
|
+
$(document).off('change.knxUltimateHueControllerHueServer', '#node-input-serverHue');
|
|
855
|
+
$(document).off('change.knxUltimateHueControllerKnxVisibility', '#node-input-server');
|
|
675
856
|
delete controllerNode.__configureHueControllerDeviceControl;
|
|
676
857
|
};
|
|
677
858
|
renderProfileEditor(selectedType);
|
|
678
|
-
|
|
859
|
+
updateHueDeviceControlVisibility();
|
|
860
|
+
probeHueBridgeReadiness({ reset: true });
|
|
679
861
|
},
|
|
680
862
|
oneditsave() {
|
|
681
863
|
// Preserve lifecycle order: close auxiliary UI, collect mounted fields,
|
|
@@ -705,11 +887,12 @@
|
|
|
705
887
|
delete this._hueControllerGetLegacyContext;
|
|
706
888
|
delete this._hueControllerGetProfile;
|
|
707
889
|
delete this._hueControllerCloseMigration;
|
|
890
|
+
delete this._hueControllerRestoreOriginalState;
|
|
708
891
|
delete this._hueControllerCleanupDevice;
|
|
709
892
|
},
|
|
710
893
|
oneditcancel() {
|
|
711
|
-
// Cancel releases profile-owned editor state
|
|
712
|
-
//
|
|
894
|
+
// Cancel releases profile-owned editor state and restores the snapshot
|
|
895
|
+
// taken before device/profile previews mutated the live node object.
|
|
713
896
|
if (typeof this._hueControllerCloseMigration === 'function') this._hueControllerCloseMigration();
|
|
714
897
|
const profile = typeof this._hueControllerGetProfile === 'function'
|
|
715
898
|
? this._hueControllerGetProfile()
|
|
@@ -722,7 +905,12 @@
|
|
|
722
905
|
definition.oneditcancel.call(legacyContext || this);
|
|
723
906
|
}
|
|
724
907
|
if (typeof this._hueControllerCleanupDevice === 'function') this._hueControllerCleanupDevice();
|
|
908
|
+
if (typeof this._hueControllerRestoreOriginalState === 'function') this._hueControllerRestoreOriginalState();
|
|
909
|
+
delete this._hueControllerCollectEditor;
|
|
910
|
+
delete this._hueControllerGetLegacyContext;
|
|
911
|
+
delete this._hueControllerGetProfile;
|
|
725
912
|
delete this._hueControllerCloseMigration;
|
|
913
|
+
delete this._hueControllerRestoreOriginalState;
|
|
726
914
|
delete this._hueControllerCleanupDevice;
|
|
727
915
|
}
|
|
728
916
|
});
|
|
@@ -759,11 +947,18 @@
|
|
|
759
947
|
</label>
|
|
760
948
|
<input type="text" id="node-input-serverHue">
|
|
761
949
|
</div>
|
|
762
|
-
<
|
|
763
|
-
|
|
764
|
-
<
|
|
765
|
-
|
|
766
|
-
|
|
950
|
+
<div class="form-row" id="hue-controller-devices-loading" style="display:none; color:#666;">
|
|
951
|
+
<label></label>
|
|
952
|
+
<span id="hue-controller-devices-waiting">
|
|
953
|
+
<i class="fa fa-circle-notch fa-spin" style="color:#1b7d33;"></i>
|
|
954
|
+
<span data-i18n="knxUltimateHueController.loading_hue_devices"></span>
|
|
955
|
+
</span>
|
|
956
|
+
<span id="hue-controller-devices-unreachable" style="display:none; color:#b45309;">
|
|
957
|
+
<i class="fa fa-exclamation-triangle"></i>
|
|
958
|
+
<span data-i18n="knxUltimateHueController.hue_bridge_unreachable"></span>
|
|
959
|
+
</span>
|
|
960
|
+
</div>
|
|
961
|
+
<div class="form-row" id="hue-controller-device-row" style="display:none;">
|
|
767
962
|
<label for="node-input-name">
|
|
768
963
|
<i class="fa fa-play-circle"></i>
|
|
769
964
|
<span data-i18n="knxUltimateHueController.hue_device"></span>
|
|
@@ -778,9 +973,6 @@
|
|
|
778
973
|
style="display:none; margin-left:6px; color:#ff9800; border-color:#ff9800;" data-i18n="[title]knxUltimateHueController.locate_device">
|
|
779
974
|
<i class="fa fa-play"></i>
|
|
780
975
|
</button>
|
|
781
|
-
<span id="hue-controller-devices-loading" style="margin-left:6px; display:none; color:#1b7d33;">
|
|
782
|
-
<i class="fa fa-circle-notch fa-spin"></i>
|
|
783
|
-
</span>
|
|
784
976
|
<span id="hue-controller-selected-device-type" style="display:none; margin-left:10px; color:#666; white-space:nowrap;">
|
|
785
977
|
<i class="fa fa-cubes"></i>
|
|
786
978
|
<span data-i18n="knxUltimateHueController.device_type"></span>:
|
|
@@ -789,7 +981,6 @@
|
|
|
789
981
|
<input type="hidden" id="node-input-hueDevice">
|
|
790
982
|
<input type="hidden" id="node-input-hueControllerType">
|
|
791
983
|
</div>
|
|
792
|
-
<br />
|
|
793
984
|
<div id="hue-controller-profile-editor"></div>
|
|
794
985
|
<br/>
|
|
795
986
|
<br/>
|
|
@@ -806,6 +997,12 @@ The unified controller for Hue API v2 resources. Choose a Hue device or resource
|
|
|
806
997
|
|
|
807
998
|
Clicking or focusing the Hue device field always opens the complete resource list, even when a device is already selected. Typing continues to filter the list.
|
|
808
999
|
|
|
1000
|
+
The Hue device picker remains hidden until the selected Hue Bridge is connected and its resource catalog is ready. A loading indicator is shown while the editor waits; selecting `none` hides the indicator and device controls.
|
|
1001
|
+
|
|
1002
|
+
If the node already has a saved Hue device, its picker and KNX mapping tabs remain available while the selected bridge is offline. Resource discovery continues in the background and a fixed red error appears if the bridge does not become ready.
|
|
1003
|
+
|
|
1004
|
+
For Light resources without a KNX gateway, the editor shows a flow-only notice in place of the duplicate “Philips HUE” heading.
|
|
1005
|
+
|
|
809
1006
|
The controller supports lights and grouped lights, plugs, buttons, Tap dial, motion resources, contacts, light level, temperature, humidity, scenes, battery, Zigbee connectivity, and device software update resources.
|
|
810
1007
|
|
|
811
1008
|
## Convert a legacy HUE flow
|
|
@@ -822,7 +1019,9 @@ Each function uses its private mature runtime contract. Hue events remain status
|
|
|
822
1019
|
|
|
823
1020
|
Dedicated legacy HUE nodes remain registered for existing flows, but are frozen and receive no new features or maintenance updates. Node-RED hides their special `deprecated` category from the palette, while existing instances remain editable, deployable, lighter than HUE Controller and visibly marked `(deprecated)` on the canvas. They also show a migration notice at the top of their editor. Before starting, [watch the explanatory migration video on YouTube](https://youtu.be/f0Evf2QFI7c). Use HUE Controller for new work or migrate them with the fully local conversion button; afterwards, the browser opens only an editable usage email draft without navigating away from Node-RED. The email is never sent automatically. Before Deploy, inspect every modified HUE node and verify its function, configuration references, pins and wiring. A fixed Node-RED completion message remains visible until you click **OK** and offers an optional support button; the donation page opens only when that button is clicked.
|
|
824
1021
|
|
|
825
|
-
When no **KNX Gateway** is selected (including **none** and **Add new...**), the
|
|
1022
|
+
When no **KNX Gateway** is selected (including **none** and **Add new...**), only the Light mapping tabs are hidden. Their mounted GA, DPT and Name fields remain unchanged and reappear immediately with the correct vertical tab layout when the gateway is selected again. Hue resource selection and flow-only options remain available.
|
|
1023
|
+
|
|
1024
|
+
The Light mapping tabs contain only configuration controls; the decorative Dim, Tunable White and RGB images have been removed.
|
|
826
1025
|
|
|
827
1026
|
For a single light, the **Dim**, **Tunable White**, **RGB/HSV**, and native-effects sections are enabled from the live capabilities reported by the selected Hue API v2 light resource. A light that exposes `dimming`, `color_temperature`, or `color` therefore shows the corresponding KNX mappings automatically.
|
|
828
1027
|
|
|
@@ -4,9 +4,13 @@
|
|
|
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>
|
|
6
6
|
<p>Ein Klick oder Fokus auf das Gerätefeld öffnet immer die vollständige Hue-Ressourcenliste, auch wenn bereits ein Gerät ausgewählt ist. Beim Tippen wird die Liste weiterhin gefiltert.</p>
|
|
7
|
+
<p>Die Auswahl einer anderen Hue-Ressource ist nur eine Vorschau, bis der Editor gespeichert wird. Mit <code>Abbrechen</code> werden das zuvor gespeicherte Gerät, die Funktion und die Zuordnungen wiederhergestellt.</p>
|
|
8
|
+
<p>Die Hue-Geräteauswahl bleibt ausgeblendet, bis eine Hue Bridge ausgewählt und verbunden ist und ihren Ressourcenkatalog geladen hat. Während der Wartezeit wird eine Ladeanzeige angezeigt. Wird die Bridge-Auswahl auf <code>none</code> zurückgesetzt, werden Ladeanzeige, Geräteauswahl und Geräteeditor wieder ausgeblendet.</p>
|
|
9
|
+
<p>Wenn im Knoten bereits ein Hue-Gerät gespeichert ist, bleibt dessen Auswahl sichtbar, aber deaktiviert, während die ausgewählte Bridge offline ist; die KNX-Zuordnungsregister bleiben verfügbar. Nach 15 Sekunden meldet die Warteanzeige, dass die Hue Bridge nicht erreichbar ist. Die Ressourcensuche läuft im Hintergrund weiter; wird die Bridge nicht bereit, erscheint schließlich eine feste rote Fehlermeldung.</p>
|
|
7
10
|
<p>KNX-Zuordnungszeilen halten GA, DPT und Name in einer Zeile. DPT-Auswahl und Namensfeld verwenden kompakte Breiten; das Namensfeld kann sich in schmalen Editoren weiter verkleinern, ohne den gespeicherten Text zu ändern. Gespeicherte DPT-Werte bleiben erhalten, während die Auswahloptionen asynchron geladen werden.</p>
|
|
8
11
|
<p>Unterstützt werden Leuchten und Leuchtengruppen, Steckdosen, Taster, Tap dial, Bewegungs- und Kamerabewegungsressourcen, Kontakte, Lichtstärke, Temperatur, Luftfeuchtigkeit, Szenen, Batterie, Zigbee-Verbindung und Softwareupdate.</p>
|
|
9
|
-
<p>Wenn kein <strong>KNX-Gateway</strong> ausgewählt ist (einschließlich <code>none</code> und <em>Neu hinzufügen...</em>), werden die
|
|
12
|
+
<p>Wenn kein <strong>KNX-Gateway</strong> ausgewählt ist (einschließlich <code>none</code> und <em>Neu hinzufügen...</em>), werden die Registerkarten der Leuchtenzuordnung ausgeblendet; die Auswahl der Hue-Ressource und reine Flow-Optionen bleiben verfügbar. Die zugehörigen Felder GA, DPT und Name bleiben eingebunden und behalten ihre Werte, wenn das Gateway erneut ausgewählt wird; dabei wird auch das vertikale Registerkartenlayout unverändert wiederhergestellt.</p>
|
|
13
|
+
<p>Die Registerkarten der Leuchtenzuordnung enthalten nur Konfigurationsfelder; die dekorativen Bilder für Dimmen, abstimmbares Weiß und RGB wurden entfernt.</p>
|
|
10
14
|
<p>Bei einer einzelnen Leuchte richten sich die Bereiche <strong>Dimmen</strong>, <strong>Abstimmbares Weiß</strong>, <strong>RGB/HSV</strong> und native Effekte nach den Live-Fähigkeiten der ausgewählten Hue-API-v2-Ressource. Die zugehörigen KNX-Zuordnungen erscheinen automatisch, wenn die Leuchte <code>dimming</code>, <code>color_temperature</code> oder <code>color</code> bereitstellt.</p>
|
|
11
15
|
<p>Der Leuchteneditor zeigt gespeicherte Zuordnungen sofort an, ohne auf den Hue-Ressourcencache der Runtime zu warten. Aktuelle Fähigkeiten werden im Hintergrund geladen; schlägt die Anfrage fehl, bleiben Zuordnungen und Pin-Auswahl verfügbar und eine feste rote Node-RED-Fehlermeldung meldet den Fehler.</p>
|
|
12
16
|
<p>Locate und der Leuchten-Zuordnungsbereich werden vor den optionalen Effekt- und Register-Widgets initialisiert. Ein gespeichertes KNX-Gateway bleibt gültig, wenn Node-RED beim Start des Editors vorübergehend einen leeren Selektor anzeigt. Browserseitige Fehler und ein abgewiesener erster Locate-Befehl erzeugen eine feste rote Node-RED-Fehlermeldung mit dem technischen Detail, statt den Editor still zu lassen.</p>
|
|
@@ -42,6 +46,8 @@
|
|
|
42
46
|
|
|
43
47
|
<p>Dieser Node steuert HUE-Leuchten (einzeln oder gruppiert) und ordnet Befehle/Zustände KNX-Gruppenadressen zu.</p>
|
|
44
48
|
|
|
49
|
+
<p>Ohne KNX-Gateway erscheint anstelle der doppelten Überschrift „Philips HUE“ ein Hinweis zur reinen Flow-Nutzung.</p>
|
|
50
|
+
|
|
45
51
|
<p><strong>Leuchtengruppen:</strong> wenn ein KNX-Gateway konfiguriert ist, zeigt die Auswahl eines <code>grouped_light</code> immer die vollständigen Zuordnungen für Schalten, Dimmen, Tunable White, RGB/HSV, Effekte und Verhalten. Der Editor schränkt diese Felder nicht anhand der aktuell enthaltenen Leuchten ein.</p>
|
|
46
52
|
|
|
47
53
|
**Allgemein**
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Gerätefunktion",
|
|
8
|
-
"hue_section": "Philips Hue",
|
|
9
8
|
"hue_device": "Hue-Gerät",
|
|
10
9
|
"hue_device_placeholder": "Hue-Gerät oder -Ressource suchen",
|
|
10
|
+
"loading_hue_devices": "Verbindung zur Hue Bridge wird hergestellt und Geräte werden geladen…",
|
|
11
|
+
"hue_bridge_unreachable": "Die Hue Bridge ist nicht erreichbar.",
|
|
11
12
|
"device_type": "Gerätetyp",
|
|
12
13
|
"refresh_devices": "Hue-Geräte aktualisieren",
|
|
13
14
|
"locate_device": "Ausgewählte Hue-Leuchte lokalisieren",
|
|
14
15
|
"no_devices": "Keine Hue-Ressourcen gefunden. Prüfen Sie die Hue Bridge und aktualisieren Sie die Liste.",
|
|
16
|
+
"hue_bridge_not_ready": "Die Hue Bridge wurde nicht bereit. Prüfen Sie die Verbindung und öffnen Sie den Editor erneut.",
|
|
17
|
+
"knx_gateway_required_for_mappings": "Wählen Sie ein KNX-Gateway aus, um die Gruppenadress-Zuordnungen anzuzeigen. Ohne Gateway kann dieser Knoten nur über Eingangsnachrichten aus dem Flow gesteuert werden.",
|
|
15
18
|
"legacy_node_notice": "Dieser Legacy-HUE-Knoten ist veraltet. Verwenden Sie für neue Flows den neuen HUE Controller-Knoten.",
|
|
16
19
|
"migration_button": "Legacy-HUE-Knoten konvertieren",
|
|
17
20
|
"migration_button_title": "Alle Legacy-HUE-Knoten im Editor konvertieren",
|
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
<p>Select a Hue device or resource. HUE Controller detects its device type automatically and opens the matching KNX mapping, behaviour and node-pin editor.</p>
|
|
5
5
|
<p>The device picker, refresh action and detected type share the same compact row, matching the Matter Controller workflow.</p>
|
|
6
6
|
<p>Clicking or focusing the device field always opens the complete Hue resource list, even when a device is already selected. Typing continues to filter the list.</p>
|
|
7
|
+
<p>Selecting another Hue resource is only a preview until the editor is saved. Pressing <code>Cancel</code> restores the previously saved device, function and mappings.</p>
|
|
8
|
+
<p>The Hue device picker remains hidden until a Hue Bridge is selected, connected, and has loaded its resource catalog. A loading indicator is shown while waiting. Returning the bridge selector to <code>none</code> hides the indicator, picker and device editor again.</p>
|
|
9
|
+
<p>If the node already has a saved Hue device, its picker remains visible but disabled while the selected bridge is offline, and its KNX mapping tabs remain available. After 15 seconds the loading label reports that the Hue Bridge is not reachable. Resource discovery continues in the background and a fixed red error eventually appears if the bridge does not become ready.</p>
|
|
7
10
|
<p>KNX mapping rows keep GA, DPT and Name on one line. DPT selectors and Name fields use compact widths, and the Name field can contract further on narrow editor trays without changing its stored text. Saved DPT values are retained while the selector options load asynchronously.</p>
|
|
8
11
|
<p>Supported functions: lights and grouped lights, plugs, buttons, Tap dial, motion and camera motion, contacts, light level, temperature, humidity, scenes, battery, Zigbee connectivity, and device software update.</p>
|
|
9
|
-
<p>When no <strong>KNX Gateway</strong> is selected (including <code>none</code> and <em>Add new...</em>),
|
|
12
|
+
<p>When no <strong>KNX Gateway</strong> is selected (including <code>none</code> and <em>Add new...</em>), the Light mapping tabs are hidden while Hue resource selection and flow-only options remain available. Their GA, DPT and Name fields stay mounted and retain their values when the gateway is selected again, with the vertical tab layout restored unchanged.</p>
|
|
13
|
+
<p>The Light mapping tabs contain only configuration controls; the decorative Dim, Tunable White and RGB images have been removed.</p>
|
|
10
14
|
<p>For a single light, the <strong>Dim</strong>, <strong>Tunable White</strong>, <strong>RGB/HSV</strong>, and native-effects sections follow the live capabilities reported by the selected Hue API v2 resource. The corresponding KNX mappings appear automatically when the light exposes <code>dimming</code>, <code>color_temperature</code>, or <code>color</code>.</p>
|
|
11
15
|
<p>The Light editor renders saved mappings immediately without waiting for the runtime Hue resource cache. Current capabilities load in the background; if that request fails, the saved mappings and pin selector remain available and a fixed red Node-RED error reports the failure.</p>
|
|
12
16
|
<p>Locate and the Light mapping container initialize before optional Effects and tab widgets. A saved KNX gateway remains valid if Node-RED temporarily exposes an empty selector while the editor starts. Browser-side failures and a rejected initial Locate command produce a fixed red Node-RED error with the technical detail instead of leaving the editor silent.</p>
|
|
@@ -42,6 +46,8 @@
|
|
|
42
46
|
|
|
43
47
|
<p>This node controls Philips Hue lights (single or grouped) and maps their commands/states to KNX.</p>
|
|
44
48
|
|
|
49
|
+
<p>Without a KNX gateway, the editor shows a flow-only notice in place of the duplicate “Philips HUE” heading.</p>
|
|
50
|
+
|
|
45
51
|
<p><strong>Grouped lights:</strong> when a KNX gateway is configured, selecting a <code>grouped_light</code> always displays the complete Switch, Dim, Tunable White, RGB/HSV, Effects and Behaviour mappings. The editor does not restrict these fields according to the group's current child lights.</p>
|
|
46
52
|
|
|
47
53
|
**General**
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Device function",
|
|
8
|
-
"hue_section": "Philips Hue",
|
|
9
8
|
"hue_device": "Hue device",
|
|
10
9
|
"hue_device_placeholder": "Search your Hue device or resource",
|
|
10
|
+
"loading_hue_devices": "Connecting to the Hue Bridge and loading devices…",
|
|
11
|
+
"hue_bridge_unreachable": "Hue Bridge is not reachable.",
|
|
11
12
|
"device_type": "Device type",
|
|
12
13
|
"refresh_devices": "Refresh Hue devices",
|
|
13
14
|
"locate_device": "Locate selected Hue light",
|
|
14
15
|
"no_devices": "No Hue resources found. Check the Hue Bridge and press refresh.",
|
|
16
|
+
"hue_bridge_not_ready": "The Hue Bridge did not become ready. Check its connection and reopen the editor.",
|
|
17
|
+
"knx_gateway_required_for_mappings": "To display the group-address mappings, select a KNX gateway. Without one, this node can only be controlled by input messages from the flow.",
|
|
15
18
|
"legacy_node_notice": "This legacy HUE node is deprecated. Use the new HUE Controller node for new flows.",
|
|
16
19
|
"migration_button": "Convert legacy HUE nodes",
|
|
17
20
|
"migration_button_title": "Convert all legacy HUE nodes in the editor",
|
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
<p>Selecciona un dispositivo o recurso Hue. HUE Controller detecta automáticamente su tipo y abre el editor correspondiente para mapeos KNX, comportamiento y puertos del nodo.</p>
|
|
5
5
|
<p>El selector de dispositivo, la actualización y el tipo detectado comparten la misma fila compacta, como en Matter Controller.</p>
|
|
6
6
|
<p>Al hacer clic o enfocar el campo del dispositivo siempre se abre la lista completa de recursos Hue, aunque ya haya un dispositivo seleccionado. Al escribir, la lista sigue filtrándose.</p>
|
|
7
|
+
<p>Seleccionar otro recurso Hue es solo una vista previa hasta que se guarda el editor. Al pulsar <code>Cancelar</code> se restauran el dispositivo, la función y los mapeos guardados anteriormente.</p>
|
|
8
|
+
<p>El selector de dispositivos Hue permanece oculto hasta que se selecciona y conecta un Hue Bridge y se carga su catálogo de recursos. Durante la espera se muestra un indicador de carga. Al volver a poner el bridge en <code>none</code>, el indicador, el selector y el editor del dispositivo se ocultan de nuevo.</p>
|
|
9
|
+
<p>Si el nodo ya tiene un dispositivo Hue guardado, su selector permanece visible pero deshabilitado mientras el bridge seleccionado está desconectado, y las pestañas de mapeo KNX siguen disponibles. Tras 15 segundos, la etiqueta de espera indica que no se puede acceder a Hue Bridge. La búsqueda de recursos continúa en segundo plano y finalmente aparece un error rojo fijo si el bridge no llega a estar disponible.</p>
|
|
7
10
|
<p>Las filas de mapeo KNX mantienen GA, DPT y Nombre en una sola línea. Los campos DPT y Nombre usan anchos compactos; Nombre puede reducirse aún más en editores estrechos sin modificar el texto guardado. Los valores DPT guardados se conservan mientras las opciones del selector se cargan de forma asíncrona.</p>
|
|
8
11
|
<p>Admite luces y grupos de luces, enchufes, botones, Tap dial, movimiento y movimiento de cámara, contactos, nivel de luz, temperatura, humedad, escenas, batería, conectividad Zigbee y actualización de software.</p>
|
|
9
|
-
<p>Cuando no hay ningún <strong>Gateway KNX</strong> seleccionado (incluidos <code>none</code> y <em>Añadir nuevo...</em>), se ocultan
|
|
12
|
+
<p>Cuando no hay ningún <strong>Gateway KNX</strong> seleccionado (incluidos <code>none</code> y <em>Añadir nuevo...</em>), se ocultan las pestañas de mapeo de Luz; la selección del recurso Hue y las opciones exclusivas del flujo siguen disponibles. Sus campos GA, DPT y Nombre permanecen montados y conservan sus valores cuando se vuelve a seleccionar el gateway, restaurando también sin cambios el diseño vertical de las pestañas.</p>
|
|
13
|
+
<p>Las pestañas de mapeo de Luz contienen únicamente los controles de configuración; se han eliminado las imágenes decorativas de Regulación, Blanco regulable y RGB.</p>
|
|
10
14
|
<p>Para una luz individual, las secciones <strong>Regulación</strong>, <strong>Blanco regulable</strong>, <strong>RGB/HSV</strong> y efectos nativos siguen las capacidades en vivo declaradas por el recurso Hue API v2 seleccionado. Los mapeos KNX correspondientes aparecen automáticamente cuando la luz expone <code>dimming</code>, <code>color_temperature</code> o <code>color</code>.</p>
|
|
11
15
|
<p>El editor de Luz muestra inmediatamente los mapeos guardados sin esperar a la caché de recursos Hue del runtime. Las capacidades actuales se cargan en segundo plano; si la solicitud falla, los mapeos y el selector de pines siguen disponibles y un error rojo fijo de Node-RED informa del fallo.</p>
|
|
12
16
|
<p>Locate y el contenedor de mapeo de Luz se inicializan antes que los widgets opcionales de Efectos y pestañas. Un gateway KNX guardado sigue siendo válido si Node-RED muestra temporalmente un selector vacío al iniciar el editor. Los fallos del navegador y un primer comando Locate rechazado generan un error rojo fijo de Node-RED con el detalle técnico, en lugar de dejar el editor en silencio.</p>
|
|
@@ -42,6 +46,8 @@
|
|
|
42
46
|
|
|
43
47
|
<p> Este nodo controla las luces de Hue Philips (single o agrupada) y mapea sus comandos/estados a KNX. </p>
|
|
44
48
|
|
|
49
|
+
<p>Sin una pasarela KNX, aparece un aviso de uso exclusivo mediante el flujo en lugar del encabezado duplicado «Philips HUE».</p>
|
|
50
|
+
|
|
45
51
|
<p><strong>Grupos de luces:</strong> cuando hay una pasarela KNX configurada, al seleccionar un <code>grouped_light</code> siempre se muestran todas las asignaciones de Interruptor, Regulación, Blanco ajustable, RGB/HSV, Efectos y Comportamiento. El editor no limita estos campos según las luces incluidas actualmente en el grupo.</p>
|
|
46
52
|
|
|
47
53
|
**General**
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Función del dispositivo",
|
|
8
|
-
"hue_section": "Philips Hue",
|
|
9
8
|
"hue_device": "Dispositivo Hue",
|
|
10
9
|
"hue_device_placeholder": "Buscar dispositivo o recurso Hue",
|
|
10
|
+
"loading_hue_devices": "Conectando con Hue Bridge y cargando los dispositivos…",
|
|
11
|
+
"hue_bridge_unreachable": "Hue Bridge no está accesible.",
|
|
11
12
|
"device_type": "Tipo de dispositivo",
|
|
12
13
|
"refresh_devices": "Actualizar dispositivos Hue",
|
|
13
14
|
"locate_device": "Localizar la luz Hue seleccionada",
|
|
14
15
|
"no_devices": "No se encontraron recursos Hue. Comprueba Hue Bridge y actualiza la lista.",
|
|
16
|
+
"hue_bridge_not_ready": "Hue Bridge no llegó a estar disponible. Comprueba la conexión y vuelve a abrir el editor.",
|
|
17
|
+
"knx_gateway_required_for_mappings": "Selecciona una pasarela KNX para mostrar las asociaciones de direcciones de grupo. Sin pasarela, este nodo solo puede controlarse mediante mensajes de entrada del flujo.",
|
|
15
18
|
"legacy_node_notice": "Este nodo HUE legacy está obsoleto. Usa el nuevo nodo HUE Controller para los flujos nuevos.",
|
|
16
19
|
"migration_button": "Convertir nodos HUE legacy",
|
|
17
20
|
"migration_button_title": "Convertir todos los nodos HUE legacy del editor",
|