node-red-contrib-knx-ultimate 6.0.6 → 6.0.8
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 +6 -1
- package/nodes/knxUltimateMatterControllerDevice.html +183 -46
- package/nodes/knxUltimateMatterControllerDevice.js +11 -6
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/de/knxUltimateMatterControllerDevice.json +25 -15
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/en/knxUltimateMatterControllerDevice.json +26 -16
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/es/knxUltimateMatterControllerDevice.json +19 -9
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +16 -6
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/it/knxUltimateMatterControllerDevice.json +25 -15
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +3 -1
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +21 -11
- package/nodes/utils/matterControllerProfiles/doorLock.js +214 -0
- package/nodes/utils/matterControllerProfiles/index.js +21 -0
- package/nodes/utils/matterControllerProfiles/mappedEndpoint.js +150 -0
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
-
**Version 6.0.
|
|
9
|
+
**Version 6.0.8** - July 2026<br/>
|
|
10
10
|
|
|
11
|
+
- **Control Matter from KNX — profile deploy crash**: Door Lock and multi-purpose profiles now expose the KNX status callback before registering with `knxUltimate-config`, preventing `_Node.setNodeStatus is not a function` during deploy.<br/>
|
|
12
|
+
- **Control Matter from KNX — flow PIN persistence**: the **Node Input/Output PINs** selection is no longer overwritten from the KNX gateway presence when reopening the editor. The saved choice remains authoritative and `inputs`/`outputs` are updated coherently on save.<br/>
|
|
13
|
+
- **Matter terminology cleanup**: removed leftover Philips Hue wording from Matter controller labels, notifications, runtime status, localized strings and help. Backward-compatible saved configuration keys remain unchanged.<br/>
|
|
14
|
+
- **Control Matter from KNX (BETA) — Door Lock support**: commissioned Door Lock endpoints (including locks bridged by a vendor Matter hub) are now detected from their real `0x0101` cluster and expose KNX DPT 1 command/status mappings. `true` invokes `lockDoor`, `false` invokes `unlockDoor`, and subscribed `lockState` feedback updates KNX without command reflection. `NotFullyLocked` and `Unlatched` remain explicit flow states and are never collapsed into an unsafe binary KNX value. The optional remote-operation PIN is stored as a Node-RED credential; endpoints that do not advertise a requested command are rejected instead of receiving an invented operation. This is the first controller-side device profile in the extensible profile architecture requested in [discussion #519](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/discussions/519).<br/>
|
|
15
|
+
- **Control Matter from KNX (BETA) — multi-purpose endpoint profiles**: the controller node now keeps its established light path unchanged while routing non-light endpoints through a separate capability-driven mapped profile. Selecting a plug/On-Off actuator, cover, thermostat, fan, environmental/contact/occupancy sensor, battery, electrical-power or energy endpoint builds only the KNX mappings backed by clusters, attributes and supported commands actually reported by that endpoint. These mappings now live inside a dedicated **Mappings** tab beside **Behaviour**, matching the established light editor layout. Each mapping can be disabled by leaving its GA empty; cached status supports KNX read responses and startup publication, attribute reports never reflect commands back to Matter, and cluster events remain available on the optional flow output. Saved mappings survive an untouched editor save even when the Matter endpoint is temporarily offline.<br/>
|
|
11
16
|
- **Matter nodes — cleaner editors**: long pairing, storage, cache and endpoint-structure explanations were removed from the Matter node forms and consolidated in the localized HTML help, keeping the editors focused on fields, actions and live operational status.<br/>
|
|
12
17
|
- **Matter nodes — distinct canvas icons**: Matter Controller and Matter Bridge nodes now use separate compact icons, with `mat` above a large right/left direction arrow, making the two integration directions immediately distinguishable in the palette, workspace and configuration selectors.<br/>
|
|
13
18
|
- **Matter Controller/Bridge — protocol version and portable storage backups**: the Node-RED startup version line now logs the implemented Matter protocol revision (Matter 1.5.1, separately from the matter.js package version). Both Matter configuration editors can export and import a per-instance JSON backup containing the complete persistent Matter storage required to preserve fabrics, private credentials, sessions and commissioned/paired nodes. Import atomically replaces only the selected instance and restarts its engine; backup files contain secrets and must be protected like passwords.<br/>
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
matterEndpointId: { value: 1 },
|
|
19
19
|
matterDeviceName: { value: "" },
|
|
20
20
|
matterDeviceCapabilities: { value: "" },
|
|
21
|
+
matterMappings: { value: "[]" },
|
|
21
22
|
name: { value: "" },
|
|
22
23
|
|
|
23
24
|
nameLightSwitch: { value: "" },
|
|
@@ -123,6 +124,9 @@
|
|
|
123
124
|
restoreDayMode: { value: "no" }, // Starting from v 4.1.31
|
|
124
125
|
updateLocalStateFromKNXWrite: { value: true } // Starting from v 4.1.31
|
|
125
126
|
},
|
|
127
|
+
credentials: {
|
|
128
|
+
doorLockPin: { type: "password" }
|
|
129
|
+
},
|
|
126
130
|
inputs: 0,
|
|
127
131
|
outputs: 0,
|
|
128
132
|
icon: "node-matter-controller-icon.svg",
|
|
@@ -215,7 +219,7 @@
|
|
|
215
219
|
ensureVerticalTabsStyle();
|
|
216
220
|
const $knxServerInput = $("#node-input-server");
|
|
217
221
|
const KNX_EMPTY_VALUES = new Set(['', 'none', '_ADD_', '__NONE__']);
|
|
218
|
-
//
|
|
222
|
+
// Historical variable names are retained for saved-flow compatibility; they point to Matter widgets.
|
|
219
223
|
const $hueServerInput = $("#node-input-serverMatter");
|
|
220
224
|
const $hueDeviceInput = $("#node-input-matterNodeId");
|
|
221
225
|
const $deviceNameInput = $("#node-input-matterDeviceName");
|
|
@@ -283,8 +287,8 @@
|
|
|
283
287
|
$icon.removeClass('fa-stop fa-play').addClass(locateSessionActive ? 'fa fa-stop' : 'fa fa-play');
|
|
284
288
|
}
|
|
285
289
|
const title = locateSessionActive
|
|
286
|
-
? (node._('knxUltimateMatterControllerDevice.locate_stop_title') || 'Stop
|
|
287
|
-
: (node._('knxUltimateMatterControllerDevice.locate_start_title') || 'Locate selected
|
|
290
|
+
? (node._('knxUltimateMatterControllerDevice.locate_stop_title') || 'Stop Matter locate')
|
|
291
|
+
: (node._('knxUltimateMatterControllerDevice.locate_start_title') || 'Locate selected Matter device');
|
|
288
292
|
$locateDeviceButton.attr('title', title);
|
|
289
293
|
};
|
|
290
294
|
|
|
@@ -340,8 +344,8 @@
|
|
|
340
344
|
if (!context) {
|
|
341
345
|
if (!silent) {
|
|
342
346
|
const message = !resolveHueServerValue({ allowStored: true })
|
|
343
|
-
? (node._('knxUltimateMatterControllerDevice.locate_no_bridge') || 'Select a
|
|
344
|
-
: (node._('knxUltimateMatterControllerDevice.locate_no_device') || 'Select a
|
|
347
|
+
? (node._('knxUltimateMatterControllerDevice.locate_no_bridge') || 'Select a Matter controller first')
|
|
348
|
+
: (node._('knxUltimateMatterControllerDevice.locate_no_device') || 'Select a Matter device first');
|
|
345
349
|
RED.notify(message, 'warning');
|
|
346
350
|
}
|
|
347
351
|
updateLocateButtonState(false);
|
|
@@ -410,7 +414,7 @@
|
|
|
410
414
|
updateLocateButtonState(false);
|
|
411
415
|
clearLocateAutoReset();
|
|
412
416
|
if (!silent) {
|
|
413
|
-
RED.notify(message || (node._('knxUltimateMatterControllerDevice.locate_error') || 'Unable to locate
|
|
417
|
+
RED.notify(message || (node._('knxUltimateMatterControllerDevice.locate_error') || 'Unable to locate Matter device'), 'error');
|
|
414
418
|
}
|
|
415
419
|
}).always(() => {
|
|
416
420
|
locatePendingRequest = null;
|
|
@@ -506,11 +510,11 @@
|
|
|
506
510
|
}
|
|
507
511
|
return val;
|
|
508
512
|
};
|
|
509
|
-
const matterTypePretty = { 'ma-extendedcolorlight': 'Colour light', 'ma-colortemperaturelight': 'Tunable white', 'ma-dimmablelight': 'Dimmable light', 'ma-onofflight': 'On/off light', 'ma-onoffpluginunit': 'Plug', 'ma-dimmablepluginunit': 'Dimmable plug' };
|
|
513
|
+
const matterTypePretty = { 'ma-extendedcolorlight': 'Colour light', 'ma-colortemperaturelight': 'Tunable white', 'ma-dimmablelight': 'Dimmable light', 'ma-onofflight': 'On/off light', 'ma-onoffpluginunit': 'Plug', 'ma-dimmablepluginunit': 'Dimmable plug', 'ma-doorlock': 'Door lock', 'ma-windowcovering': 'Cover', 'ma-thermostat': 'Thermostat', 'ma-fan': 'Fan', 'ma-temperaturesensor': 'Temperature sensor', 'ma-humiditysensor': 'Humidity sensor', 'ma-lightsensor': 'Light sensor', 'ma-occupancysensor': 'Occupancy sensor', 'ma-contactsensor': 'Contact sensor' };
|
|
510
514
|
const matterPrettyType = (raw) => {
|
|
511
515
|
const key = String(raw || '').toLowerCase();
|
|
512
516
|
if (matterTypePretty[key]) return matterTypePretty[key];
|
|
513
|
-
return String(raw || '').replace(/^ma-/i, '').replace(/light$/i, ' light') || '
|
|
517
|
+
return String(raw || '').replace(/^ma-/i, '').replace(/light$/i, ' light') || 'Matter endpoint';
|
|
514
518
|
};
|
|
515
519
|
const normalizeMatterToken = (value) => String(value || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
516
520
|
const getMatterCluster = (ep, clusterId) => (ep.clusters || []).find((c) => Number(c.id) === Number(clusterId));
|
|
@@ -524,10 +528,64 @@
|
|
|
524
528
|
};
|
|
525
529
|
const matterCapabilitiesFromEndpoint = (ep) => {
|
|
526
530
|
const deviceTypes = (ep.deviceTypes || []).map(normalizeMatterToken);
|
|
531
|
+
const primaryDeviceType = (ep.deviceTypes || []).find((value) => !/bridgednode|aggregator|controlbridge/i.test(String(value))) || ep.name;
|
|
532
|
+
const deviceTypeDisplay = matterPrettyType(primaryDeviceType);
|
|
527
533
|
const typeText = deviceTypes.join(' ');
|
|
528
534
|
const onOffCluster = getMatterCluster(ep, 6);
|
|
529
535
|
const levelCluster = getMatterCluster(ep, 8);
|
|
530
536
|
const colorCluster = getMatterCluster(ep, 768);
|
|
537
|
+
const doorLockCluster = getMatterCluster(ep, 257);
|
|
538
|
+
if (doorLockCluster) {
|
|
539
|
+
return {
|
|
540
|
+
known: true,
|
|
541
|
+
profile: 'doorLock',
|
|
542
|
+
deviceTypeDisplay,
|
|
543
|
+
onOff: true,
|
|
544
|
+
level: false,
|
|
545
|
+
colorTemperature: false,
|
|
546
|
+
color: false,
|
|
547
|
+
lockDoor: hasMatterCommand(doorLockCluster, ['lockDoor']),
|
|
548
|
+
unlockDoor: hasMatterCommand(doorLockCluster, ['unlockDoor']),
|
|
549
|
+
requirePinForRemoteOperation: (() => {
|
|
550
|
+
const attribute = (doorLockCluster.attributes || []).find((item) => normalizeMatterToken(item.name) === 'requirepinforremoteoperation');
|
|
551
|
+
return attribute ? attribute.value === true : false;
|
|
552
|
+
})()
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const isMatterLight = deviceTypes.some((type) => /(?:onoff|dimmable|colortemperature|extendedcolor)light/.test(type));
|
|
556
|
+
if (!isMatterLight) {
|
|
557
|
+
const mappedTargets = [];
|
|
558
|
+
const addCommand = (clusterId, target, label, dpt) => {
|
|
559
|
+
const cluster = getMatterCluster(ep, clusterId);
|
|
560
|
+
if (hasMatterCommand(cluster, [target])) mappedTargets.push({ direction: 'command', endpointId: Number(ep.endpointId), clusterId, targetKind: 'command', target, label, dpt });
|
|
561
|
+
};
|
|
562
|
+
const addAttribute = (clusterId, target, label, dpt, writable) => {
|
|
563
|
+
const cluster = getMatterCluster(ep, clusterId);
|
|
564
|
+
if (hasMatterAttribute(cluster, [target])) mappedTargets.push({ direction: writable ? 'command' : 'status', endpointId: Number(ep.endpointId), clusterId, targetKind: 'attribute', target, label, dpt });
|
|
565
|
+
};
|
|
566
|
+
addCommand(6, 'on', 'onoff_command', '1.001');
|
|
567
|
+
addAttribute(6, 'onOff', 'onoff_state', '1.001', false);
|
|
568
|
+
addCommand(258, 'upOrOpen', 'cover_updown', '1.008');
|
|
569
|
+
addCommand(258, 'stopMotion', 'cover_stop', '1.017');
|
|
570
|
+
addCommand(258, 'goToLiftPercentage', 'cover_position_command', '5.001');
|
|
571
|
+
addAttribute(258, 'currentPositionLiftPercent100ths', 'cover_position_state', '5.001', false);
|
|
572
|
+
addAttribute(513, 'occupiedHeatingSetpoint', 'heating_command', '9.001', true);
|
|
573
|
+
addAttribute(513, 'occupiedHeatingSetpoint', 'heating_state', '9.001', false);
|
|
574
|
+
addAttribute(513, 'occupiedCoolingSetpoint', 'cooling_command', '9.001', true);
|
|
575
|
+
addAttribute(513, 'occupiedCoolingSetpoint', 'cooling_state', '9.001', false);
|
|
576
|
+
addAttribute(513, 'localTemperature', 'local_temperature', '9.001', false);
|
|
577
|
+
addAttribute(514, 'percentSetting', 'fan_command', '5.001', true);
|
|
578
|
+
addAttribute(514, 'percentCurrent', 'fan_state', '5.001', false);
|
|
579
|
+
addAttribute(1026, 'measuredValue', 'temperature', '9.001', false);
|
|
580
|
+
addAttribute(1029, 'measuredValue', 'humidity', '9.007', false);
|
|
581
|
+
addAttribute(1024, 'measuredValue', 'illuminance', '9.004', false);
|
|
582
|
+
addAttribute(1030, 'occupancy', 'occupancy', '1.011', false);
|
|
583
|
+
addAttribute(69, 'stateValue', 'contact', '1.002', false);
|
|
584
|
+
addAttribute(47, 'batPercentRemaining', 'battery', '5.001', false);
|
|
585
|
+
addAttribute(144, 'activePower', 'active_power', '14.056', false);
|
|
586
|
+
addAttribute(145, 'cumulativeEnergyImported', 'imported_energy', '13.013', false);
|
|
587
|
+
return { known: true, profile: 'mapped', deviceTypeDisplay, onOff: false, level: false, colorTemperature: false, color: false, mappedTargets };
|
|
588
|
+
}
|
|
531
589
|
const hasColorTemperatureType = /colortemperature|extendedcolor/.test(typeText);
|
|
532
590
|
const hasColorType = /extendedcolor/.test(typeText);
|
|
533
591
|
const hasLevelType = /dimmable|colortemperature|extendedcolor/.test(typeText);
|
|
@@ -542,6 +600,7 @@
|
|
|
542
600
|
);
|
|
543
601
|
return {
|
|
544
602
|
known: true,
|
|
603
|
+
deviceTypeDisplay,
|
|
545
604
|
colorDetectionVersion: 2,
|
|
546
605
|
onOff: !!onOffCluster || /onoff|dimmable|colortemperature|extendedcolor/.test(typeText),
|
|
547
606
|
level: !!levelCluster || hasLevelType,
|
|
@@ -568,7 +627,7 @@
|
|
|
568
627
|
const serializeMatterCapabilities = (capabilities) => JSON.stringify(capabilities || {});
|
|
569
628
|
const getJSONPromise = (url) => new Promise((resolve, reject) => { $.getJSON(url, resolve).fail(reject); });
|
|
570
629
|
|
|
571
|
-
//
|
|
630
|
+
// Filters Matter items shaped as { value, matterNodeId, matterEndpointId }.
|
|
572
631
|
const filterHueDevices = (devices, term) => {
|
|
573
632
|
const cleaned = (term || '').replace(/exactmatch/gi, '').trim();
|
|
574
633
|
return $.map(devices, (item) => {
|
|
@@ -578,9 +637,8 @@
|
|
|
578
637
|
});
|
|
579
638
|
};
|
|
580
639
|
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
// so a bridge (e.g. a Hue Bridge paired via Matter) lists each lamp.
|
|
640
|
+
// Expand every commissioned node into its functional endpoints. Lights keep
|
|
641
|
+
// the legacy light UI; other endpoints select a capability-driven profile.
|
|
584
642
|
const fetchHueDevices = (term, respond, { forceRefresh = false } = {}) => {
|
|
585
643
|
const matterServer = getHueServerConfig();
|
|
586
644
|
if (!matterServer) {
|
|
@@ -609,8 +667,10 @@
|
|
|
609
667
|
const items = [];
|
|
610
668
|
results.forEach((entry) => {
|
|
611
669
|
const device = entry.device;
|
|
612
|
-
|
|
613
|
-
|
|
670
|
+
// Multi-purpose controller: list every functional endpoint. Capability
|
|
671
|
+
// detection below decides whether it uses the unchanged light path or
|
|
672
|
+
// a dedicated/mapped non-light profile.
|
|
673
|
+
const eps = entry.endpoints.filter((ep) => Number(ep.endpointId) !== 0);
|
|
614
674
|
const many = eps.length > 1;
|
|
615
675
|
eps.forEach((ep) => {
|
|
616
676
|
const label = matterEndpointLabel(ep);
|
|
@@ -697,11 +757,11 @@
|
|
|
697
757
|
$.getJSON(`knxUltimateDpts?serverId=${serverId}&_=${Date.now()}`, (data) => {
|
|
698
758
|
data.forEach((dpt) => {
|
|
699
759
|
if (prefixes.some((prefix) => prefix === "" || dpt.value.startsWith(prefix))) {
|
|
700
|
-
// Adjustment for
|
|
760
|
+
// Adjustment for Matter color temperature
|
|
701
761
|
if (dpt.value.startsWith("7.600")) {
|
|
702
762
|
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - KNX Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute)"));
|
|
703
763
|
} else if (dpt.value.startsWith("9.002")) {
|
|
704
|
-
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " -
|
|
764
|
+
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - Matter Kelvin range 2000-6535 K (Home Assistant color_temperature_mode: absolute_float)"));
|
|
705
765
|
} else if (dpt.value.startsWith("5.001")) {
|
|
706
766
|
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - Homeassistant color_temperature_mode: relative"));
|
|
707
767
|
} else {
|
|
@@ -934,12 +994,29 @@
|
|
|
934
994
|
const supportsTemperature = hasHueDevice && caps.colorTemperature === true;
|
|
935
995
|
const supportsColor = hasHueDevice && caps.color === true;
|
|
936
996
|
const hasRichBehaviour = supportsDim && (supportsTemperature || supportsColor);
|
|
997
|
+
const isDoorLock = caps.profile === 'doorLock';
|
|
998
|
+
const isMapped = caps.profile === 'mapped';
|
|
999
|
+
const deviceTypeDisplay = String(caps.deviceTypeDisplay || '').trim();
|
|
937
1000
|
|
|
938
1001
|
setMatterTabVisible('tabs-1', supportsSwitch);
|
|
939
1002
|
setMatterTabVisible('tabs-2', supportsDim);
|
|
940
1003
|
setMatterTabVisible('tabs-3', supportsTemperature);
|
|
941
1004
|
setMatterTabVisible('tabs-4', supportsColor);
|
|
1005
|
+
setMatterTabVisible('tabs-mapped', isMapped);
|
|
942
1006
|
setMatterTabVisible('tabs-6', hasHueDevice);
|
|
1007
|
+
$('#matter-door-lock-options').toggle(isDoorLock);
|
|
1008
|
+
$('#matter-switch-command-label').text(isDoorLock
|
|
1009
|
+
? (node._('knxUltimateMatterControllerDevice.door_lock_command') || 'Lock / unlock')
|
|
1010
|
+
: (node._('knxUltimateMatterControllerDevice.control') || 'Control'));
|
|
1011
|
+
$('#matter-switch-status-label').text(isDoorLock
|
|
1012
|
+
? (node._('knxUltimateMatterControllerDevice.door_lock_status') || 'Lock state')
|
|
1013
|
+
: (node._('knxUltimateMatterControllerDevice.status') || 'Status'));
|
|
1014
|
+
$('#matter-door-lock-command-warning').toggle(isDoorLock && (caps.lockDoor === false || caps.unlockDoor === false));
|
|
1015
|
+
$('#matter-door-lock-pin-required').toggle(isDoorLock && caps.requirePinForRemoteOperation === true);
|
|
1016
|
+
$('#matter-mapped-profile').toggle(isMapped);
|
|
1017
|
+
if (isMapped) renderMappedProfile(caps.mappedTargets || []);
|
|
1018
|
+
$('#matter-selected-device-type-value').text(deviceTypeDisplay);
|
|
1019
|
+
$('#matter-selected-device-type').toggle(hasHueDevice && deviceTypeDisplay !== '');
|
|
943
1020
|
try { $tabs.tabs('refresh'); } catch (error) { /* empty */ }
|
|
944
1021
|
ensureVisibleMatterTabSelected();
|
|
945
1022
|
|
|
@@ -960,6 +1037,40 @@
|
|
|
960
1037
|
refreshBehaviourVisibility();
|
|
961
1038
|
};
|
|
962
1039
|
|
|
1040
|
+
const parseMatterMappings = () => {
|
|
1041
|
+
try { return JSON.parse($('#node-input-matterMappings').val() || node.matterMappings || '[]'); } catch (error) { return []; }
|
|
1042
|
+
};
|
|
1043
|
+
const renderMappedProfile = (targets) => {
|
|
1044
|
+
const saved = parseMatterMappings();
|
|
1045
|
+
const $body = $('#matter-mapped-profile-body').empty();
|
|
1046
|
+
targets.forEach((target) => {
|
|
1047
|
+
const key = `${target.direction}:${target.endpointId}:${target.clusterId}:${target.targetKind}:${target.target}`;
|
|
1048
|
+
const previous = saved.find((item) => `${item.direction}:${item.endpointId}:${item.clusterId}:${item.targetKind}:${item.target}` === key) || {};
|
|
1049
|
+
const $row = $('<div class="form-row matter-mapped-row" style="display:flex;align-items:center;"></div>').data('mapping', target);
|
|
1050
|
+
const translated = node._(`knxUltimateMatterControllerDevice.mapped.${target.label}`);
|
|
1051
|
+
$('<label style="width:220px; margin-right:10px;"></label>').text(translated || target.target).appendTo($row);
|
|
1052
|
+
$('<label style="width:25px; margin:0 5px 0 0;">GA</label>').appendTo($row);
|
|
1053
|
+
const $ga = $('<input class="matter-mapped-ga" type="text" placeholder="Ex: 1/1/1" style="width:100px; margin-right:18px;">').val(previous.ga || '').appendTo($row);
|
|
1054
|
+
$('<label style="width:35px; margin:0 5px 0 0;">DPT</label>').appendTo($row);
|
|
1055
|
+
const $dpt = $('<input class="matter-mapped-dpt" type="text" placeholder="DPT" style="width:140px;">').val(previous.dpt || target.dpt || '').appendTo($row);
|
|
1056
|
+
$ga.autocomplete({
|
|
1057
|
+
minLength: 0,
|
|
1058
|
+
source(request, response) {
|
|
1059
|
+
if (!hasKnxServerSelected()) { response([]); return; }
|
|
1060
|
+
$.getJSON(`knxUltimatecsv?nodeID=${resolveKnxServerValue()}&_=${Date.now()}`, (data) => {
|
|
1061
|
+
response($.map(data, (value) => {
|
|
1062
|
+
const searchable = `${value.ga} ${value.devicename} ${value.dpt}`;
|
|
1063
|
+
if (!htmlUtilsfullCSVSearch(searchable, request.term || '')) return null;
|
|
1064
|
+
return { label: `${value.ga} # ${value.devicename} # ${value.dpt}`, value: value.ga, dpt: value.dpt };
|
|
1065
|
+
}));
|
|
1066
|
+
}).fail(() => response([]));
|
|
1067
|
+
},
|
|
1068
|
+
select(event, ui) { if (ui.item.dpt) $dpt.val(ui.item.dpt); }
|
|
1069
|
+
}).focus(function () { $(this).autocomplete('search', $(this).val()); });
|
|
1070
|
+
$body.append($row);
|
|
1071
|
+
});
|
|
1072
|
+
};
|
|
1073
|
+
|
|
963
1074
|
const updateTabsVisibility = () => {
|
|
964
1075
|
const knxSelected = hasKnxServerSelected();
|
|
965
1076
|
const hueDeviceSelected = getHueDeviceValue() !== '';
|
|
@@ -973,13 +1084,6 @@
|
|
|
973
1084
|
}
|
|
974
1085
|
applyMatterCapabilities(currentMatterCapabilities);
|
|
975
1086
|
|
|
976
|
-
if ($pinSelect.length) {
|
|
977
|
-
const desiredPins = knxSelected ? 'no' : 'yes';
|
|
978
|
-
if ($pinSelect.val() !== desiredPins) {
|
|
979
|
-
$pinSelect.val(desiredPins).trigger('change');
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
1087
|
if ($pinSectionRow.length) {
|
|
984
1088
|
$pinSectionRow.show();
|
|
985
1089
|
}
|
|
@@ -1125,7 +1229,7 @@
|
|
|
1125
1229
|
.append($("<option>").val("temperature").text(node._("knxUltimateMatterControllerDevice.select_temperature_brightness")));
|
|
1126
1230
|
$("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness);
|
|
1127
1231
|
$("#node-input-enableDayNightLighting").val(node.enableDayNightLighting);
|
|
1128
|
-
// "Get current" reads
|
|
1232
|
+
// Live "Get current" reads are not available on this Matter path.
|
|
1129
1233
|
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
1130
1234
|
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
1131
1235
|
applyMatterCapabilities(currentMatterCapabilities);
|
|
@@ -1150,8 +1254,8 @@
|
|
|
1150
1254
|
if ($("#node-input-enableDayNightLighting").val() === "yes") blinkBackground("#colorPickerNight");
|
|
1151
1255
|
});
|
|
1152
1256
|
|
|
1153
|
-
// "Get current"
|
|
1154
|
-
//
|
|
1257
|
+
// Live "Get current" reads do not exist on this Matter path; the buttons are
|
|
1258
|
+
// hidden by populateSwitchOnCombos above.
|
|
1155
1259
|
|
|
1156
1260
|
// Fill options for minDimLevel and maxDimLevel and comboBrightnessAtSwitchOn (for color brightness at switch on, with temperature toghedher)
|
|
1157
1261
|
for (let index = 100; index >= 0; index -= 5) {
|
|
@@ -1321,6 +1425,20 @@
|
|
|
1321
1425
|
this.matterNodeId = ($("#node-input-matterNodeId").val() || '').trim();
|
|
1322
1426
|
this.matterEndpointId = Number($("#node-input-matterEndpointId").val() || 1);
|
|
1323
1427
|
this.matterDeviceCapabilities = ($("#node-input-matterDeviceCapabilities").val() || '').trim();
|
|
1428
|
+
const mappedMappings = [];
|
|
1429
|
+
const $mappedRows = $('#matter-mapped-profile-body .matter-mapped-row');
|
|
1430
|
+
$mappedRows.each(function () {
|
|
1431
|
+
const base = $(this).data('mapping') || {};
|
|
1432
|
+
const ga = ($(this).find('.matter-mapped-ga').val() || '').trim();
|
|
1433
|
+
const dpt = ($(this).find('.matter-mapped-dpt').val() || '').trim();
|
|
1434
|
+
if (ga && dpt) mappedMappings.push(Object.assign({}, base, { ga, dpt }));
|
|
1435
|
+
});
|
|
1436
|
+
// Preserve saved mappings when the endpoint is offline and its asynchronous
|
|
1437
|
+
// structure could not be rebuilt during an untouched editor round trip.
|
|
1438
|
+
this.matterMappings = $mappedRows.length > 0
|
|
1439
|
+
? JSON.stringify(mappedMappings)
|
|
1440
|
+
: ($('#node-input-matterMappings').val() || this.matterMappings || '[]');
|
|
1441
|
+
$('#node-input-matterMappings').val(this.matterMappings);
|
|
1324
1442
|
this.name = this.matterDeviceName || this.name || '';
|
|
1325
1443
|
$("#node-input-name").val(this.name);
|
|
1326
1444
|
const parseSavedMatterCapabilities = (raw) => {
|
|
@@ -1546,9 +1664,15 @@
|
|
|
1546
1664
|
<span class="hue-devices-loading" style="margin-left:6px; display:none; color:#1b7d33;">
|
|
1547
1665
|
<i class="fa fa-circle-notch fa-spin"></i>
|
|
1548
1666
|
</span>
|
|
1667
|
+
<span id="matter-selected-device-type" style="display:none; margin-left:10px; color:#666; white-space:nowrap;">
|
|
1668
|
+
<i class="fa fa-microchip"></i>
|
|
1669
|
+
<span data-i18n="knxUltimateMatterControllerDevice.device_type"></span>:
|
|
1670
|
+
<span id="matter-selected-device-type-value" style="font-weight:600;"></span>
|
|
1671
|
+
</span>
|
|
1549
1672
|
<input type="hidden" id="node-input-matterNodeId" />
|
|
1550
1673
|
<input type="hidden" id="node-input-matterEndpointId" />
|
|
1551
1674
|
<input type="hidden" id="node-input-matterDeviceCapabilities" />
|
|
1675
|
+
<input type="hidden" id="node-input-matterMappings" />
|
|
1552
1676
|
<input type="hidden" id="node-input-name" />
|
|
1553
1677
|
</div>
|
|
1554
1678
|
|
|
@@ -1560,12 +1684,18 @@
|
|
|
1560
1684
|
<li><a href="#tabs-2"><i class="fa-solid fa-arrow-up-wide-short"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.dim"></span></a></li>
|
|
1561
1685
|
<li><a href="#tabs-3"><i class="fa-solid fa-temperature-quarter"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.tunable_white"></span></a></li>
|
|
1562
1686
|
<li><a href="#tabs-4"><i class="fa-solid fa-palette"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.rgb_hsv"></span></a></li>
|
|
1687
|
+
<li><a href="#tabs-mapped"><i class="fa-solid fa-list-check"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.mappings"></span></a></li>
|
|
1563
1688
|
<li><a href="#tabs-6"><i class="fa-solid fa-code-merge"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.behaviour"></span></a></li>
|
|
1564
1689
|
</ul>
|
|
1690
|
+
<div id="tabs-mapped">
|
|
1691
|
+
<div id="matter-mapped-profile" style="display:none;">
|
|
1692
|
+
<div id="matter-mapped-profile-body"></div>
|
|
1693
|
+
</div>
|
|
1694
|
+
</div>
|
|
1565
1695
|
<div id="tabs-1">
|
|
1566
1696
|
<p>
|
|
1567
1697
|
<div class="form-row">
|
|
1568
|
-
<label for="node-input-nameLightSwitch" style="width:110px;"><i class="fa fa-play-circle-o"></i> <span data-i18n="knxUltimateMatterControllerDevice.control"></span></label>
|
|
1698
|
+
<label for="node-input-nameLightSwitch" style="width:110px;"><i class="fa fa-play-circle-o"></i> <span id="matter-switch-command-label" data-i18n="knxUltimateMatterControllerDevice.control"></span></label>
|
|
1569
1699
|
|
|
1570
1700
|
<label for="node-input-GALightSwitch" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1571
1701
|
<input type="text" id="node-input-GALightSwitch" placeholder="Ex: 1/1/1"
|
|
@@ -1578,7 +1708,7 @@
|
|
|
1578
1708
|
<input type="text" id="node-input-nameLightSwitch" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1579
1709
|
</div>
|
|
1580
1710
|
<div class="form-row">
|
|
1581
|
-
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-question-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.status"></span></label>
|
|
1711
|
+
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-question-circle"></i> <span id="matter-switch-status-label" data-i18n="knxUltimateMatterControllerDevice.status"></span></label>
|
|
1582
1712
|
|
|
1583
1713
|
<label for="node-input-GALightState" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1584
1714
|
<input type="text" id="node-input-GALightState" placeholder="Ex: 1/1/1"
|
|
@@ -1591,6 +1721,14 @@
|
|
|
1591
1721
|
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1592
1722
|
<input type="text" id="node-input-nameLightState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1593
1723
|
</div>
|
|
1724
|
+
<div id="matter-door-lock-options" style="display:none;">
|
|
1725
|
+
<div class="form-row">
|
|
1726
|
+
<label for="node-input-doorLockPin" style="width:260px;"><i class="fa fa-key"></i> <span data-i18n="knxUltimateMatterControllerDevice.door_lock_pin"></span></label>
|
|
1727
|
+
<input type="password" id="node-input-doorLockPin" style="width:210px;" autocomplete="new-password">
|
|
1728
|
+
</div>
|
|
1729
|
+
<div id="matter-door-lock-pin-required" class="form-tips" style="display:none;" data-i18n="knxUltimateMatterControllerDevice.door_lock_pin_required"></div>
|
|
1730
|
+
<div id="matter-door-lock-command-warning" class="form-tips" style="display:none; color:#a94442;" data-i18n="knxUltimateMatterControllerDevice.door_lock_commands_missing"></div>
|
|
1731
|
+
</div>
|
|
1594
1732
|
</p>
|
|
1595
1733
|
</div>
|
|
1596
1734
|
<div id="tabs-2">
|
|
@@ -2006,14 +2144,13 @@
|
|
|
2006
2144
|
|
|
2007
2145
|
|
|
2008
2146
|
<script type="text/markdown" data-help-name="knxUltimateMatterControllerDevice">
|
|
2009
|
-
<p>This node controls
|
|
2147
|
+
<p>This node controls commissioned Matter endpoints and maps their commands and states to KNX.</p>
|
|
2010
2148
|
|
|
2011
2149
|
**General**
|
|
2012
2150
|
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
Hue-only features (native effects, locate) are not available on Matter.
|
|
2151
|
+
The editor detects the selected endpoint capabilities and exposes only the corresponding
|
|
2152
|
+
Matter functions. Bridged Matter devices are expanded into their individual endpoints in
|
|
2153
|
+
the device picker.
|
|
2017
2154
|
|
|
2018
2155
|
|Property|Description|
|
|
2019
2156
|
|--|--|
|
|
@@ -2025,14 +2162,14 @@ Hue-only features (native effects, locate) are not available on Matter.
|
|
|
2025
2162
|
|
|
2026
2163
|
**OPTIONS**
|
|
2027
2164
|
|
|
2028
|
-
Here you can link KNX Group Addresses to the available
|
|
2165
|
+
Here you can link KNX Group Addresses to the available Matter commands and states.<br/>
|
|
2029
2166
|
Start typing in the GA field (name or Group Address); suggestions appear while you type.
|
|
2030
2167
|
|
|
2031
2168
|
**Switch**
|
|
2032
2169
|
|
|
2033
2170
|
|Property|Description|
|
|
2034
2171
|
|--|--|
|
|
2035
|
-
| Control | This GA
|
|
2172
|
+
| Control | This GA turns the Matter light on or off via a boolean KNX value true/false. |
|
|
2036
2173
|
| Status | Link this to the light's switch status group address|
|
|
2037
2174
|
|
|
2038
2175
|
<br/>
|
|
@@ -2041,8 +2178,8 @@ Start typing in the GA field (name or Group Address); suggestions appear while y
|
|
|
2041
2178
|
|
|
2042
2179
|
|Property|Description|
|
|
2043
2180
|
|--|--|
|
|
2044
|
-
| Control dim | Relative DIM of the
|
|
2045
|
-
| Control % | Changes the absolute
|
|
2181
|
+
| Control dim | Relative DIM of the Matter light. You can set the dimming speed in the **Behaviour** tab. |
|
|
2182
|
+
| Control % | Changes the absolute Matter light brightness (0-100%). |
|
|
2046
2183
|
| Status % | Link this to the light's brightness status KNX group address |
|
|
2047
2184
|
| Dim Speed (ms) | Dimming speed in milliseconds. Applies to both the light brightness and the tunable-white datapoints. Calculated over the 0%→100% range. |
|
|
2048
2185
|
| Min Dim brightness | Tha Minimum brightness that the lamp can reach. For example, if you are dimming the light down, the light will stop dimming at the specified brightness %. |
|
|
@@ -2057,8 +2194,8 @@ Start typing in the GA field (name or Group Address); suggestions appear while y
|
|
|
2057
2194
|
| Control dim | Change white temperature using DPT 3.007 dimming. Speed is set in the **Behaviour** tab.|
|
|
2058
2195
|
| Control % | Change white temperature using DPT 5.001. 0 = full warm, 100 = full cold.|
|
|
2059
2196
|
| Status %| Temperature status GA. DPT 5.001 absolute value: 0 = full warm, 100 = full cold.|
|
|
2060
|
-
| Control kelvin | **DPT 7.600
|
|
2061
|
-
| Status kelvin | **DPT 7.600
|
|
2197
|
+
| Control kelvin | **DPT 7.600:** set the Matter color temperature in Kelvin in the supported endpoint range.<br/>**DPT 9.002:** set the temperature as a KNX floating-point value. Conversions may introduce small deviations. |
|
|
2198
|
+
| Status kelvin | **DPT 7.600:** read the Matter color temperature in Kelvin.<br/>**DPT 9.002:** read the temperature as a KNX floating-point value. |
|
|
2062
2199
|
| Invert dim direction | Inverts the DIM direction. |
|
|
2063
2200
|
<br/>
|
|
2064
2201
|
|
|
@@ -2082,12 +2219,12 @@ For controlling the HSV "V” (brightness), use the standard controls under the
|
|
|
2082
2219
|
|
|
2083
2220
|
**Effects**
|
|
2084
2221
|
|
|
2085
|
-
|
|
2222
|
+
_Basic Matter effects_
|
|
2086
2223
|
|
|
2087
2224
|
|Property|Description|
|
|
2088
2225
|
|--|--|
|
|
2089
|
-
| Blink | _true_
|
|
2090
|
-
| Color Cycle | _true_
|
|
2226
|
+
| Blink | _true_ blinks the light, _false_ stops blinking. Useful for signalling and available on compatible Matter lights. |
|
|
2227
|
+
| Color Cycle | _true_ starts the cycle, _false_ stops it. Randomly changes the Matter light color at regular intervals when color control is supported. |
|
|
2091
2228
|
|
|
2092
2229
|
<br/>
|
|
2093
2230
|
|
|
@@ -2095,16 +2232,16 @@ _Non-Hue basic effects_
|
|
|
2095
2232
|
|
|
2096
2233
|
| Property | Description |
|
|
2097
2234
|
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2098
|
-
| Read status at startup | Read the
|
|
2099
|
-
| KNX Brightness Status | Updates the KNX brightness
|
|
2100
|
-
| Update local cached
|
|
2235
|
+
| Read status at startup | Read the Matter endpoint status at Node-RED startup or full deploy and send it to the KNX bus. |
|
|
2236
|
+
| KNX Brightness Status | Updates the KNX brightness status whenever the Matter light switches ON/OFF. It can send 0% while OFF and restore the previous value when ON, or leave the brightness status unchanged. |
|
|
2237
|
+
| Update local cached Matter state from KNX bus writes | When enabled, KNX writes immediately update the local Matter-state cache without waiting for the endpoint report. Disable it if the cache should follow only real Matter reports. |
|
|
2101
2238
|
| Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/> **Select color: ** the light will be switched on with the color of your choice. To change color, just CLICK on the color selector (under the _Select color_ control).<br/>**Select temperature and brightness: ** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br/>**None:** the light will retain its last status. In case you've enable the night lighting, after the night time ends, the lamp will resume the color/temperature/brightness state set at day time. |
|
|
2102
2239
|
| Night Lighting | It allows to set a particular light color/brightness at nighttime. The options are the same as the daytime. You could select either a temperature/brightness or color. A cozy temperature of 2700 Kelvin, with a brightness of 10% or 20%, is a good choice for bathroom's night light.|
|
|
2103
2240
|
| Day/Night | Select the group address used to set the day/night behaviour. The group address value is _true_ if daytime, _false_ if nighttime. |
|
|
2104
2241
|
| Invert day/night value | Invert the values of _Day/Night_ group address. Default value is **unchecked** . |
|
|
2105
2242
|
| Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
|
|
2106
2243
|
| Force day mode | You can force the day mode by manually switching the light as described here: **Switch to DAY mode by rapid switching the ligth off then on (This light only) ** does what described and acts only on this light.**Switch to DAY mode by rapid switching the ligth off then on (apply yo ALL light nodes)** acts to ALL Light nodes, by setting the Day/Night group address to Day mode. |
|
|
2107
|
-
| Node Input/Output PINs | Hide or show the input/output PINs.
|
|
2244
|
+
| Node Input/Output PINs | Hide or show the flow input/output PINs. The input accepts supported Matter commands and the output emits validated Matter endpoint events. The selection is persisted with the node. |
|
|
2108
2245
|
|
|
2109
2246
|
### Note
|
|
2110
2247
|
|
|
@@ -9,6 +9,7 @@ const dptlib = require('knxultimate').dptlib
|
|
|
9
9
|
const hueColorConverter = require('./utils/colorManipulators/hueColorConverter')
|
|
10
10
|
const { createLightEngine } = require('./utils/lightEngines')
|
|
11
11
|
const { hueStateToCanonical, matterEventToHuePatch } = require('./utils/lightEngines/matterHueShim')
|
|
12
|
+
const { setupMatterControllerProfile } = require('./utils/matterControllerProfiles')
|
|
12
13
|
|
|
13
14
|
module.exports = function (RED) {
|
|
14
15
|
function knxUltimateMatterControllerDevice (config) {
|
|
@@ -20,6 +21,10 @@ module.exports = function (RED) {
|
|
|
20
21
|
node.matterEndpointId = config.matterEndpointId !== undefined ? Number(config.matterEndpointId) : 1
|
|
21
22
|
node.matterDeviceName = config.matterDeviceName !== undefined ? String(config.matterDeviceName) : ''
|
|
22
23
|
|
|
24
|
+
let matterCapabilities = {}
|
|
25
|
+
try { matterCapabilities = JSON.parse(config.matterDeviceCapabilities || '{}') } catch (error) { /* empty */ }
|
|
26
|
+
if (setupMatterControllerProfile(matterCapabilities.profile, RED, node, config)) return
|
|
27
|
+
|
|
23
28
|
// Matter engine (adapter). The manager is resolved lazily at each write, because
|
|
24
29
|
// matter-config creates its matterManager asynchronously after the deploy.
|
|
25
30
|
node.lightEngine = createLightEngine('matter', {
|
|
@@ -110,8 +115,8 @@ module.exports = function (RED) {
|
|
|
110
115
|
config.GADaylightSensor
|
|
111
116
|
].map((ga) => String(ga || '').trim()).filter((ga) => ga !== '')
|
|
112
117
|
const isConfiguredKNXGA = (ga) => node.knxUltimateAcceptedGAs.includes(String(ga || '').trim())
|
|
113
|
-
// Synthetic
|
|
114
|
-
// reports. It lets the
|
|
118
|
+
// Synthetic current-device compatibility state, kept in sync from Matter attribute
|
|
119
|
+
// reports. It lets the established light logic run untouched on a Matter light.
|
|
115
120
|
node.currentHUEDevice = {
|
|
116
121
|
id: node.matterNodeId,
|
|
117
122
|
type: 'light',
|
|
@@ -216,14 +221,14 @@ module.exports = function (RED) {
|
|
|
216
221
|
node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
|
|
217
222
|
} catch (error) { }
|
|
218
223
|
}
|
|
219
|
-
//
|
|
224
|
+
// Compatibility callback used to update the Matter node status.
|
|
220
225
|
node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
|
|
221
226
|
try {
|
|
222
227
|
if (node.currentHUEDevice?.on?.on === true) { fill = 'blue'; shape = 'dot' } else { fill = 'blue'; shape = 'ring' };
|
|
223
228
|
if (payload === undefined) payload = ''
|
|
224
229
|
const dDate = new Date()
|
|
225
230
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
|
|
226
|
-
node.sHUENodeStatusText = `|
|
|
231
|
+
node.sHUENodeStatusText = `|Matter: ${text} ${payload} (${formatTs(dDate)})`
|
|
227
232
|
node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
|
|
228
233
|
} catch (error) { }
|
|
229
234
|
}
|
|
@@ -318,7 +323,7 @@ module.exports = function (RED) {
|
|
|
318
323
|
return Math.floor(Math.random() * (max - min + 1) + min) // The maximum is inclusive and the minimum is inclusive
|
|
319
324
|
}
|
|
320
325
|
|
|
321
|
-
//
|
|
326
|
+
// Compatibility callback invoked by the Matter controller adapter.
|
|
322
327
|
node.handleSend = (msg) => {
|
|
323
328
|
if (!msg || !msg.knx || !isConfiguredKNXGA(msg.knx.destination)) return
|
|
324
329
|
if (node.currentHUEDevice === undefined && node.serverHue.linkStatus === 'connected') {
|
|
@@ -1161,7 +1166,7 @@ module.exports = function (RED) {
|
|
|
1161
1166
|
node.setNodeStatusHue({
|
|
1162
1167
|
fill: 'red',
|
|
1163
1168
|
shape: 'ring',
|
|
1164
|
-
text: "Rejected
|
|
1169
|
+
text: "Rejected Matter light settings. I'm still not ready...",
|
|
1165
1170
|
payload: ''
|
|
1166
1171
|
})
|
|
1167
1172
|
return
|
|
@@ -15,11 +15,13 @@ Er ersetzt die unveröffentlichten getrennten Matter-Controller-Nodes und behäl
|
|
|
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`. |
|
|
18
|
+
| Türschloss | Eine DPT-`1.xxx`-Befehls-GA ruft bei `true` `lockDoor` und bei `false` `unlockDoor` auf; eine separate Status-GA erhält nur eindeutige Verriegelt/Entriegelt-Zustände. Falls erforderlich, wird die Fernbedienungs-PIN im Credential-Feld gespeichert. Nicht angebotene Befehle werden abgewiesen. |
|
|
19
|
+
| Andere Endpunkte | Steckdosen, Ein/Aus-Aktoren, Jalousien, Thermostate, Lüfter, Umwelt-/Kontakt-/Belegungssensoren sowie Batterie-, Leistungs- und Energieendpunkte verwenden das Mehrzweckprofil. Der Tab **Zuordnungen** enthält nur tatsächlich angebotene Cluster, Attribute und Befehle. |
|
|
18
20
|
| Lichtsteuerung | Für Licht-Endpunkte wird die vollständige Licht-UI verwendet: relatives DIM (DPT `3.007`), Helligkeit %, RGB/HSV, Tunable White, Einschalt-Helligkeit/-Temperatur, Tag/Nacht-Licht, Min/Max-Dimmlevel und Dimmgeschwindigkeit. Nicht unterstützte Bereiche bleiben ausgeblendet. |
|
|
19
21
|
| Sensoren | Sensor-Endpunkte zeigen ihre Mess-/Status-GA nur bei Unterstützung: Temperatur, Feuchte, Helligkeit, Präsenz, Kontakt und Batterie. |
|
|
20
22
|
| Read at startup | Veröffentlicht den gecachten Matter-Wert beim Deploy/Start oder wenn sich das Gerät erneut verbindet. |
|
|
21
23
|
| Update local state from KNX write | Aktualisiert den lokalen Matter/KNX-Cache, wenn ein Telegramm auf eine konfigurierte KNX-GA geschrieben wird. |
|
|
22
|
-
| Node Input/Output PINs | Zeigt Node-RED-Eingangs-/Ausgangspins. Der Eingang akzeptiert boolesche Payloads sowie Matter-ähnliche `msg.payload` oder `msg.on.on`; der Ausgang sendet Statusupdates. |
|
|
24
|
+
| Node Input/Output PINs | Zeigt Node-RED-Eingangs-/Ausgangspins. Der Eingang akzeptiert boolesche Payloads sowie Matter-ähnliche `msg.payload` oder `msg.on.on`; der Ausgang sendet Statusupdates. Die Auswahl bleibt beim erneuten Öffnen des Editors erhalten. |
|
|
23
25
|
|
|
24
26
|
## Verhalten
|
|
25
27
|
|