node-red-contrib-knx-ultimate 6.0.7 → 6.0.9
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 -1
- package/nodes/knxUltimateMatterBridge.js +2 -0
- package/nodes/knxUltimateMatterControllerDevice.html +29 -37
- package/nodes/knxUltimateMatterControllerDevice.js +10 -6
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/de/knxUltimateMatterControllerDevice.json +15 -15
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/en/knxUltimateMatterControllerDevice.json +16 -16
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/es/knxUltimateMatterControllerDevice.json +9 -9
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +6 -6
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/it/knxUltimateMatterControllerDevice.json +15 -15
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +1 -1
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +11 -11
- package/nodes/matter-config.js +4 -1
- package/nodes/utils/matterControllerProfiles/doorLock.js +16 -0
- package/nodes/utils/matterControllerProfiles/index.js +3 -0
- package/nodes/utils/matterControllerProfiles/mappedEndpoint.js +14 -0
- package/nodes/utils/matterEngine.mjs +23 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
-
**Version 6.0.
|
|
9
|
+
**Version 6.0.9** - July 2026<br/>
|
|
10
10
|
|
|
11
|
+
- **Matter Controller — post-commission naming**: assigning a name immediately after pairing now waits for matter.js to finish exposing the root `BasicInformation` cluster. Root endpoint 0 is resolved explicitly, preventing the misleading `BasicInformation cluster not found` warning seen when re-adding devices such as Shelly Plug.<br/>
|
|
12
|
+
- **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/>
|
|
13
|
+
- **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/>
|
|
14
|
+
- **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/>
|
|
11
15
|
- **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/>
|
|
12
16
|
- **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/>
|
|
13
17
|
- **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/>
|
|
@@ -157,6 +157,8 @@ module.exports = function (RED) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
const safeSendToKNX = (telegram, context = 'write') => {
|
|
160
|
+
// Matter command handlers are asynchronous and shared by many endpoints. Guard
|
|
161
|
+
// each KNX dispatch so one unavailable gateway cannot break other device routes.
|
|
160
162
|
try {
|
|
161
163
|
if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
|
|
162
164
|
node.setNodeStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context})` })
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
ensureVerticalTabsStyle();
|
|
220
220
|
const $knxServerInput = $("#node-input-server");
|
|
221
221
|
const KNX_EMPTY_VALUES = new Set(['', 'none', '_ADD_', '__NONE__']);
|
|
222
|
-
//
|
|
222
|
+
// Historical variable names are retained for saved-flow compatibility; they point to Matter widgets.
|
|
223
223
|
const $hueServerInput = $("#node-input-serverMatter");
|
|
224
224
|
const $hueDeviceInput = $("#node-input-matterNodeId");
|
|
225
225
|
const $deviceNameInput = $("#node-input-matterDeviceName");
|
|
@@ -287,8 +287,8 @@
|
|
|
287
287
|
$icon.removeClass('fa-stop fa-play').addClass(locateSessionActive ? 'fa fa-stop' : 'fa fa-play');
|
|
288
288
|
}
|
|
289
289
|
const title = locateSessionActive
|
|
290
|
-
? (node._('knxUltimateMatterControllerDevice.locate_stop_title') || 'Stop
|
|
291
|
-
: (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');
|
|
292
292
|
$locateDeviceButton.attr('title', title);
|
|
293
293
|
};
|
|
294
294
|
|
|
@@ -344,8 +344,8 @@
|
|
|
344
344
|
if (!context) {
|
|
345
345
|
if (!silent) {
|
|
346
346
|
const message = !resolveHueServerValue({ allowStored: true })
|
|
347
|
-
? (node._('knxUltimateMatterControllerDevice.locate_no_bridge') || 'Select a
|
|
348
|
-
: (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');
|
|
349
349
|
RED.notify(message, 'warning');
|
|
350
350
|
}
|
|
351
351
|
updateLocateButtonState(false);
|
|
@@ -414,7 +414,7 @@
|
|
|
414
414
|
updateLocateButtonState(false);
|
|
415
415
|
clearLocateAutoReset();
|
|
416
416
|
if (!silent) {
|
|
417
|
-
RED.notify(message || (node._('knxUltimateMatterControllerDevice.locate_error') || 'Unable to locate
|
|
417
|
+
RED.notify(message || (node._('knxUltimateMatterControllerDevice.locate_error') || 'Unable to locate Matter device'), 'error');
|
|
418
418
|
}
|
|
419
419
|
}).always(() => {
|
|
420
420
|
locatePendingRequest = null;
|
|
@@ -627,7 +627,7 @@
|
|
|
627
627
|
const serializeMatterCapabilities = (capabilities) => JSON.stringify(capabilities || {});
|
|
628
628
|
const getJSONPromise = (url) => new Promise((resolve, reject) => { $.getJSON(url, resolve).fail(reject); });
|
|
629
629
|
|
|
630
|
-
//
|
|
630
|
+
// Filters Matter items shaped as { value, matterNodeId, matterEndpointId }.
|
|
631
631
|
const filterHueDevices = (devices, term) => {
|
|
632
632
|
const cleaned = (term || '').replace(/exactmatch/gi, '').trim();
|
|
633
633
|
return $.map(devices, (item) => {
|
|
@@ -757,11 +757,11 @@
|
|
|
757
757
|
$.getJSON(`knxUltimateDpts?serverId=${serverId}&_=${Date.now()}`, (data) => {
|
|
758
758
|
data.forEach((dpt) => {
|
|
759
759
|
if (prefixes.some((prefix) => prefix === "" || dpt.value.startsWith(prefix))) {
|
|
760
|
-
// Adjustment for
|
|
760
|
+
// Adjustment for Matter color temperature
|
|
761
761
|
if (dpt.value.startsWith("7.600")) {
|
|
762
762
|
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - KNX Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute)"));
|
|
763
763
|
} else if (dpt.value.startsWith("9.002")) {
|
|
764
|
-
$(_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)"));
|
|
765
765
|
} else if (dpt.value.startsWith("5.001")) {
|
|
766
766
|
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - Homeassistant color_temperature_mode: relative"));
|
|
767
767
|
} else {
|
|
@@ -1084,13 +1084,6 @@
|
|
|
1084
1084
|
}
|
|
1085
1085
|
applyMatterCapabilities(currentMatterCapabilities);
|
|
1086
1086
|
|
|
1087
|
-
if ($pinSelect.length) {
|
|
1088
|
-
const desiredPins = knxSelected ? 'no' : 'yes';
|
|
1089
|
-
if ($pinSelect.val() !== desiredPins) {
|
|
1090
|
-
$pinSelect.val(desiredPins).trigger('change');
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1087
|
if ($pinSectionRow.length) {
|
|
1095
1088
|
$pinSectionRow.show();
|
|
1096
1089
|
}
|
|
@@ -1236,7 +1229,7 @@
|
|
|
1236
1229
|
.append($("<option>").val("temperature").text(node._("knxUltimateMatterControllerDevice.select_temperature_brightness")));
|
|
1237
1230
|
$("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness);
|
|
1238
1231
|
$("#node-input-enableDayNightLighting").val(node.enableDayNightLighting);
|
|
1239
|
-
// "Get current" reads
|
|
1232
|
+
// Live "Get current" reads are not available on this Matter path.
|
|
1240
1233
|
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
1241
1234
|
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
1242
1235
|
applyMatterCapabilities(currentMatterCapabilities);
|
|
@@ -1261,8 +1254,8 @@
|
|
|
1261
1254
|
if ($("#node-input-enableDayNightLighting").val() === "yes") blinkBackground("#colorPickerNight");
|
|
1262
1255
|
});
|
|
1263
1256
|
|
|
1264
|
-
// "Get current"
|
|
1265
|
-
//
|
|
1257
|
+
// Live "Get current" reads do not exist on this Matter path; the buttons are
|
|
1258
|
+
// hidden by populateSwitchOnCombos above.
|
|
1266
1259
|
|
|
1267
1260
|
// Fill options for minDimLevel and maxDimLevel and comboBrightnessAtSwitchOn (for color brightness at switch on, with temperature toghedher)
|
|
1268
1261
|
for (let index = 100; index >= 0; index -= 5) {
|
|
@@ -2151,14 +2144,13 @@
|
|
|
2151
2144
|
|
|
2152
2145
|
|
|
2153
2146
|
<script type="text/markdown" data-help-name="knxUltimateMatterControllerDevice">
|
|
2154
|
-
<p>This node controls
|
|
2147
|
+
<p>This node controls commissioned Matter endpoints and maps their commands and states to KNX.</p>
|
|
2155
2148
|
|
|
2156
2149
|
**General**
|
|
2157
2150
|
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
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.
|
|
2162
2154
|
|
|
2163
2155
|
|Property|Description|
|
|
2164
2156
|
|--|--|
|
|
@@ -2170,14 +2162,14 @@ Hue-only features (native effects, locate) are not available on Matter.
|
|
|
2170
2162
|
|
|
2171
2163
|
**OPTIONS**
|
|
2172
2164
|
|
|
2173
|
-
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/>
|
|
2174
2166
|
Start typing in the GA field (name or Group Address); suggestions appear while you type.
|
|
2175
2167
|
|
|
2176
2168
|
**Switch**
|
|
2177
2169
|
|
|
2178
2170
|
|Property|Description|
|
|
2179
2171
|
|--|--|
|
|
2180
|
-
| Control | This GA
|
|
2172
|
+
| Control | This GA turns the Matter light on or off via a boolean KNX value true/false. |
|
|
2181
2173
|
| Status | Link this to the light's switch status group address|
|
|
2182
2174
|
|
|
2183
2175
|
<br/>
|
|
@@ -2186,8 +2178,8 @@ Start typing in the GA field (name or Group Address); suggestions appear while y
|
|
|
2186
2178
|
|
|
2187
2179
|
|Property|Description|
|
|
2188
2180
|
|--|--|
|
|
2189
|
-
| Control dim | Relative DIM of the
|
|
2190
|
-
| 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%). |
|
|
2191
2183
|
| Status % | Link this to the light's brightness status KNX group address |
|
|
2192
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. |
|
|
2193
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 %. |
|
|
@@ -2202,8 +2194,8 @@ Start typing in the GA field (name or Group Address); suggestions appear while y
|
|
|
2202
2194
|
| Control dim | Change white temperature using DPT 3.007 dimming. Speed is set in the **Behaviour** tab.|
|
|
2203
2195
|
| Control % | Change white temperature using DPT 5.001. 0 = full warm, 100 = full cold.|
|
|
2204
2196
|
| Status %| Temperature status GA. DPT 5.001 absolute value: 0 = full warm, 100 = full cold.|
|
|
2205
|
-
| Control kelvin | **DPT 7.600
|
|
2206
|
-
| 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. |
|
|
2207
2199
|
| Invert dim direction | Inverts the DIM direction. |
|
|
2208
2200
|
<br/>
|
|
2209
2201
|
|
|
@@ -2227,12 +2219,12 @@ For controlling the HSV "V” (brightness), use the standard controls under the
|
|
|
2227
2219
|
|
|
2228
2220
|
**Effects**
|
|
2229
2221
|
|
|
2230
|
-
|
|
2222
|
+
_Basic Matter effects_
|
|
2231
2223
|
|
|
2232
2224
|
|Property|Description|
|
|
2233
2225
|
|--|--|
|
|
2234
|
-
| Blink | _true_
|
|
2235
|
-
| 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. |
|
|
2236
2228
|
|
|
2237
2229
|
<br/>
|
|
2238
2230
|
|
|
@@ -2240,16 +2232,16 @@ _Non-Hue basic effects_
|
|
|
2240
2232
|
|
|
2241
2233
|
| Property | Description |
|
|
2242
2234
|
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2243
|
-
| Read status at startup | Read the
|
|
2244
|
-
| KNX Brightness Status | Updates the KNX brightness
|
|
2245
|
-
| 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. |
|
|
2246
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. |
|
|
2247
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.|
|
|
2248
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. |
|
|
2249
2241
|
| Invert day/night value | Invert the values of _Day/Night_ group address. Default value is **unchecked** . |
|
|
2250
2242
|
| Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
|
|
2251
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. |
|
|
2252
|
-
| 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. |
|
|
2253
2245
|
|
|
2254
2246
|
### Note
|
|
2255
2247
|
|
|
@@ -23,6 +23,10 @@ module.exports = function (RED) {
|
|
|
23
23
|
|
|
24
24
|
let matterCapabilities = {}
|
|
25
25
|
try { matterCapabilities = JSON.parse(config.matterDeviceCapabilities || '{}') } catch (error) { /* empty */ }
|
|
26
|
+
// Non-light endpoints are handled by isolated profiles. A successful profile setup
|
|
27
|
+
// owns the complete node lifecycle and deliberately bypasses the legacy light path.
|
|
28
|
+
// Keeping this boundary here prevents new device types from changing established
|
|
29
|
+
// light behaviour or its saved configuration contract.
|
|
26
30
|
if (setupMatterControllerProfile(matterCapabilities.profile, RED, node, config)) return
|
|
27
31
|
|
|
28
32
|
// Matter engine (adapter). The manager is resolved lazily at each write, because
|
|
@@ -115,8 +119,8 @@ module.exports = function (RED) {
|
|
|
115
119
|
config.GADaylightSensor
|
|
116
120
|
].map((ga) => String(ga || '').trim()).filter((ga) => ga !== '')
|
|
117
121
|
const isConfiguredKNXGA = (ga) => node.knxUltimateAcceptedGAs.includes(String(ga || '').trim())
|
|
118
|
-
// Synthetic
|
|
119
|
-
// reports. It lets the
|
|
122
|
+
// Synthetic current-device compatibility state, kept in sync from Matter attribute
|
|
123
|
+
// reports. It lets the established light logic run untouched on a Matter light.
|
|
120
124
|
node.currentHUEDevice = {
|
|
121
125
|
id: node.matterNodeId,
|
|
122
126
|
type: 'light',
|
|
@@ -221,14 +225,14 @@ module.exports = function (RED) {
|
|
|
221
225
|
node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
|
|
222
226
|
} catch (error) { }
|
|
223
227
|
}
|
|
224
|
-
//
|
|
228
|
+
// Compatibility callback used to update the Matter node status.
|
|
225
229
|
node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
|
|
226
230
|
try {
|
|
227
231
|
if (node.currentHUEDevice?.on?.on === true) { fill = 'blue'; shape = 'dot' } else { fill = 'blue'; shape = 'ring' };
|
|
228
232
|
if (payload === undefined) payload = ''
|
|
229
233
|
const dDate = new Date()
|
|
230
234
|
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
|
|
231
|
-
node.sHUENodeStatusText = `|
|
|
235
|
+
node.sHUENodeStatusText = `|Matter: ${text} ${payload} (${formatTs(dDate)})`
|
|
232
236
|
node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
|
|
233
237
|
} catch (error) { }
|
|
234
238
|
}
|
|
@@ -323,7 +327,7 @@ module.exports = function (RED) {
|
|
|
323
327
|
return Math.floor(Math.random() * (max - min + 1) + min) // The maximum is inclusive and the minimum is inclusive
|
|
324
328
|
}
|
|
325
329
|
|
|
326
|
-
//
|
|
330
|
+
// Compatibility callback invoked by the Matter controller adapter.
|
|
327
331
|
node.handleSend = (msg) => {
|
|
328
332
|
if (!msg || !msg.knx || !isConfiguredKNXGA(msg.knx.destination)) return
|
|
329
333
|
if (node.currentHUEDevice === undefined && node.serverHue.linkStatus === 'connected') {
|
|
@@ -1166,7 +1170,7 @@ module.exports = function (RED) {
|
|
|
1166
1170
|
node.setNodeStatusHue({
|
|
1167
1171
|
fill: 'red',
|
|
1168
1172
|
shape: 'ring',
|
|
1169
|
-
text: "Rejected
|
|
1173
|
+
text: "Rejected Matter light settings. I'm still not ready...",
|
|
1170
1174
|
payload: ''
|
|
1171
1175
|
})
|
|
1172
1176
|
return
|
|
@@ -21,7 +21,7 @@ Er ersetzt die unveröffentlichten getrennten Matter-Controller-Nodes und behäl
|
|
|
21
21
|
| Sensoren | Sensor-Endpunkte zeigen ihre Mess-/Status-GA nur bei Unterstützung: Temperatur, Feuchte, Helligkeit, Präsenz, Kontakt und Batterie. |
|
|
22
22
|
| Read at startup | Veröffentlicht den gecachten Matter-Wert beim Deploy/Start oder wenn sich das Gerät erneut verbindet. |
|
|
23
23
|
| Update local state from KNX write | Aktualisiert den lokalen Matter/KNX-Cache, wenn ein Telegramm auf eine konfigurierte KNX-GA geschrieben wird. |
|
|
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. |
|
|
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. |
|
|
25
25
|
|
|
26
26
|
## Verhalten
|
|
27
27
|
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"get_current": "Aktuellen Wert holen",
|
|
36
36
|
"get_again": "Erneut holen",
|
|
37
37
|
"wait": "Bitte warten...",
|
|
38
|
-
"locate_no_bridge": "Bitte zuerst
|
|
39
|
-
"locate_no_device": "Bitte zuerst ein
|
|
38
|
+
"locate_no_bridge": "Bitte zuerst einen Matter-Controller auswählen",
|
|
39
|
+
"locate_no_device": "Bitte zuerst ein Matter-Gerät auswählen",
|
|
40
40
|
"locate_success": "Locate-Befehl gesendet",
|
|
41
41
|
"locate_started": "Locate-Modus gestartet. Nochmal drücken, um zu stoppen (automatisch nach 10 Minuten).",
|
|
42
42
|
"locate_stopped": "Locate-Modus beendet.",
|
|
43
|
-
"locate_start_title": "Ausgewähltes
|
|
43
|
+
"locate_start_title": "Ausgewähltes Matter-Gerät lokalisieren",
|
|
44
44
|
"locate_stop_title": "Locate-Modus beenden",
|
|
45
|
-
"locate_error": "
|
|
45
|
+
"locate_error": "Matter-Gerät konnte nicht lokalisiert werden",
|
|
46
46
|
"day_night": "Tag/Nacht",
|
|
47
47
|
"invert_day_night": "Tag/Nacht-Wert invertieren",
|
|
48
48
|
"override_night_mode": "Tagmodus erzwingen",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"opt_no": "Nein",
|
|
57
57
|
"opt_yes_emit": "Ja, und KNX-Telegramme senden.",
|
|
58
58
|
"knx_brightness_status": "KNX Helligkeitsstatus",
|
|
59
|
-
"knx_brightness_onhueoff": "Wenn
|
|
60
|
-
"knx_brightness_no": "Unverändert lassen (Standard
|
|
61
|
-
"update_local_state_from_knx_write": "Lokalen
|
|
62
|
-
"update_local_state_from_knx_write_hint": "Aktiviert: schnellere lokale Reaktionen und konsistentere sofortige KNX-Leseantworten. Deaktiviert: Cache nur durch echte
|
|
63
|
-
"use_min_brightness": "Minimale Helligkeit der
|
|
59
|
+
"knx_brightness_onhueoff": "Wenn Matter aus: 0% senden. Wenn Matter an: vorherigen Wert wiederherstellen (Standard-KNX-Verhalten)",
|
|
60
|
+
"knx_brightness_no": "Unverändert lassen (Standard-Matter-Verhalten)",
|
|
61
|
+
"update_local_state_from_knx_write": "Lokalen Matter-Cache durch KNX-Bus-Schreibtelegramme aktualisieren",
|
|
62
|
+
"update_local_state_from_knx_write_hint": "Aktiviert: schnellere lokale Reaktionen und konsistentere sofortige KNX-Leseantworten. Deaktiviert: Cache nur durch echte Matter-Berichte aktualisieren.",
|
|
63
|
+
"use_min_brightness": "Minimale Helligkeit der Matter-Lampe verwenden",
|
|
64
64
|
"k_suffix": "K",
|
|
65
65
|
"temp_desc_2200": "(Beginn der Philips White Ambiance Reihe)",
|
|
66
66
|
"temp_desc_2700": "(Warmweiß, intim, gemütlich, persönlich, für Wohnzimmer)",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
"effect_status": "Effekt-Status",
|
|
84
84
|
"effect_mapping": "Zuordnungen",
|
|
85
85
|
"effect_autofill": "Mit verfügbaren Effekten füllen",
|
|
86
|
-
"effect_tip": "Hinterlege KNX-Wert/
|
|
86
|
+
"effect_tip": "Hinterlege KNX-Wert/Matter-Effekt-Paare. Bei passendem KNX-Wert wird der ausgewählte Effekt ausgelöst.",
|
|
87
87
|
"effect_tip_status": "Ist eine Status-GA gesetzt, sendet der Knoten den aktuellen Effekt (zugeordneter Wert oder Name) auf den KNX-Bus.",
|
|
88
|
-
"effect_not_supported": "Diese Leuchte stellt keine
|
|
88
|
+
"effect_not_supported": "Diese Leuchte stellt keine Matter-Effekte bereit.",
|
|
89
89
|
"effect_knx_value_placeholder": "Abzugleichender Wert",
|
|
90
|
-
"effect_base_label": "
|
|
91
|
-
"effect_native_label": "
|
|
90
|
+
"effect_base_label": "Matter-Basiseffekte",
|
|
91
|
+
"effect_native_label": "Native Matter-Effekte",
|
|
92
92
|
"matter_controller": "Matter-Controller",
|
|
93
93
|
"matter_device": "Matter-Gerät",
|
|
94
94
|
"device_type": "Gerätetyp",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"name": "Name",
|
|
101
101
|
"youtube_sample": "YouTube-Beispiel",
|
|
102
102
|
"knx_gw": "KNX-Gateway",
|
|
103
|
-
"hue_bridge": "
|
|
104
|
-
"philips_hue": "
|
|
103
|
+
"hue_bridge": "Matter-Controller",
|
|
104
|
+
"philips_hue": "Matter",
|
|
105
105
|
"read": "Lesen"
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -21,7 +21,7 @@ It replaces the unpublished per-device Matter controller nodes and keeps the ful
|
|
|
21
21
|
| Sensors | Sensor endpoints expose their measurement/status GA only when supported: temperature, humidity, illuminance, occupancy, contact and battery. |
|
|
22
22
|
| Read at startup | Publishes the cached Matter value at deploy/startup or when the device reconnects. |
|
|
23
23
|
| Update local state from KNX write | Updates the local Matter/KNX cache when a telegram is written on a configured KNX GA. |
|
|
24
|
-
| Node Input/Output PINs | Shows Node-RED input/output pins. Input accepts boolean payloads and Matter-style `msg.payload` or `msg.on.on`; output emits state updates. |
|
|
24
|
+
| Node Input/Output PINs | Shows Node-RED input/output pins. Input accepts boolean payloads and Matter-style `msg.payload` or `msg.on.on`; output emits state updates. The selection is preserved when the editor is reopened. |
|
|
25
25
|
|
|
26
26
|
## Behaviour
|
|
27
27
|
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"get_current": "Get current",
|
|
36
36
|
"get_again": "Get again",
|
|
37
37
|
"wait": "Wait...",
|
|
38
|
-
"locate_no_bridge": "Select a
|
|
39
|
-
"locate_no_device": "Select a
|
|
38
|
+
"locate_no_bridge": "Select a Matter controller first",
|
|
39
|
+
"locate_no_device": "Select a Matter device first",
|
|
40
40
|
"locate_success": "Locate command sent",
|
|
41
41
|
"locate_started": "Locate mode started. Press again to stop (auto-stops after 10 minutes).",
|
|
42
42
|
"locate_stopped": "Locate mode stopped.",
|
|
43
|
-
"locate_start_title": "Locate selected
|
|
43
|
+
"locate_start_title": "Locate selected Matter device",
|
|
44
44
|
"locate_stop_title": "Stop locate mode",
|
|
45
|
-
"locate_error": "Unable to locate
|
|
45
|
+
"locate_error": "Unable to locate Matter device",
|
|
46
46
|
"day_night": "Day/Night",
|
|
47
47
|
"invert_day_night": "Invert day/night value",
|
|
48
48
|
"override_night_mode": "Force day mode",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"opt_no": "No",
|
|
57
57
|
"opt_yes_emit": "Yes, and emit KNX telegrams.",
|
|
58
58
|
"knx_brightness_status": "KNX Brightness Status",
|
|
59
|
-
"knx_brightness_onhueoff": "When
|
|
60
|
-
"knx_brightness_no": "Leave as is (default
|
|
61
|
-
"update_local_state_from_knx_write": "Update local cached
|
|
62
|
-
"update_local_state_from_knx_write_hint": "Enabled: faster local reactions and consistent immediate KNX read responses. Disabled: keep the cache aligned only with real
|
|
63
|
-
"use_min_brightness": "Use minimum brightness specified
|
|
59
|
+
"knx_brightness_onhueoff": "When the Matter light is Off send 0%. When it is On, restore the previous value (default KNX behaviour)",
|
|
60
|
+
"knx_brightness_no": "Leave as is (default Matter behaviour)",
|
|
61
|
+
"update_local_state_from_knx_write": "Update local cached Matter state from KNX bus writes",
|
|
62
|
+
"update_local_state_from_knx_write_hint": "Enabled: faster local reactions and consistent immediate KNX read responses. Disabled: keep the cache aligned only with real Matter reports.",
|
|
63
|
+
"use_min_brightness": "Use minimum brightness specified for the Matter light",
|
|
64
64
|
"k_suffix": "K",
|
|
65
65
|
"temp_desc_2200": "(start of Philips White Ambiance lights range)",
|
|
66
66
|
"temp_desc_2700": "(warm white, intimate, cozy, personal, for living rooms)",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
"effect_status": "Effect status",
|
|
84
84
|
"effect_mapping": "Mappings",
|
|
85
85
|
"effect_autofill": "Fill with available effects",
|
|
86
|
-
"effect_tip": "Provide KNX value /
|
|
87
|
-
"effect_tip_status": "If a status group address is configured, the current
|
|
88
|
-
"effect_not_supported": "This light does not expose
|
|
86
|
+
"effect_tip": "Provide KNX value / Matter effect pairs. When the incoming KNX payload matches the value, the selected effect is applied to the light.",
|
|
87
|
+
"effect_tip_status": "If a status group address is configured, the current Matter effect is emitted using the mapped KNX value or the effect name.",
|
|
88
|
+
"effect_not_supported": "This light does not expose Matter effects.",
|
|
89
89
|
"effect_knx_value_placeholder": "Value to match",
|
|
90
|
-
"effect_base_label": "
|
|
91
|
-
"effect_native_label": "
|
|
90
|
+
"effect_base_label": "Basic Matter effects",
|
|
91
|
+
"effect_native_label": "Native Matter effects",
|
|
92
92
|
"matter_controller": "Matter controller",
|
|
93
93
|
"matter_device": "Matter device",
|
|
94
94
|
"device_type": "Device type",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"name": "Name",
|
|
101
101
|
"youtube_sample": "Youtube sample",
|
|
102
102
|
"knx_gw": "KNX GW",
|
|
103
|
-
"hue_bridge": "
|
|
104
|
-
"philips_hue": "
|
|
103
|
+
"hue_bridge": "Matter controller",
|
|
104
|
+
"philips_hue": "Matter",
|
|
105
105
|
"read": "Read"
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -21,7 +21,7 @@ Sustituye a los nodos Matter separados no publicados y conserva toda la UI de lu
|
|
|
21
21
|
| Sensores | Los endpoints de sensor muestran su GA de medida/estado solo cuando está soportado: temperatura, humedad, iluminancia, ocupación, contacto y batería. |
|
|
22
22
|
| Read at startup | Publica el valor Matter en caché al desplegar/iniciar o cuando el dispositivo se reconecta. |
|
|
23
23
|
| Update local state from KNX write | Actualiza la caché local Matter/KNX cuando se escribe un telegrama en una GA KNX configurada. |
|
|
24
|
-
| Node Input/Output PINs | Muestra pines de entrada/salida Node-RED. La entrada acepta payloads booleanos y mensajes Matter en `msg.payload` o `msg.on.on`; la salida emite estados. |
|
|
24
|
+
| Node Input/Output PINs | Muestra pines de entrada/salida Node-RED. La entrada acepta payloads booleanos y mensajes Matter en `msg.payload` o `msg.on.on`; la salida emite estados. La selección se conserva al volver a abrir el editor. |
|
|
25
25
|
|
|
26
26
|
## Comportamiento
|
|
27
27
|
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"get_current": "Obtener",
|
|
36
36
|
"get_again": "Volver a",
|
|
37
37
|
"wait": "Esperar...",
|
|
38
|
-
"locate_no_bridge": "Seleccione primero un
|
|
39
|
-
"locate_no_device": "Seleccione primero un dispositivo
|
|
38
|
+
"locate_no_bridge": "Seleccione primero un controlador Matter",
|
|
39
|
+
"locate_no_device": "Seleccione primero un dispositivo Matter",
|
|
40
40
|
"locate_success": "Comando de localización enviado",
|
|
41
41
|
"locate_started": "Modo de localización iniciado. Pulsa de nuevo para detenerlo (se detiene automáticamente tras 10 minutos).",
|
|
42
42
|
"locate_stopped": "Modo de localización detenido.",
|
|
43
|
-
"locate_start_title": "Localizar el dispositivo
|
|
43
|
+
"locate_start_title": "Localizar el dispositivo Matter seleccionado",
|
|
44
44
|
"locate_stop_title": "Detener el modo de localización",
|
|
45
|
-
"locate_error": "No se pudo localizar el dispositivo
|
|
45
|
+
"locate_error": "No se pudo localizar el dispositivo Matter",
|
|
46
46
|
"day_night": "Día/noche",
|
|
47
47
|
"invert_day_night": "Invertir el valor de día/noche",
|
|
48
48
|
"override_night_mode": "Forzar el modo diurno",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"knx_brightness_status": "Estado de brillo KNX",
|
|
59
59
|
"knx_brightness_onhueoff": "Cuando la luz del tono está apagada, envíe 0%. Cuando se enciende, restaure el valor anterior (comportamiento de KNX predeterminado)",
|
|
60
60
|
"knx_brightness_no": "Salir como está (comportamiento de tono predeterminado)",
|
|
61
|
-
"update_local_state_from_knx_write": "Actualizar el estado local en caché de
|
|
62
|
-
"update_local_state_from_knx_write_hint": "Activado: reacciones locales
|
|
61
|
+
"update_local_state_from_knx_write": "Actualizar el estado local en caché de Matter a partir de escrituras del bus KNX",
|
|
62
|
+
"update_local_state_from_knx_write_hint": "Activado: reacciones locales más rápidas y respuestas inmediatas de lectura KNX más coherentes. Desactivado: la caché se actualiza solo con informes Matter reales.",
|
|
63
63
|
"use_min_brightness": "Use brillo mínimo especificado en la luz del tono",
|
|
64
64
|
"k_suffix": "K",
|
|
65
65
|
"temp_desc_2200": "(Inicio de Philips White Ambiance Lights Range)",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"effect_status": "Estado del efecto",
|
|
84
84
|
"effect_mapping": "Mapeos",
|
|
85
85
|
"effect_autofill": "Llenar con los efectos disponibles",
|
|
86
|
-
"effect_tip": "Proporcione pares de valor KNX /
|
|
86
|
+
"effect_tip": "Proporcione pares de valor KNX / efecto Matter. Cuando la carga útil de KNX entrante coincide con el valor, el efecto seleccionado se aplica a la luz.",
|
|
87
87
|
"effect_tip_status": "Si se configura una dirección de grupo de estado, el efecto de tono actual se emite utilizando el valor KNX asignado o el nombre del efecto.",
|
|
88
88
|
"effect_not_supported": "Esta luz no expone los efectos del tono.",
|
|
89
89
|
"effect_knx_value_placeholder": "Valor para coincidir",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"name": "Nombre",
|
|
101
101
|
"youtube_sample": "Muestra de youtube",
|
|
102
102
|
"knx_gw": "KNX GW",
|
|
103
|
-
"hue_bridge": "
|
|
104
|
-
"philips_hue": "
|
|
103
|
+
"hue_bridge": "Controlador Matter",
|
|
104
|
+
"philips_hue": "Matter",
|
|
105
105
|
"read": "Leer"
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -21,7 +21,7 @@ Il remplace les nœuds Matter séparés non publiés et conserve toute l'UI lumi
|
|
|
21
21
|
| Capteurs | Les endpoints capteur affichent leur GA de mesure/état uniquement si elle est supportée : température, humidité, éclairement, occupation, contact et batterie. |
|
|
22
22
|
| Read at startup | Publie la valeur Matter en cache au déploiement/démarrage ou quand le périphérique se reconnecte. |
|
|
23
23
|
| Update local state from KNX write | Met à jour le cache local Matter/KNX lorsqu'un télégramme est écrit sur une GA KNX configurée. |
|
|
24
|
-
| Node Input/Output PINs | Affiche les pins entrée/sortie Node-RED. L'entrée accepte les payloads booléens et les messages Matter dans `msg.payload` ou `msg.on.on`; la sortie émet les états. |
|
|
24
|
+
| Node Input/Output PINs | Affiche les pins entrée/sortie Node-RED. L'entrée accepte les payloads booléens et les messages Matter dans `msg.payload` ou `msg.on.on`; la sortie émet les états. La sélection est conservée à la réouverture de l'éditeur. |
|
|
25
25
|
|
|
26
26
|
## Comportement
|
|
27
27
|
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"opt_no": "Non",
|
|
49
49
|
"opt_yes_emit": "Oui, et émettez des télégrammes KNX.",
|
|
50
50
|
"knx_brightness_status": "Statut de luminosité de KNX",
|
|
51
|
-
"knx_brightness_onhueoff": "Lorsque
|
|
51
|
+
"knx_brightness_onhueoff": "Lorsque la lumière Matter est éteinte, envoyer 0 %. Lorsqu'elle est allumée, restaurer la valeur précédente (comportement KNX par défaut)",
|
|
52
52
|
"knx_brightness_no": "Laisser tel quel (comportement de teinte par défaut)",
|
|
53
|
-
"update_local_state_from_knx_write": "Mettre à jour l'état
|
|
54
|
-
"update_local_state_from_knx_write_hint": "
|
|
53
|
+
"update_local_state_from_knx_write": "Mettre à jour l'état Matter local en cache à partir des écritures du bus KNX",
|
|
54
|
+
"update_local_state_from_knx_write_hint": "Activé : réactions locales plus rapides et réponses immédiates de lecture KNX plus cohérentes. Désactivé : le cache ne suit que les rapports Matter réels.",
|
|
55
55
|
"use_min_brightness": "Utilisez une luminosité minimale spécifiée dans la lumière des teintes",
|
|
56
56
|
"k_suffix": "K",
|
|
57
57
|
"temp_desc_2200": "(Début de la gamme Philips White Ambiance Lights)",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"effect_status": "État de l'effet",
|
|
76
76
|
"effect_mapping": "Mappages",
|
|
77
77
|
"effect_autofill": "Remplissez les effets disponibles",
|
|
78
|
-
"effect_tip": "Fournir des paires
|
|
78
|
+
"effect_tip": "Fournir des paires valeur KNX / effet Matter. Lorsque la charge utile KNX entrante correspond à la valeur, l'effet sélectionné est appliqué à la lumière.",
|
|
79
79
|
"effect_tip_status": "Si une adresse de groupe d'état est configurée, l'effet de teinte actuel est émis en utilisant la valeur KNX mappée ou le nom d'effet.",
|
|
80
80
|
"effect_not_supported": "Cette lumière n'expose pas les effets de la teinte.",
|
|
81
81
|
"effect_knx_value_placeholder": "Valeur à correspondre",
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
"name": "Nom",
|
|
93
93
|
"youtube_sample": "Échantillon YouTube",
|
|
94
94
|
"knx_gw": "KNX GW",
|
|
95
|
-
"hue_bridge": "
|
|
96
|
-
"philips_hue": "
|
|
95
|
+
"hue_bridge": "Contrôleur Matter",
|
|
96
|
+
"philips_hue": "Matter",
|
|
97
97
|
"read": "Lire"
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -21,7 +21,7 @@ Sostituisce i nodi Matter separati non pubblicati e mantiene tutta la UI luce qu
|
|
|
21
21
|
| Sensori | Gli endpoint sensore mostrano il relativo GA di misura/stato solo quando supportato: temperatura, umidità, illuminamento, presenza, contatto e batteria. |
|
|
22
22
|
| Read at startup | Pubblica il valore Matter in cache al deploy/avvio o quando il dispositivo si riconnette. |
|
|
23
23
|
| Update local state from KNX write | Aggiorna la cache locale Matter/KNX quando arriva una scrittura su un GA KNX configurato. |
|
|
24
|
-
| Node Input/Output PINs | Mostra i pin input/output Node-RED. L'input accetta payload booleani e messaggi stile Matter in `msg.payload` o `msg.on.on`; l'output emette gli aggiornamenti di stato. |
|
|
24
|
+
| Node Input/Output PINs | Mostra i pin input/output Node-RED. L'input accetta payload booleani e messaggi stile Matter in `msg.payload` o `msg.on.on`; l'output emette gli aggiornamenti di stato. La selezione viene mantenuta alla riapertura dell'editor. |
|
|
25
25
|
|
|
26
26
|
## Comportamento
|
|
27
27
|
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"get_current": "Ottieni corrente",
|
|
36
36
|
"get_again": "Ottieni di nuovo",
|
|
37
37
|
"wait": "Attendere...",
|
|
38
|
-
"locate_no_bridge": "Seleziona prima un
|
|
39
|
-
"locate_no_device": "Seleziona prima un dispositivo
|
|
38
|
+
"locate_no_bridge": "Seleziona prima un controller Matter",
|
|
39
|
+
"locate_no_device": "Seleziona prima un dispositivo Matter",
|
|
40
40
|
"locate_success": "Comando di localizzazione inviato",
|
|
41
41
|
"locate_started": "Modalità di localizzazione avviata. Premi di nuovo per fermarla (si ferma automaticamente dopo 10 minuti).",
|
|
42
42
|
"locate_stopped": "Modalità di localizzazione terminata.",
|
|
43
|
-
"locate_start_title": "Localizza il dispositivo
|
|
43
|
+
"locate_start_title": "Localizza il dispositivo Matter selezionato",
|
|
44
44
|
"locate_stop_title": "Ferma la modalità di localizzazione",
|
|
45
|
-
"locate_error": "Impossibile localizzare il dispositivo
|
|
45
|
+
"locate_error": "Impossibile localizzare il dispositivo Matter",
|
|
46
46
|
"day_night": "Giorno/Notte",
|
|
47
47
|
"invert_day_night": "Inverti valore giorno/notte",
|
|
48
48
|
"override_night_mode": "Forza modalità diurna",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"opt_no": "No",
|
|
57
57
|
"opt_yes_emit": "Sì, ed emetti i telegrammi KNX.",
|
|
58
58
|
"knx_brightness_status": "Stato luminosità KNX",
|
|
59
|
-
"knx_brightness_onhueoff": "Se la luce
|
|
60
|
-
"knx_brightness_no": "Lascia invariato (comportamento
|
|
61
|
-
"update_local_state_from_knx_write": "Aggiorna lo stato
|
|
62
|
-
"update_local_state_from_knx_write_hint": "Abilitato: reazioni locali
|
|
63
|
-
"use_min_brightness": "Usa la luminosità minima specificata
|
|
59
|
+
"knx_brightness_onhueoff": "Se la luce Matter è spenta invia 0%. Se è accesa, ripristina il valore precedente (comportamento KNX predefinito)",
|
|
60
|
+
"knx_brightness_no": "Lascia invariato (comportamento Matter predefinito)",
|
|
61
|
+
"update_local_state_from_knx_write": "Aggiorna lo stato Matter locale in cache dai write provenienti dal bus KNX",
|
|
62
|
+
"update_local_state_from_knx_write_hint": "Abilitato: reazioni locali più rapide e risposte immediate ai read KNX più coerenti. Disabilitato: la cache si aggiorna solo dai report Matter reali.",
|
|
63
|
+
"use_min_brightness": "Usa la luminosità minima specificata per la luce Matter",
|
|
64
64
|
"k_suffix": "K",
|
|
65
65
|
"temp_desc_2200": "(inizio della gamma Philips White Ambiance)",
|
|
66
66
|
"temp_desc_2700": "(bianco caldo, intimo, accogliente, personale, per soggiorni)",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
"effect_status": "Stato effetti",
|
|
84
84
|
"effect_mapping": "Associazioni",
|
|
85
85
|
"effect_autofill": "Compila con gli effetti disponibili",
|
|
86
|
-
"effect_tip": "Definisci le coppie valore KNX / effetto
|
|
86
|
+
"effect_tip": "Definisci le coppie valore KNX / effetto Matter. Quando il valore KNX ricevuto coincide, viene attivato l'effetto selezionato.",
|
|
87
87
|
"effect_tip_status": "Se configuri lo stato, il nodo invierà su KNX l'effetto corrente (valore associato o nome).",
|
|
88
|
-
"effect_not_supported": "Questa lampada non espone effetti
|
|
88
|
+
"effect_not_supported": "Questa lampada non espone effetti Matter.",
|
|
89
89
|
"effect_knx_value_placeholder": "Valore da confrontare",
|
|
90
|
-
"effect_base_label": "Effetti base
|
|
91
|
-
"effect_native_label": "Effetti nativi
|
|
90
|
+
"effect_base_label": "Effetti base Matter",
|
|
91
|
+
"effect_native_label": "Effetti nativi Matter",
|
|
92
92
|
"matter_controller": "Controller Matter",
|
|
93
93
|
"matter_device": "Dispositivo Matter",
|
|
94
94
|
"device_type": "Tipo dispositivo",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"name": "Nome",
|
|
101
101
|
"youtube_sample": "Esempio YouTube",
|
|
102
102
|
"knx_gw": "Gateway KNX",
|
|
103
|
-
"hue_bridge": "
|
|
104
|
-
"philips_hue": "
|
|
103
|
+
"hue_bridge": "Controller Matter",
|
|
104
|
+
"philips_hue": "Matter",
|
|
105
105
|
"read": "Leggi"
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| 传感器 | 传感器 endpoint 只在支持时显示对应测量/状态 GA:温度、湿度、照度、占用、接触和电池。 |
|
|
22
22
|
| Read at startup | 在部署/启动或设备重新连接时发布缓存的 Matter 值。 |
|
|
23
23
|
| Update local state from KNX write | 当配置的 KNX GA 收到写入 telegram 时,更新本地 Matter/KNX 缓存。 |
|
|
24
|
-
| Node Input/Output PINs | 显示 Node-RED 输入/输出端口。输入接受布尔 payload,以及 `msg.payload` 或 `msg.on.on` 中的 Matter
|
|
24
|
+
| Node Input/Output PINs | 显示 Node-RED 输入/输出端口。输入接受布尔 payload,以及 `msg.payload` 或 `msg.on.on` 中的 Matter 风格消息;输出发送状态更新。重新打开编辑器时会保留此选择。 |
|
|
25
25
|
|
|
26
26
|
## 行为
|
|
27
27
|
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"opt_no": "否",
|
|
49
49
|
"opt_yes_emit": "是,并发送 KNX 电报。",
|
|
50
50
|
"knx_brightness_status": "KNX 亮度状态",
|
|
51
|
-
"knx_brightness_onhueoff": "
|
|
52
|
-
"knx_brightness_no": "保持不变(默认
|
|
53
|
-
"update_local_state_from_knx_write": "根据 KNX 总线写入更新本地缓存的
|
|
54
|
-
"update_local_state_from_knx_write_hint": "启用:本地响应更快,KNX
|
|
55
|
-
"use_min_brightness": "使用
|
|
51
|
+
"knx_brightness_onhueoff": "Matter 灯关闭时发送 0%。Matter 灯打开时恢复先前值(默认 KNX 行为)",
|
|
52
|
+
"knx_brightness_no": "保持不变(默认 Matter 行为)",
|
|
53
|
+
"update_local_state_from_knx_write": "根据 KNX 总线写入更新本地缓存的 Matter 状态",
|
|
54
|
+
"update_local_state_from_knx_write_hint": "启用:本地响应更快,KNX 即时读回更一致。禁用:缓存只根据真实的 Matter 报告更新。",
|
|
55
|
+
"use_min_brightness": "使用 Matter 灯中设置的最小亮度",
|
|
56
56
|
"k_suffix": "K",
|
|
57
57
|
"temp_desc_2200": "(飞利浦 White Ambiance 系列起始)",
|
|
58
58
|
"temp_desc_2700": "(暖白,温馨、舒适、私密,适合客厅)",
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
"effect_status": "效果状态",
|
|
76
76
|
"effect_mapping": "映射",
|
|
77
77
|
"effect_autofill": "填充可用效果",
|
|
78
|
-
"effect_tip": "配置 KNX 数值与
|
|
78
|
+
"effect_tip": "配置 KNX 数值与 Matter 效果对,收到匹配的 KNX 数值时触发对应效果。",
|
|
79
79
|
"effect_tip_status": "若配置状态组地址,将把当前效果(映射值或效果名称)发送到 KNX。",
|
|
80
|
-
"effect_not_supported": "该灯具不支持
|
|
80
|
+
"effect_not_supported": "该灯具不支持 Matter 效果。",
|
|
81
81
|
"effect_knx_value_placeholder": "匹配值",
|
|
82
|
-
"effect_base_label": "
|
|
83
|
-
"effect_native_label": "
|
|
82
|
+
"effect_base_label": "Matter 基础效果",
|
|
83
|
+
"effect_native_label": "Matter 原生效果",
|
|
84
84
|
"matter_controller": "Matter 控制器",
|
|
85
85
|
"matter_device": "Matter 设备",
|
|
86
86
|
"device_type": "设备类型",
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
"name": "名称",
|
|
93
93
|
"youtube_sample": "YouTube 示例",
|
|
94
94
|
"knx_gw": "KNX 网关",
|
|
95
|
-
"hue_bridge": "
|
|
96
|
-
"philips_hue": "
|
|
95
|
+
"hue_bridge": "Matter 控制器",
|
|
96
|
+
"philips_hue": "Matter",
|
|
97
97
|
"read": "读取"
|
|
98
98
|
}
|
|
99
99
|
}
|
package/nodes/matter-config.js
CHANGED
|
@@ -31,7 +31,8 @@ module.exports = (RED) => {
|
|
|
31
31
|
node.matterInstanceId = `knxultimate-matter-${node.id.replace(/[^a-zA-Z0-9]/g, '')}`
|
|
32
32
|
node.matterStoragePath = path.join(RED.settings.userDir || '.', 'knxultimatestorage', 'matter')
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Expose controller connectivity without copying engine state into the config node.
|
|
35
|
+
// Consumers read the latest value lazily, including during asynchronous startup.
|
|
35
36
|
Object.defineProperty(node, 'linkStatus', {
|
|
36
37
|
get: function () {
|
|
37
38
|
return node.matterManager?.matterConnectionStatus ?? 'disconnected'
|
|
@@ -39,6 +40,8 @@ module.exports = (RED) => {
|
|
|
39
40
|
})
|
|
40
41
|
|
|
41
42
|
const safeClientCall = (client, fn, label) => {
|
|
43
|
+
// One faulty device node must never interrupt event delivery to the remaining
|
|
44
|
+
// Matter clients registered on this shared controller connection.
|
|
42
45
|
try {
|
|
43
46
|
if (!client || typeof fn !== 'function') return
|
|
44
47
|
fn()
|
|
@@ -28,12 +28,16 @@ const lockStateName = (value) => {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const lockStateToBoolean = (value) => {
|
|
31
|
+
// KNX DPT 1 can represent only two states. Do not collapse transitional or
|
|
32
|
+
// ambiguous Matter lock states into a potentially unsafe locked/unlocked value.
|
|
31
33
|
if (Number(value) === LOCK_STATE.LOCKED) return true
|
|
32
34
|
if (Number(value) === LOCK_STATE.UNLOCKED) return false
|
|
33
35
|
return undefined
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const setupDoorLockProfile = (RED, node, config) => {
|
|
39
|
+
// Profiles initialize the same flags consumed by knxUltimate-config as ordinary
|
|
40
|
+
// KNX nodes. In particular, listenallga must remain enabled for handleSend calls.
|
|
37
41
|
node.name = config.name || node.matterDeviceName || 'Control Matter door lock from KNX'
|
|
38
42
|
node.topic = node.name
|
|
39
43
|
node.notifyreadrequest = true
|
|
@@ -53,8 +57,14 @@ const setupDoorLockProfile = (RED, node, config) => {
|
|
|
53
57
|
.filter((ga) => ga !== '')
|
|
54
58
|
|
|
55
59
|
const setStatus = (fill, shape, text) => node.status({ fill, shape, text })
|
|
60
|
+
// knxUltimate-config invokes this synchronously from addClient(). Profiles return
|
|
61
|
+
// early from the main light constructor, so they must expose the callback before
|
|
62
|
+
// registering with the shared KNX configuration node.
|
|
63
|
+
node.setNodeStatus = ({ fill = 'grey', shape = 'ring', text = '' } = {}) => setStatus(fill, shape, text)
|
|
56
64
|
|
|
57
65
|
const commandArgs = () => {
|
|
66
|
+
// Matter represents the remote credential as bytes. Keep the PIN in Node-RED's
|
|
67
|
+
// credential store and materialize the Buffer only for the outgoing command.
|
|
58
68
|
const pin = String(node.credentials?.doorLockPin || '')
|
|
59
69
|
return pin === '' ? {} : { pinCode: Buffer.from(pin, 'utf8') }
|
|
60
70
|
}
|
|
@@ -91,6 +101,8 @@ const setupDoorLockProfile = (RED, node, config) => {
|
|
|
91
101
|
node.currentLockState = Number(rawState)
|
|
92
102
|
const state = lockStateToBoolean(rawState)
|
|
93
103
|
const name = lockStateName(rawState)
|
|
104
|
+
// Attribute reports update KNX state only; they never enqueue a Matter command.
|
|
105
|
+
// This one-way path is the primary feedback-loop guard for Door Lock endpoints.
|
|
94
106
|
if (state !== undefined) writeKnxState(state)
|
|
95
107
|
sendFlow(source, state, rawState)
|
|
96
108
|
setStatus(state === undefined ? 'yellow' : 'blue', state === undefined ? 'ring' : 'dot', `Matter: ${name}`)
|
|
@@ -102,6 +114,8 @@ const setupDoorLockProfile = (RED, node, config) => {
|
|
|
102
114
|
const capabilities = (() => {
|
|
103
115
|
try { return JSON.parse(config.matterDeviceCapabilities || '{}') } catch (error) { return {} }
|
|
104
116
|
})()
|
|
117
|
+
// Never invent optional operations: the editor persists the commands actually
|
|
118
|
+
// advertised by this endpoint and runtime validation enforces that snapshot.
|
|
105
119
|
if (locked && capabilities.lockDoor === false) throw new Error('The Matter endpoint does not expose lockDoor')
|
|
106
120
|
if (!locked && capabilities.unlockDoor === false) throw new Error('The Matter endpoint does not expose unlockDoor')
|
|
107
121
|
const queued = manager.writeMatterQueueAdd({
|
|
@@ -153,6 +167,8 @@ const setupDoorLockProfile = (RED, node, config) => {
|
|
|
153
167
|
node.handleMatterClusterEvent = () => {}
|
|
154
168
|
node.handleMatterNodeInitialized = () => {
|
|
155
169
|
try {
|
|
170
|
+
// The controller engine already caches the initial attribute read. Reusing that
|
|
171
|
+
// value avoids an extra request and publishes startup state through the same path.
|
|
156
172
|
const value = node.serverMatter?.matterManager?.getCachedAttribute(
|
|
157
173
|
node.matterNodeId,
|
|
158
174
|
node.matterEndpointId,
|
|
@@ -13,7 +13,10 @@ const PROFILE_SETUPS = Object.freeze({
|
|
|
13
13
|
|
|
14
14
|
const setupMatterControllerProfile = (profile, RED, node, config) => {
|
|
15
15
|
const setup = PROFILE_SETUPS[profile]
|
|
16
|
+
// Returning false is significant: the caller must continue through the unchanged
|
|
17
|
+
// Matter-light implementation when no specialized profile claims the endpoint.
|
|
16
18
|
if (typeof setup !== 'function') return false
|
|
19
|
+
// A profile that returns successfully owns input, close, KNX and Matter handlers.
|
|
17
20
|
setup(RED, node, config)
|
|
18
21
|
return true
|
|
19
22
|
}
|
|
@@ -13,6 +13,8 @@ const isValidGroupAddress = (value) => {
|
|
|
13
13
|
const parseMappings = (value) => {
|
|
14
14
|
try {
|
|
15
15
|
const parsed = Array.isArray(value) ? value : JSON.parse(value || '[]')
|
|
16
|
+
// Ignore incomplete or stale editor rows. Only mappings with a valid KNX address,
|
|
17
|
+
// resolvable DPT and explicit Matter target are allowed into runtime routing.
|
|
16
18
|
return parsed.filter((mapping) => {
|
|
17
19
|
if (!mapping || !isValidGroupAddress(mapping.ga) || !mapping.target || !mapping.dpt) return false
|
|
18
20
|
try { dptlib.resolve(mapping.dpt); return true } catch (error) { return false }
|
|
@@ -42,6 +44,8 @@ const setupMappedEndpointProfile = (RED, node, config) => {
|
|
|
42
44
|
let lastInitialReadTs = 0
|
|
43
45
|
|
|
44
46
|
const status = (fill, shape, text) => node.status({ fill, shape, text })
|
|
47
|
+
// knxUltimate-config calls setNodeStatus synchronously while adding the client.
|
|
48
|
+
node.setNodeStatus = ({ fill = 'grey', shape = 'ring', text = '' } = {}) => status(fill, shape, text)
|
|
45
49
|
const manager = () => node.serverMatter?.matterManager
|
|
46
50
|
const sendKnx = (mapping, payload, outputtype = 'write') => {
|
|
47
51
|
if (payload === undefined || !node.serverKNX) return false
|
|
@@ -57,12 +61,16 @@ const setupMappedEndpointProfile = (RED, node, config) => {
|
|
|
57
61
|
const sendCached = (mapping, outputtype) => {
|
|
58
62
|
const currentManager = manager()
|
|
59
63
|
if (!currentManager) return false
|
|
64
|
+
// Cached reports are authoritative for KNX read responses and startup publication;
|
|
65
|
+
// reading them must not generate a new Matter write or command.
|
|
60
66
|
const value = currentManager.getCachedAttribute(node.matterNodeId, mapping.endpointId, mapping.clusterId, mapping.target)
|
|
61
67
|
return sendKnx(mapping, matterToKnx(mapping.clusterId, mapping.target, value), outputtype)
|
|
62
68
|
}
|
|
63
69
|
const enqueue = (mapping, value) => {
|
|
64
70
|
const currentManager = manager()
|
|
65
71
|
if (!currentManager) throw new Error('Matter controller not ready')
|
|
72
|
+
// Conversion happens before queueing so the Matter manager receives a canonical,
|
|
73
|
+
// cluster-specific command or attribute write rather than a raw KNX payload.
|
|
66
74
|
const action = knxToMatter(mapping, value)
|
|
67
75
|
if (!action) return
|
|
68
76
|
const queued = currentManager.writeMatterQueueAdd({
|
|
@@ -96,6 +104,8 @@ const setupMappedEndpointProfile = (RED, node, config) => {
|
|
|
96
104
|
}
|
|
97
105
|
node.handleSendMatter = (event) => {
|
|
98
106
|
try {
|
|
107
|
+
// Attribute reports travel only toward KNX/flow. Keeping feedback separate from
|
|
108
|
+
// enqueue() prevents programmatic state synchronization from echoing to Matter.
|
|
99
109
|
if (String(event?.nodeId) !== String(node.matterNodeId) || Number(event?.endpointId) !== Number(node.matterEndpointId)) return
|
|
100
110
|
node.mappings.filter((mapping) => mapping.direction === 'status' && Number(mapping.clusterId) === Number(event.clusterId) && mapping.target === event.attributeName).forEach((mapping) => {
|
|
101
111
|
sendKnx(mapping, matterToKnx(event.clusterId, event.attributeName, event.value))
|
|
@@ -108,11 +118,15 @@ const setupMappedEndpointProfile = (RED, node, config) => {
|
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
node.handleMatterClusterEvent = (event) => {
|
|
121
|
+
// Cluster events have no implicit KNX mapping. They are exposed as raw flow output
|
|
122
|
+
// only when the user explicitly enables the node pins.
|
|
111
123
|
if (enablePins && String(event?.nodeId) === String(node.matterNodeId) && Number(event?.endpointId) === Number(node.matterEndpointId)) {
|
|
112
124
|
node.send({ topic: `${event.clusterId}.${event.eventName}`, payload: event.events, matter: event })
|
|
113
125
|
}
|
|
114
126
|
}
|
|
115
127
|
node.handleMatterNodeInitialized = () => {
|
|
128
|
+
// nodeInitialized can be emitted more than once while sessions recover. Bound the
|
|
129
|
+
// startup publication to avoid duplicate KNX telegram bursts during reconnects.
|
|
116
130
|
if (config.readStatusAtStartup === 'no' || Date.now() - lastInitialReadTs < 5000) return
|
|
117
131
|
let sent = 0
|
|
118
132
|
node.mappings.filter((mapping) => mapping.direction === 'status').forEach((mapping) => { if (sendCached(mapping, 'write')) sent += 1 })
|
|
@@ -443,6 +443,15 @@ class classMatter extends EventEmitter {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
_findEndpoint = (node, _endpointId) => {
|
|
446
|
+
// getDevices() can expose only functional/bridged endpoints while the root endpoint
|
|
447
|
+
// lives behind getRootEndpoint(). Resolve endpoint 0 explicitly for node clusters
|
|
448
|
+
// such as BasicInformation and GeneralCommissioning.
|
|
449
|
+
if (Number(_endpointId) === 0 && typeof node?.getRootEndpoint === 'function') {
|
|
450
|
+
try {
|
|
451
|
+
const root = node.getRootEndpoint()
|
|
452
|
+
if (root !== undefined) return root
|
|
453
|
+
} catch (error) { /* The legacy endpoint structure may still be initializing. */ }
|
|
454
|
+
}
|
|
446
455
|
return this._getAllEndpoints(node).find((ep) => Number(ep.number) === Number(_endpointId))
|
|
447
456
|
}
|
|
448
457
|
|
|
@@ -564,12 +573,21 @@ class classMatter extends EventEmitter {
|
|
|
564
573
|
if (label.length > 64) throw new Error('Matter device name is too long')
|
|
565
574
|
const node = this.pairedNodes.get(_nodeIdString)
|
|
566
575
|
if (node === undefined) throw new Error(`Matter node ${_nodeIdString} unknown or not yet connected`)
|
|
576
|
+
// commission() attaches the PairedNode before its first wildcard read/subscription
|
|
577
|
+
// has necessarily built the legacy endpoint structure. A user-supplied name is
|
|
578
|
+
// applied immediately after commissioning, so wait briefly for BasicInformation
|
|
579
|
+
// instead of reporting a misleading permanent-cluster warning.
|
|
580
|
+
const deadline = Date.now() + 10000
|
|
567
581
|
let clusterClient
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
582
|
+
do {
|
|
583
|
+
try {
|
|
584
|
+
if (typeof node.getRootClusterClient === 'function') clusterClient = node.getRootClusterClient(this._api.BasicInformation)
|
|
585
|
+
} catch (error) { /* Endpoint structure is still initializing. */ }
|
|
586
|
+
if (clusterClient === undefined) clusterClient = this._findClusterClient(node, 0, 40) // BasicInformation on the root endpoint
|
|
587
|
+
if (clusterClient !== undefined) break
|
|
588
|
+
await pleaseWait(100)
|
|
589
|
+
} while (Date.now() < deadline)
|
|
590
|
+
if (clusterClient === undefined) throw new Error(`BasicInformation cluster was not available on node ${_nodeIdString} after initialization`)
|
|
573
591
|
const attribute = clusterClient.attributes.nodeLabel
|
|
574
592
|
if (attribute === undefined) throw new Error(`nodeLabel attribute not found on node ${_nodeIdString}`)
|
|
575
593
|
await attribute.set(label)
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=20.18.1"
|
|
5
5
|
},
|
|
6
|
-
"version": "6.0.
|
|
6
|
+
"version": "6.0.9",
|
|
7
7
|
"description": "Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, KNX AI for diagnosticsand KNX routing between interfaces. Easy to use and highly configurable.",
|
|
8
8
|
"files": [
|
|
9
9
|
"nodes/",
|
|
@@ -145,4 +145,4 @@
|
|
|
145
145
|
"vite": "^7.1.3",
|
|
146
146
|
"vue": "^3.5.21"
|
|
147
147
|
}
|
|
148
|
-
}
|
|
148
|
+
}
|