node-red-contrib-knx-ultimate 6.2.3-beta.4 → 6.3.0
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 +11 -0
- package/nodes/hue-config.js +13 -0
- package/nodes/knxUltimate.html +52 -58
- package/nodes/knxUltimateHueController.html +246 -35
- package/nodes/knxUltimateMatterControllerDevice.html +15 -3
- package/nodes/locales/de/knxUltimate.html +1 -1
- package/nodes/locales/de/knxUltimateHueController.html +3 -2
- package/nodes/locales/de/knxUltimateHueController.json +7 -0
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/locales/en/knxUltimate.html +1 -1
- package/nodes/locales/en/knxUltimateHueController.html +3 -2
- package/nodes/locales/en/knxUltimateHueController.json +7 -0
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/locales/es/knxUltimate.html +1 -1
- package/nodes/locales/es/knxUltimateHueController.html +3 -2
- package/nodes/locales/es/knxUltimateHueController.json +7 -0
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/locales/fr/knxUltimate.html +1 -1
- package/nodes/locales/fr/knxUltimateHueController.html +3 -2
- package/nodes/locales/fr/knxUltimateHueController.json +7 -0
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/locales/it/knxUltimate.html +1 -1
- package/nodes/locales/it/knxUltimateHueController.html +3 -2
- package/nodes/locales/it/knxUltimateHueController.json +7 -0
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/locales/zh-CN/knxUltimate.html +1 -1
- package/nodes/locales/zh-CN/knxUltimateHueController.html +3 -2
- package/nodes/locales/zh-CN/knxUltimateHueController.json +7 -0
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +2 -0
- package/nodes/utils/hueControllerResourceCatalog.js +71 -0
- package/package.json +1 -1
- package/resources/hueControllerProfiles.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 6.3.0** - August 2026<br/>
|
|
10
|
+
|
|
11
|
+
- **HUE Controller**: unified device-first workflow for all supported Hue API v2 resources, with automatic type detection and legacy-node migration.<br/>
|
|
12
|
+
- **Matter**: clearer commissioning progress and refined Controller/Bridge workflows and documentation.<br/>
|
|
13
|
+
- **Editor pickers**: Hue, Matter and KNX Group Address lists now open reliably and show every available entry before filtering.<br/>
|
|
14
|
+
|
|
15
|
+
**Version 6.2.3-beta.5** - August 2026<br/>
|
|
16
|
+
|
|
17
|
+
- **HUE Controller**: device-first selection with automatic type detection and all supported Hue API v2 resources.<br/>
|
|
18
|
+
- **Device pickers**: Hue, Matter and KNX Group Address lists now stay open on click and show all entries before filtering.<br/>
|
|
19
|
+
|
|
9
20
|
**Version 6.2.3-beta.4** - August 2026<br/>
|
|
10
21
|
|
|
11
22
|
- **Matter Controller — live commissioning progress**: replaced the pairing overlay spinner with a milestone-based progress bar driven by the actual matter.js commissioning flow. The blocking panel now describes the active discovery, PASE, device-information, fail-safe, regulatory, time-sync, attestation, credential, access-control, CASE reconnect and completion phase in English; when exposed by the commissionee, its product name, Vendor ID and Product ID appear below the current operation. Progress polling is local to the Node-RED editor, stops with the pairing request and is isolated by operation ID across editor tabs.<br/>
|
package/nodes/hue-config.js
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
/* eslint-disable no-inner-declarations */
|
|
6
6
|
/* eslint-disable max-len */
|
|
7
7
|
const cloneDeep = require('lodash/cloneDeep')
|
|
8
|
+
const {
|
|
9
|
+
HUE_CONTROLLER_RESOURCE_TYPES,
|
|
10
|
+
buildHueControllerResourceCatalog
|
|
11
|
+
} = require('./utils/hueControllerResourceCatalog')
|
|
8
12
|
// const classHUE = require("./utils/hueEngine").classHUE;
|
|
9
13
|
const hueColorConverter = require('./utils/colorManipulators/hueColorConverter')
|
|
10
14
|
|
|
@@ -355,6 +359,15 @@ module.exports = (RED) => {
|
|
|
355
359
|
}
|
|
356
360
|
}
|
|
357
361
|
if (node.hueAllResources === undefined) return
|
|
362
|
+
if (_rtype === 'hue_controller') {
|
|
363
|
+
const resourceResults = await Promise.all(HUE_CONTROLLER_RESOURCE_TYPES.map(async (resourceType) => {
|
|
364
|
+
const result = await node.getResources(resourceType)
|
|
365
|
+
return [resourceType, Array.isArray(result?.devices) ? result.devices : []]
|
|
366
|
+
}))
|
|
367
|
+
return {
|
|
368
|
+
devices: buildHueControllerResourceCatalog(Object.fromEntries(resourceResults))
|
|
369
|
+
}
|
|
370
|
+
}
|
|
358
371
|
// Returns capitalized string
|
|
359
372
|
function capStr (s) {
|
|
360
373
|
if (typeof s !== 'string') return ''
|
package/nodes/knxUltimate.html
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
<!-- <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/jquery.searchableSelect.js"></script> -->
|
|
2
2
|
|
|
3
3
|
<style>
|
|
4
|
-
/* Monaco editor - first area (input to bus): light green */
|
|
5
|
-
#sendMsgToKNXCode-editor .monaco-editor,
|
|
6
|
-
#sendMsgToKNXCode-editor .monaco-editor-background,
|
|
7
|
-
#sendMsgToKNXCode-editor .monaco-editor .margin,
|
|
8
|
-
#sendMsgToKNXCode-editor .monaco-editor .overflow-guard,
|
|
9
|
-
#sendMsgToKNXCode-editor .monaco-editor .lines-content,
|
|
10
|
-
#sendMsgToKNXCode-editor .monaco-editor .editor-scrollable {
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#receiveMsgFromKNXCode-editor .monaco-editor
|
|
16
|
-
#receiveMsgFromKNXCode-editor .monaco-editor
|
|
17
|
-
#receiveMsgFromKNXCode-editor .monaco-editor .
|
|
18
|
-
#receiveMsgFromKNXCode-editor .monaco-editor .
|
|
19
|
-
#receiveMsgFromKNXCode-editor .monaco-editor .
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
/* Monaco editor - first area (input to bus): light green */
|
|
5
|
+
#sendMsgToKNXCode-editor .monaco-editor,
|
|
6
|
+
#sendMsgToKNXCode-editor .monaco-editor-background,
|
|
7
|
+
#sendMsgToKNXCode-editor .monaco-editor .margin,
|
|
8
|
+
#sendMsgToKNXCode-editor .monaco-editor .overflow-guard,
|
|
9
|
+
#sendMsgToKNXCode-editor .monaco-editor .lines-content,
|
|
10
|
+
#sendMsgToKNXCode-editor .monaco-editor .editor-scrollable {
|
|
11
|
+
background-color: #e8f5e9 !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Monaco editor - second area (bus to output): light yellow */
|
|
15
|
+
#receiveMsgFromKNXCode-editor .monaco-editor,
|
|
16
|
+
#receiveMsgFromKNXCode-editor .monaco-editor-background,
|
|
17
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .margin,
|
|
18
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .overflow-guard,
|
|
19
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .lines-content,
|
|
20
|
+
#receiveMsgFromKNXCode-editor .monaco-editor .editor-scrollable {
|
|
21
|
+
background-color: #fffde7 !important;
|
|
22
|
+
}
|
|
22
23
|
</style>
|
|
23
24
|
|
|
24
25
|
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
|
|
@@ -76,19 +77,19 @@
|
|
|
76
77
|
return ((this.outputRBE === "true" || this.outputRBE === true) ? "|rbe| " : "") + functionSendMsgToKNXCode + (this.name || this.topic || "KNX Device") + (this.setTopicType === 'str' || this.setTopicType === undefined ? '' : ' [' + (this.setTopicType === 'listenAllGA' ? 'Universal' : this.setTopicType) + ']') + functionreceiveMsgFromKNXCode + ((this.inputRBE === "true" || this.inputRBE === true) ? " |rbe|" : "")
|
|
77
78
|
},
|
|
78
79
|
paletteLabel: "KNX DEVICE",
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
button: {
|
|
81
|
+
enabled: function () {
|
|
82
|
+
return !this.changed;
|
|
83
|
+
},
|
|
84
|
+
visible: function () {
|
|
85
|
+
const isUniversal = this.setTopicType === 'listenAllGA' || this.listenallga === true || this.listenallga === 'true';
|
|
86
|
+
if (isUniversal) return false;
|
|
87
|
+
return this.buttonEnabled === true || this.buttonEnabled === "true";
|
|
88
|
+
},
|
|
89
|
+
onclick: function () {
|
|
90
|
+
const node = this;
|
|
91
|
+
const mode = node.buttonMode || 'toggle';
|
|
92
|
+
const request = { id: node.id, mode };
|
|
92
93
|
if (mode === 'value') {
|
|
93
94
|
request.value = node.buttonStaticValue;
|
|
94
95
|
}
|
|
@@ -148,6 +149,7 @@
|
|
|
148
149
|
|
|
149
150
|
|
|
150
151
|
var node = this;
|
|
152
|
+
let topicPickerMouseDown = false;
|
|
151
153
|
if ($("#node-input-server").val() === "_ADD_") {
|
|
152
154
|
// Node-Red 4.0.x has a bug not selecting the default server node
|
|
153
155
|
try {
|
|
@@ -964,8 +966,20 @@ return msg;`
|
|
|
964
966
|
}
|
|
965
967
|
} catch (e) { }
|
|
966
968
|
}
|
|
967
|
-
}).
|
|
968
|
-
|
|
969
|
+
}).on('mousedown.knxUltimateTopicPicker', function () {
|
|
970
|
+
topicPickerMouseDown = true;
|
|
971
|
+
}).on('focus.knxUltimateTopicPicker', function () {
|
|
972
|
+
if (!topicPickerMouseDown) {
|
|
973
|
+
$(this).autocomplete('search', '');
|
|
974
|
+
}
|
|
975
|
+
}).on('click.knxUltimateTopicPicker', function () {
|
|
976
|
+
topicPickerMouseDown = false;
|
|
977
|
+
const input = this;
|
|
978
|
+
setTimeout(function () {
|
|
979
|
+
$(input).autocomplete('search', '');
|
|
980
|
+
}, 0);
|
|
981
|
+
}).on('blur.knxUltimateTopicPicker', function () {
|
|
982
|
+
topicPickerMouseDown = false;
|
|
969
983
|
}).autocomplete("instance")._renderItem = function (ul, item) {
|
|
970
984
|
const isSecure = !!item.isSecure;
|
|
971
985
|
const colorStyle = isSecure ? 'color: green;' : '';
|
|
@@ -1185,10 +1199,6 @@ return msg;`
|
|
|
1185
1199
|
<input id="node-input-gaSecure" name="node-input-gaSecure" type="hidden" value="false" />
|
|
1186
1200
|
|
|
1187
1201
|
<div class="form-row">
|
|
1188
|
-
<b>KNX Device node</b>
|
|
1189
|
-
<br />
|
|
1190
|
-
<br />
|
|
1191
|
-
|
|
1192
1202
|
<label for="node-input-server">
|
|
1193
1203
|
<i class="fa fa-circle-o"></i> <span data-i18n="knxUltimate.properties.node-input-server"></span>
|
|
1194
1204
|
</label>
|
|
@@ -1209,13 +1219,14 @@ return msg;`
|
|
|
1209
1219
|
<option value="global" data-i18n="knxUltimate.selectlists.SetTopic_global"></option>
|
|
1210
1220
|
<option value="env" data-i18n="knxUltimate.selectlists.SetTopic_env"></option>
|
|
1211
1221
|
</select>
|
|
1212
|
-
|
|
1222
|
+
<input type="text" id="node-input-topic" data-i18n="[placeholder]knxUltimate.placeholder.search"
|
|
1213
1223
|
style="width:180px" />
|
|
1214
1224
|
<span id="gaSecureShield" title="Data Secure" style="display:none;color:green;margin-left:6px;"><i
|
|
1215
1225
|
class="fa fa-shield"></i></span>
|
|
1226
|
+
<input style="flex:1 1 240px; min-width:240px; max-width:240px;" type="text" id="node-input-name"
|
|
1227
|
+
data-i18n="[placeholder]knxUltimate.properties.node-input-name" />
|
|
1216
1228
|
</div>
|
|
1217
1229
|
|
|
1218
|
-
|
|
1219
1230
|
<div class="form-row" id="divDatapointSelection">
|
|
1220
1231
|
<label for="node-input-dpt">
|
|
1221
1232
|
<img
|
|
@@ -1229,19 +1240,10 @@ return msg;`
|
|
|
1229
1240
|
</div>
|
|
1230
1241
|
<div class="form-row">
|
|
1231
1242
|
<span id="dptDetailsContainer" style="display:none;width:100%;">
|
|
1232
|
-
<div id="sampleCodeEditor" class="dpt-details-helplink" style="color:red;margin-bottom:4px;"></div>
|
|
1233
|
-
<div class="node-text-editor" id="example-editor" style="height:
|
|
1243
|
+
<!-- <div id="sampleCodeEditor" class="dpt-details-helplink" style="color:red;margin-bottom:4px;"></div> -->
|
|
1244
|
+
<div class="node-text-editor" id="example-editor" style="height:110px;padding:0px;"></div>
|
|
1234
1245
|
</span>
|
|
1235
1246
|
</div>
|
|
1236
|
-
<!-- <div class="form-row">
|
|
1237
|
-
<input type="checkbox" id="node-input-listenallga"
|
|
1238
|
-
style="display:inline-block; width:auto; vertical-align:top;" />
|
|
1239
|
-
<label style="width:auto" for="node-input-listenallga">
|
|
1240
|
-
<img
|
|
1241
|
-
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFGmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDMtMjNUMTY6MjM6MjMrMDE6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTAzLTIzVDE2OjI1OjM0KzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTAzLTIzVDE2OjI1OjM0KzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmJmNGM3NWVjLTIwNGYtNGY1YS05YTMxLTQ5NTU5YWJmZDE4NSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpiZjRjNzVlYy0yMDRmLTRmNWEtOWEzMS00OTU1OWFiZmQxODUiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpiZjRjNzVlYy0yMDRmLTRmNWEtOWEzMS00OTU1OWFiZmQxODUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJmNGM3NWVjLTIwNGYtNGY1YS05YTMxLTQ5NTU5YWJmZDE4NSIgc3RFdnQ6d2hlbj0iMjAyMC0wMy0yM1QxNjoyMzoyMyswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjEgKE1hY2ludG9zaCkiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nhtLUgAAAE9JREFUKJG1UMsOACAIgub//7IdqvVQtjrExU1EEQLuiGCvgTNgl5D74MmVZPu4wIxQAgm+/sDec2VhgQPgf0sq1unjMlYJE/3MZrvy+kMFZQkZEWfC7ikAAAAASUVORK5CYII="></img>
|
|
1242
|
-
<span data-i18n="knxUltimate.properties.node-input-listenallga"></span>
|
|
1243
|
-
</label>
|
|
1244
|
-
</div> -->
|
|
1245
1247
|
<div id="tabs">
|
|
1246
1248
|
<ul>
|
|
1247
1249
|
<li><a href="#tabs-1"><i class="fa fa-braille"></i> Advanced options</a></li>
|
|
@@ -1254,14 +1256,6 @@ return msg;`
|
|
|
1254
1256
|
<i class="fa fa-desktop"></i> General properties
|
|
1255
1257
|
</dt>
|
|
1256
1258
|
</div>
|
|
1257
|
-
<div class="form-row">
|
|
1258
|
-
<label style="width:180px" for="node-input-name">
|
|
1259
|
-
<i class="fa fa-tag"></i>
|
|
1260
|
-
<span data-i18n="knxUltimate.properties.node-input-name"></span>
|
|
1261
|
-
</label>
|
|
1262
|
-
<input style="flex:1 1 240px; min-width:240px; max-width:240px;" type="text" id="node-input-name"
|
|
1263
|
-
data-i18n="[placeholder]knxUltimate.properties.node-input-name" />
|
|
1264
|
-
</div>
|
|
1265
1259
|
<div class="form-row" id="divTopic">
|
|
1266
1260
|
<label style="width:180px" for="node-input-outputtopic">
|
|
1267
1261
|
<i class="fa fa-tasks"></i>
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
// Node-RED 4.x uses `_ADD_` both for the visible "none" option (when
|
|
37
37
|
// gateways exist) and for "Add new..." (when none exist).
|
|
38
38
|
const KNX_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__']);
|
|
39
|
+
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__']);
|
|
39
40
|
const TUTORIALS_URL = 'https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E';
|
|
40
41
|
const profileBundle = window.KNXUltimateHueControllerProfiles;
|
|
41
42
|
|
|
@@ -294,6 +295,11 @@
|
|
|
294
295
|
let legacyContext;
|
|
295
296
|
let closeMigrationDialog = () => { };
|
|
296
297
|
const profileDrafts = {};
|
|
298
|
+
let cachedHueResources = [];
|
|
299
|
+
let selectedHueResource;
|
|
300
|
+
let activeHueServerId;
|
|
301
|
+
let hueDevicePickerMouseDown = false;
|
|
302
|
+
const defaultHueDevicePlaceholder = $('#node-input-name').attr('placeholder') || '';
|
|
297
303
|
|
|
298
304
|
const hasLegacyHueNodes = editorContainsLegacyHueNodes();
|
|
299
305
|
const $migrationButton = $('#hue-controller-migrate-legacy-flow');
|
|
@@ -357,6 +363,168 @@
|
|
|
357
363
|
});
|
|
358
364
|
};
|
|
359
365
|
|
|
366
|
+
const controllerText = (key, fallback) => {
|
|
367
|
+
try {
|
|
368
|
+
const translated = controllerNode._(`knxUltimateHueController.${key}`);
|
|
369
|
+
if (translated && translated !== `knxUltimateHueController.${key}`) return translated;
|
|
370
|
+
} catch (error) { /* empty */ }
|
|
371
|
+
return fallback;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const resolveHueServerId = ({ allowStored = false } = {}) => {
|
|
375
|
+
const domValue = $('#node-input-serverHue').val();
|
|
376
|
+
if (domValue !== undefined && domValue !== null) {
|
|
377
|
+
const normalized = String(domValue).trim();
|
|
378
|
+
if (normalized !== '' && !HUE_EMPTY_SERVER_VALUES.has(normalized.toLowerCase())) return normalized;
|
|
379
|
+
}
|
|
380
|
+
if (allowStored && controllerNode.serverHue !== undefined && controllerNode.serverHue !== null) {
|
|
381
|
+
const stored = String(controllerNode.serverHue).trim();
|
|
382
|
+
if (stored !== '' && !HUE_EMPTY_SERVER_VALUES.has(stored.toLowerCase())) return stored;
|
|
383
|
+
}
|
|
384
|
+
return '';
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const getHueDeviceValue = () => {
|
|
388
|
+
const domValue = $('#node-input-hueDevice').val();
|
|
389
|
+
if (domValue !== undefined && domValue !== null && String(domValue).trim() !== '') return String(domValue).trim();
|
|
390
|
+
return controllerNode.hueDevice === undefined || controllerNode.hueDevice === null
|
|
391
|
+
? ''
|
|
392
|
+
: String(controllerNode.hueDevice).trim();
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const getRawHueResourceId = (value) => String(value || '').split('#')[0];
|
|
396
|
+
|
|
397
|
+
const updateSelectedDevicePresentation = (controllerType = controllerNode.hueControllerType) => {
|
|
398
|
+
const hasDevice = getHueDeviceValue() !== '';
|
|
399
|
+
const profile = PROFILE_TYPES[controllerType] || PROFILE_TYPES.light;
|
|
400
|
+
const typeLabel = controllerText(`types.${controllerType}`, profile.label);
|
|
401
|
+
$('#hue-controller-selected-device-type-value').text(typeLabel);
|
|
402
|
+
$('#hue-controller-selected-device-type').toggle(hasDevice);
|
|
403
|
+
$('#hue-controller-locate-device').toggle(hasDevice && controllerType === 'light');
|
|
404
|
+
$('#hue-controller-profile-editor').toggle(hasDevice);
|
|
405
|
+
$('#node-input-hueControllerType').val(controllerType);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const filterHueResources = (term) => {
|
|
409
|
+
const search = String(term || '').replace(/exactmatch/gi, '').trim().toLowerCase();
|
|
410
|
+
return cachedHueResources.filter((item) => {
|
|
411
|
+
if (search === '') return true;
|
|
412
|
+
return String(item.name || item.id || '').toLowerCase().includes(search);
|
|
413
|
+
}).map((item) => ({
|
|
414
|
+
...item,
|
|
415
|
+
value: item.name || item.id,
|
|
416
|
+
label: item.name || item.id
|
|
417
|
+
}));
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const reconcileSavedHueResource = () => {
|
|
421
|
+
const savedDevice = getHueDeviceValue();
|
|
422
|
+
if (savedDevice === '') return;
|
|
423
|
+
const rawId = getRawHueResourceId(savedDevice);
|
|
424
|
+
const match = cachedHueResources.find((item) => item.hueDevice === savedDevice)
|
|
425
|
+
|| cachedHueResources.find((item) => item.id === rawId && item.controllerType === controllerNode.hueControllerType)
|
|
426
|
+
|| cachedHueResources.find((item) => item.id === rawId);
|
|
427
|
+
if (!match) {
|
|
428
|
+
updateSelectedDevicePresentation();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
selectedHueResource = match;
|
|
432
|
+
if (!$('#node-input-name').val()) $('#node-input-name').val(match.name || match.id);
|
|
433
|
+
if (match.controllerType !== controllerNode.hueControllerType) {
|
|
434
|
+
applyHueResourceSelection(match);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
updateSelectedDevicePresentation(match.controllerType);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const fetchHueResources = (term, response, { forceRefresh = false } = {}) => {
|
|
441
|
+
const reply = typeof response === 'function' ? response : () => { };
|
|
442
|
+
const hueServerId = resolveHueServerId({ allowStored: true });
|
|
443
|
+
if (hueServerId === '') {
|
|
444
|
+
reply([]);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (!forceRefresh && cachedHueResources.length > 0) {
|
|
448
|
+
reply(filterHueResources(term));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
$('#hue-controller-devices-loading').show();
|
|
452
|
+
const refreshQuery = forceRefresh ? '&forceRefresh=1' : '';
|
|
453
|
+
$.getJSON(`KNXUltimateGetResourcesHUE?rtype=hue_controller&serverId=${encodeURIComponent(hueServerId)}${refreshQuery}&_=${Date.now()}`, (data) => {
|
|
454
|
+
const devices = Array.isArray(data) ? data : (Array.isArray(data?.devices) ? data.devices : []);
|
|
455
|
+
cachedHueResources = devices.filter((item) => item && PROFILE_TYPES[item.controllerType] && item.id && item.hueDevice);
|
|
456
|
+
$('#node-input-name').attr('placeholder', cachedHueResources.length > 0
|
|
457
|
+
? defaultHueDevicePlaceholder
|
|
458
|
+
: controllerText('no_devices', 'No Hue resources found. Check the Hue Bridge and press refresh.'));
|
|
459
|
+
reconcileSavedHueResource();
|
|
460
|
+
reply(filterHueResources(term));
|
|
461
|
+
}).fail(() => {
|
|
462
|
+
cachedHueResources = [];
|
|
463
|
+
$('#node-input-name').attr('placeholder', controllerText('no_devices', 'No Hue resources found. Check the Hue Bridge and press refresh.'));
|
|
464
|
+
reply([]);
|
|
465
|
+
}).always(() => {
|
|
466
|
+
$('#hue-controller-devices-loading').hide();
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const configureUnifiedDeviceControl = () => {
|
|
471
|
+
const $deviceName = $('#node-input-name');
|
|
472
|
+
if (!$deviceName.length) return;
|
|
473
|
+
$deviceName.autocomplete({
|
|
474
|
+
minLength: 0,
|
|
475
|
+
source(request, response) {
|
|
476
|
+
fetchHueResources(request.term, response);
|
|
477
|
+
},
|
|
478
|
+
select(event, ui) {
|
|
479
|
+
applyHueResourceSelection(ui.item);
|
|
480
|
+
return false;
|
|
481
|
+
},
|
|
482
|
+
change(event, ui) {
|
|
483
|
+
if (ui.item || selectedHueResource?.name === $deviceName.val()) return;
|
|
484
|
+
selectedHueResource = undefined;
|
|
485
|
+
controllerNode.hueDevice = '';
|
|
486
|
+
$('#node-input-hueDevice').val('');
|
|
487
|
+
updateSelectedDevicePresentation();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
$deviceName.off('.knxUltimateHueControllerDevice')
|
|
491
|
+
.on('mousedown.knxUltimateHueControllerDevice', () => {
|
|
492
|
+
hueDevicePickerMouseDown = true;
|
|
493
|
+
})
|
|
494
|
+
.on('focus.knxUltimateHueControllerDevice', function () {
|
|
495
|
+
if (!hueDevicePickerMouseDown) $(this).autocomplete('search', '');
|
|
496
|
+
})
|
|
497
|
+
.on('click.knxUltimateHueControllerDevice', function () {
|
|
498
|
+
hueDevicePickerMouseDown = false;
|
|
499
|
+
const input = this;
|
|
500
|
+
setTimeout(() => $(input).autocomplete('search', ''), 0);
|
|
501
|
+
})
|
|
502
|
+
.on('blur.knxUltimateHueControllerDevice', () => {
|
|
503
|
+
hueDevicePickerMouseDown = false;
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const applyHueResourceSelection = (item) => {
|
|
508
|
+
if (!item || !PROFILE_TYPES[item.controllerType] || !item.hueDevice) return;
|
|
509
|
+
const nextType = item.controllerType;
|
|
510
|
+
const typeChanged = nextType !== controllerNode.hueControllerType;
|
|
511
|
+
collectCurrentEditor();
|
|
512
|
+
if (typeChanged) prepareProfileValues(nextType, getProfileDefinition(nextType));
|
|
513
|
+
selectedHueResource = item;
|
|
514
|
+
controllerNode.hueControllerType = nextType;
|
|
515
|
+
controllerNode.name = item.name || item.id;
|
|
516
|
+
controllerNode.hueDevice = item.hueDevice;
|
|
517
|
+
if (item.deviceObject) controllerNode.hueDeviceObject = cloneEditorValue(item.deviceObject);
|
|
518
|
+
const nextDefinition = getProfileDefinition(nextType);
|
|
519
|
+
controllerNode.inputs = Number(nextDefinition?.inputs || 0);
|
|
520
|
+
controllerNode.outputs = Number(nextDefinition?.outputs || 0);
|
|
521
|
+
$('#node-input-name').val(controllerNode.name);
|
|
522
|
+
$('#node-input-hueDevice').val(controllerNode.hueDevice);
|
|
523
|
+
$('#node-input-hueControllerType').val(nextType);
|
|
524
|
+
renderProfileEditor(nextType, true);
|
|
525
|
+
updateSelectedDevicePresentation(nextType);
|
|
526
|
+
};
|
|
527
|
+
|
|
360
528
|
const renderProfileEditor = (controllerType, skipCollect) => {
|
|
361
529
|
// Controlled lifecycle transition:
|
|
362
530
|
// collect old -> dispose old -> mount private HTML -> translate ->
|
|
@@ -403,6 +571,13 @@
|
|
|
403
571
|
$profileContent.find('#node-input-server, #node-input-serverHue').each(function () {
|
|
404
572
|
$(this).closest('.form-row').remove();
|
|
405
573
|
});
|
|
574
|
+
// Device selection belongs to the unified wrapper. Remove the private
|
|
575
|
+
// profile's duplicate row/hidden value while keeping the static fields
|
|
576
|
+
// available to its mature lifecycle callbacks.
|
|
577
|
+
$profileContent.find('#node-input-name').each(function () {
|
|
578
|
+
$(this).closest('.form-row').remove();
|
|
579
|
+
});
|
|
580
|
+
$profileContent.find('#node-input-hueDevice').remove();
|
|
406
581
|
$container.empty().append($profileContent.contents());
|
|
407
582
|
// Node-RED translates the static dialog before oneditprepare. This
|
|
408
583
|
// fragment belongs to another node type, so qualify every key with the
|
|
@@ -428,9 +603,14 @@
|
|
|
428
603
|
// Light deliberately receives the real Hue config-node selection.
|
|
429
604
|
// Its private editor owns the bounded readiness poll and displays the
|
|
430
605
|
// hourglass until the bridge resources are available or it times out.
|
|
606
|
+
// The Light editor completes asynchronously and installs its historic
|
|
607
|
+
// light-only autocomplete inside Go(). Give it an explicit callback
|
|
608
|
+
// that restores the Controller-wide catalog after that late setup.
|
|
609
|
+
controllerNode.__configureHueControllerDeviceControl = configureUnifiedDeviceControl;
|
|
431
610
|
if (typeof definition.oneditprepare === 'function') {
|
|
432
611
|
definition.oneditprepare.call(legacyContext);
|
|
433
612
|
}
|
|
613
|
+
configureUnifiedDeviceControl();
|
|
434
614
|
// Profile guidance is already available in the node help. Keeping it
|
|
435
615
|
// out of the embedded editor makes every device profile more compact.
|
|
436
616
|
$container.find('.form-tips').remove();
|
|
@@ -449,29 +629,46 @@
|
|
|
449
629
|
$('#node-input-server')
|
|
450
630
|
.off('.knxUltimateHueControllerKnxVisibility')
|
|
451
631
|
.on('change.knxUltimateHueControllerKnxVisibility', updateControllerKnxVisibility);
|
|
632
|
+
updateSelectedDevicePresentation(controllerType);
|
|
452
633
|
focusProfileHelp(controllerType);
|
|
453
634
|
};
|
|
454
635
|
|
|
455
636
|
const selectedType = PROFILE_TYPES[this.hueControllerType] ? this.hueControllerType : 'light';
|
|
456
|
-
$('#node-input-hueControllerType')
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
637
|
+
$('#node-input-hueControllerType').val(selectedType);
|
|
638
|
+
|
|
639
|
+
activeHueServerId = resolveHueServerId({ allowStored: true });
|
|
640
|
+
$('#hue-controller-refresh-devices')
|
|
641
|
+
.off('.knxUltimateHueControllerDevice')
|
|
642
|
+
.on('click.knxUltimateHueControllerDevice', () => {
|
|
643
|
+
cachedHueResources = [];
|
|
644
|
+
fetchHueResources('', (items) => {
|
|
645
|
+
if (items.length) $('#node-input-name').autocomplete('search', '');
|
|
646
|
+
}, { forceRefresh: true });
|
|
647
|
+
});
|
|
648
|
+
$('#node-input-serverHue')
|
|
649
|
+
.off('.knxUltimateHueControllerDevice')
|
|
650
|
+
.on('change.knxUltimateHueControllerDevice', () => {
|
|
651
|
+
const nextServerId = resolveHueServerId();
|
|
652
|
+
if (nextServerId === activeHueServerId) return;
|
|
653
|
+
activeHueServerId = nextServerId;
|
|
654
|
+
cachedHueResources = [];
|
|
655
|
+
selectedHueResource = undefined;
|
|
656
|
+
controllerNode.name = '';
|
|
657
|
+
controllerNode.hueDevice = '';
|
|
658
|
+
$('#node-input-name, #node-input-hueDevice').val('');
|
|
659
|
+
updateSelectedDevicePresentation();
|
|
468
660
|
});
|
|
469
661
|
|
|
470
662
|
this._hueControllerCollectEditor = collectCurrentEditor;
|
|
471
663
|
this._hueControllerGetLegacyContext = () => legacyContext;
|
|
472
664
|
this._hueControllerGetProfile = () => activeProfile;
|
|
473
665
|
this._hueControllerCloseMigration = () => closeMigrationDialog();
|
|
666
|
+
this._hueControllerCleanupDevice = () => {
|
|
667
|
+
$('#hue-controller-refresh-devices, #node-input-serverHue, #node-input-name').off('.knxUltimateHueControllerDevice');
|
|
668
|
+
delete controllerNode.__configureHueControllerDeviceControl;
|
|
669
|
+
};
|
|
474
670
|
renderProfileEditor(selectedType);
|
|
671
|
+
fetchHueResources('', () => { });
|
|
475
672
|
},
|
|
476
673
|
oneditsave() {
|
|
477
674
|
// Preserve lifecycle order: close auxiliary UI, collect mounted fields,
|
|
@@ -496,10 +693,12 @@
|
|
|
496
693
|
if (Number.isFinite(Number(legacyContext.inputs))) this.inputs = Number(legacyContext.inputs);
|
|
497
694
|
if (Number.isFinite(Number(legacyContext.outputs))) this.outputs = Number(legacyContext.outputs);
|
|
498
695
|
}
|
|
696
|
+
if (typeof this._hueControllerCleanupDevice === 'function') this._hueControllerCleanupDevice();
|
|
499
697
|
delete this._hueControllerCollectEditor;
|
|
500
698
|
delete this._hueControllerGetLegacyContext;
|
|
501
699
|
delete this._hueControllerGetProfile;
|
|
502
700
|
delete this._hueControllerCloseMigration;
|
|
701
|
+
delete this._hueControllerCleanupDevice;
|
|
503
702
|
},
|
|
504
703
|
oneditcancel() {
|
|
505
704
|
// Cancel releases profile-owned editor state but deliberately does not
|
|
@@ -515,7 +714,9 @@
|
|
|
515
714
|
if (definition && typeof definition.oneditcancel === 'function') {
|
|
516
715
|
definition.oneditcancel.call(legacyContext || this);
|
|
517
716
|
}
|
|
717
|
+
if (typeof this._hueControllerCleanupDevice === 'function') this._hueControllerCleanupDevice();
|
|
518
718
|
delete this._hueControllerCloseMigration;
|
|
719
|
+
delete this._hueControllerCleanupDevice;
|
|
519
720
|
}
|
|
520
721
|
});
|
|
521
722
|
}());
|
|
@@ -551,29 +752,37 @@
|
|
|
551
752
|
</label>
|
|
552
753
|
<input type="text" id="node-input-serverHue">
|
|
553
754
|
</div>
|
|
554
|
-
<
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
755
|
+
<br />
|
|
756
|
+
<p>
|
|
757
|
+
<b><span data-i18n="knxUltimateHueController.hue_section"></span></b>
|
|
758
|
+
</p>
|
|
759
|
+
<div class="form-row" id="hue-controller-device-row">
|
|
760
|
+
<label for="node-input-name">
|
|
761
|
+
<i class="fa fa-play-circle"></i>
|
|
762
|
+
<span data-i18n="knxUltimateHueController.hue_device"></span>
|
|
558
763
|
</label>
|
|
559
|
-
<
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
<
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
<
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
<
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
<
|
|
574
|
-
<
|
|
575
|
-
|
|
764
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]knxUltimateHueController.hue_device_placeholder"
|
|
765
|
+
style="flex:1 1 240px; min-width:240px; max-width:240px;">
|
|
766
|
+
<button type="button" id="hue-controller-refresh-devices" class="red-ui-button"
|
|
767
|
+
style="margin-left:6px; color:#1b7d33; border-color:#1b7d33;" data-i18n="[title]knxUltimateHueController.refresh_devices">
|
|
768
|
+
<i class="fa fa-sync"></i>
|
|
769
|
+
</button>
|
|
770
|
+
<button type="button" id="hue-controller-locate-device" class="red-ui-button hue-locate-device"
|
|
771
|
+
style="display:none; margin-left:6px; color:#ff9800; border-color:#ff9800;" data-i18n="[title]knxUltimateHueController.locate_device">
|
|
772
|
+
<i class="fa fa-play"></i>
|
|
773
|
+
</button>
|
|
774
|
+
<span id="hue-controller-devices-loading" style="margin-left:6px; display:none; color:#1b7d33;">
|
|
775
|
+
<i class="fa fa-circle-notch fa-spin"></i>
|
|
776
|
+
</span>
|
|
777
|
+
<span id="hue-controller-selected-device-type" style="display:none; margin-left:10px; color:#666; white-space:nowrap;">
|
|
778
|
+
<i class="fa fa-cubes"></i>
|
|
779
|
+
<span data-i18n="knxUltimateHueController.device_type"></span>:
|
|
780
|
+
<span id="hue-controller-selected-device-type-value" style="font-weight:600;"></span>
|
|
781
|
+
</span>
|
|
782
|
+
<input type="hidden" id="node-input-hueDevice">
|
|
783
|
+
<input type="hidden" id="node-input-hueControllerType">
|
|
576
784
|
</div>
|
|
785
|
+
<br />
|
|
577
786
|
<div id="hue-controller-profile-editor"></div>
|
|
578
787
|
<br/>
|
|
579
788
|
<br/>
|
|
@@ -586,7 +795,9 @@
|
|
|
586
795
|
|
|
587
796
|
[**KNX-Ultimate video tutorials (YouTube playlist)**](https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E)
|
|
588
797
|
|
|
589
|
-
The unified controller for Hue API v2 resources. Choose
|
|
798
|
+
The unified controller for Hue API v2 resources. Choose a Hue device or resource; its device type is detected automatically and the matching KNX mapping, behaviour and node pins are then displayed.
|
|
799
|
+
|
|
800
|
+
Clicking or focusing the Hue device field always opens the complete resource list, even when a device is already selected. Typing continues to filter the list.
|
|
590
801
|
|
|
591
802
|
The controller supports lights and grouped lights, plugs, buttons, Tap dial, motion resources, contacts, light level, temperature, humidity, scenes, battery, Zigbee connectivity, and device software update resources.
|
|
592
803
|
|
|
@@ -610,7 +821,7 @@ For a single light, the **Dim**, **Tunable White**, **RGB/HSV**, and native-effe
|
|
|
610
821
|
|
|
611
822
|
While the Hue Bridge is still loading its resources, the Light editor displays a spinning hourglass and polls readiness every 500 ms. The wait is bounded: after about 10 seconds the editor is released, the mapping tabs remain hidden, and a localized error asks you to check the bridge configuration, deploy, and retry. Closing, saving, or changing function always cancels the pending timer.
|
|
612
823
|
|
|
613
|
-
Changing **
|
|
824
|
+
Changing the selected **Hue device** automatically changes the detected **Device type**, visible editor and node pin layout. After saving, reopen the node to verify the selected resource and mappings.
|
|
614
825
|
|
|
615
826
|
KNX mapping rows keep GA, DPT and Name on one line. DPT selectors use a compact fixed range, while Name fields are shorter and may contract further on a narrow editor tray without changing their stored text. Saved DPT values are retained while selector options load asynchronously.
|
|
616
827
|
|
|
@@ -297,6 +297,7 @@
|
|
|
297
297
|
$deviceNameInput.val(node.name);
|
|
298
298
|
}
|
|
299
299
|
let selectedMatterDeviceName = ($deviceNameInput.val() || '').trim();
|
|
300
|
+
let matterDevicePickerMouseDown = false;
|
|
300
301
|
let showingNoHueDevicesPlaceholder = false;
|
|
301
302
|
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__', '__null__', 'null', 'undefined']);
|
|
302
303
|
let locateSessionActive = false;
|
|
@@ -1506,8 +1507,16 @@
|
|
|
1506
1507
|
focus(event, ui) {
|
|
1507
1508
|
event.preventDefault();
|
|
1508
1509
|
}
|
|
1509
|
-
}).
|
|
1510
|
-
|
|
1510
|
+
}).on('mousedown.knxUltimateMatterControllerDevicePicker', () => {
|
|
1511
|
+
matterDevicePickerMouseDown = true;
|
|
1512
|
+
}).on('focus.knxUltimateMatterControllerDevicePicker', function () {
|
|
1513
|
+
if (!matterDevicePickerMouseDown) $(this).autocomplete('search', '');
|
|
1514
|
+
}).on('click.knxUltimateMatterControllerDevicePicker', function () {
|
|
1515
|
+
matterDevicePickerMouseDown = false;
|
|
1516
|
+
const input = this;
|
|
1517
|
+
setTimeout(() => $(input).autocomplete('search', ''), 0);
|
|
1518
|
+
}).on('blur.knxUltimateMatterControllerDevicePicker', () => {
|
|
1519
|
+
matterDevicePickerMouseDown = false;
|
|
1511
1520
|
}).on('input.knxUltimateMatterControllerDevice', function () {
|
|
1512
1521
|
const typedValue = $(this).val().trim();
|
|
1513
1522
|
if (typedValue === '' || typedValue !== selectedMatterDeviceName) {
|
|
@@ -1529,7 +1538,7 @@
|
|
|
1529
1538
|
node._cachedHueLightDevices = cachedHueDevices;
|
|
1530
1539
|
fetchHueDevices('', () => {
|
|
1531
1540
|
if ($deviceNameInput.length) {
|
|
1532
|
-
$deviceNameInput.autocomplete('search',
|
|
1541
|
+
$deviceNameInput.autocomplete('search', '');
|
|
1533
1542
|
}
|
|
1534
1543
|
}, { forceRefresh: true });
|
|
1535
1544
|
});
|
|
@@ -2668,6 +2677,9 @@ the device picker. Door Lock, Window Covering, Thermostat, Fan and Switch endpoi
|
|
|
2668
2677
|
dedicated profiles. Switch press, long-press and multi-press events are emitted on the
|
|
2669
2678
|
optional flow output; simpler endpoints continue through the generic mapped fallback.
|
|
2670
2679
|
|
|
2680
|
+
Clicking or focusing the Matter device field always opens the complete commissioned
|
|
2681
|
+
endpoint list, even when a device is already selected. Typing continues to filter it.
|
|
2682
|
+
|
|
2671
2683
|
**Flow input**
|
|
2672
2684
|
|
|
2673
2685
|
Enable **Node Input/Output PINs** to reveal the **Flow input** section directly below
|
|
@@ -11,7 +11,7 @@ Dieser Node steuert eine KNX-Gruppenadresse und ist der am häufigsten verwendet
|
|
|
11
11
|
|--|--|
|
|
12
12
|
| Gateway | Zu verwendendes KNX-Gateway auswählen |
|
|
13
13
|
| GA-Typ (Dropdown) | Typ der Gruppenadresse. **3-Ebenen** ist Standard (Eingabe der 3-stufigen GA oder GA-Bezeichnung, sofern ETS importiert). **Global** liest die GA beim Start aus einer globalen Variablen, **Flow** analog auf Flow-Ebene. **$Env variable** liest die GA aus einer Umgebungsvariable. **Universeller Modus (alle GAs abhören)** reagiert auf ALLE Gruppenadressen. |
|
|
14
|
-
| Gruppenadresse | Zu steuernde Gruppenadresse.
|
|
14
|
+
| Gruppenadresse | Zu steuernde Gruppenadresse. Bei importierter ETS öffnet ein Klick oder Fokus auf dieses Feld die vollständige Liste, auch wenn bereits eine GA ausgewählt ist; durch Tippen werden die Einträge gefiltert. Kann leer bleiben, wenn du sie per `msg.setConfig` setzt. |
|
|
15
15
|
| Datenpunkt | Der zum Node gehörende Datapoint. |
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate video tutorials (YouTube playlist)</strong></a> Sehen Sie sich vor dem Start die <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">Videoanleitung auf YouTube</a> an.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> ist der einheitliche Knoten für Hue-API-v2-Ressourcen.</p>
|
|
4
|
-
<p>Wählen Sie
|
|
5
|
-
<p>
|
|
4
|
+
<p>Wählen Sie ein Hue-Gerät oder eine Hue-Ressource. HUE Controller erkennt den Gerätetyp automatisch und öffnet den passenden Editor für KNX-Zuordnungen, Verhalten und Node-Anschlüsse.</p>
|
|
5
|
+
<p>Geräteauswahl, Aktualisierung und erkannter Typ stehen wie beim Matter Controller in derselben kompakten Zeile.</p>
|
|
6
|
+
<p>Ein Klick oder Fokus auf das Gerätefeld öffnet immer die vollständige Hue-Ressourcenliste, auch wenn bereits ein Gerät ausgewählt ist. Beim Tippen wird die Liste weiterhin gefiltert.</p>
|
|
6
7
|
<p>KNX-Zuordnungszeilen halten GA, DPT und Name in einer Zeile. DPT-Auswahl und Namensfeld verwenden kompakte Breiten; das Namensfeld kann sich in schmalen Editoren weiter verkleinern, ohne den gespeicherten Text zu ändern. Gespeicherte DPT-Werte bleiben erhalten, während die Auswahloptionen asynchron geladen werden.</p>
|
|
7
8
|
<p>Unterstützt werden Leuchten und Leuchtengruppen, Steckdosen, Taster, Tap dial, Bewegungs- und Kamerabewegungsressourcen, Kontakte, Lichtstärke, Temperatur, Luftfeuchtigkeit, Szenen, Batterie, Zigbee-Verbindung und Softwareupdate.</p>
|
|
8
9
|
<p>Wenn kein <strong>KNX-Gateway</strong> ausgewählt ist (einschließlich <code>none</code> und <em>Neu hinzufügen...</em>), werden die KNX-Zuordnungsfelder ausgeblendet; die Auswahl der Hue-Ressource und reine Flow-Optionen bleiben verfügbar.</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Gerätefunktion",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Hue-Gerät",
|
|
10
|
+
"hue_device_placeholder": "Hue-Gerät oder -Ressource suchen",
|
|
11
|
+
"device_type": "Gerätetyp",
|
|
12
|
+
"refresh_devices": "Hue-Geräte aktualisieren",
|
|
13
|
+
"locate_device": "Ausgewählte Hue-Leuchte lokalisieren",
|
|
14
|
+
"no_devices": "Keine Hue-Ressourcen gefunden. Prüfen Sie die Hue Bridge und aktualisieren Sie die Liste.",
|
|
8
15
|
"legacy_node_notice": "Dieser Legacy-HUE-Knoten ist veraltet. Verwenden Sie für neue Flows den neuen HUE Controller-Knoten.",
|
|
9
16
|
"migration_button": "Legacy-HUE-Knoten konvertieren",
|
|
10
17
|
"migration_button_title": "Alle Legacy-HUE-Knoten im Editor konvertieren",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
Dieser Knoten steuert einen bereits gekoppelten Matter-Endpunkt über KNX. Wähle das Matter-Gerät aus; der Editor erkennt die Fähigkeiten des Endpunkts und zeigt nur die passenden KNX-Zuordnungen.
|
|
5
5
|
|
|
6
|
+
Ein Klick oder Fokus auf das Matter-Gerätefeld öffnet immer die vollständige Liste der gekoppelten Endpunkte, auch wenn bereits ein Gerät ausgewählt ist. Beim Tippen wird die Liste weiterhin gefiltert.
|
|
7
|
+
|
|
6
8
|
Er ersetzt die unveröffentlichten getrennten Matter-Controller-Nodes und behält die komplette Licht-UI bei, wenn der gewählte Endpunkt ein Licht ist.
|
|
7
9
|
|
|
8
10
|
## Konfiguration
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|--|--|
|
|
12
12
|
| Gateway | Select the KNX gateway to be used |
|
|
13
13
|
| GA Type dropdown list | The group address type. **3-Levels** is the default, where you can type the _3 level group address_ or the _group address name_ (if you've upload the ETS file), or **Global**, for reading the GA at startup from a global variable, or **Flow** that do the same as the _Global_, but at flow level. Select **$Env variable** to read the group address from an environment variable. Select **Universal mode (listen to all Group Addresses)** to react to ALL group addresses.|
|
|
14
|
-
| Group Addr. | The KNX Group Address you want to control. If you've imported the ETS group addresses file,
|
|
14
|
+
| Group Addr. | The KNX Group Address you want to control. If you've imported the ETS group addresses file, clicking or focusing this field opens the complete list, even when a GA is already selected; typing filters the entries. You can leave it empty if you wish set it with _msg.setConfig_ input message. |
|
|
15
15
|
| Datapoint | The datapoint belonging to your node.|
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate video tutorials (YouTube playlist)</strong></a> <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">Watch the explanatory video on YouTube</a> before starting.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> is the unified node for Hue API v2 resources.</p>
|
|
4
|
-
<p>Select
|
|
5
|
-
<p>The device
|
|
4
|
+
<p>Select a Hue device or resource. HUE Controller detects its device type automatically and opens the matching KNX mapping, behaviour and node-pin editor.</p>
|
|
5
|
+
<p>The device picker, refresh action and detected type share the same compact row, matching the Matter Controller workflow.</p>
|
|
6
|
+
<p>Clicking or focusing the device field always opens the complete Hue resource list, even when a device is already selected. Typing continues to filter the list.</p>
|
|
6
7
|
<p>KNX mapping rows keep GA, DPT and Name on one line. DPT selectors and Name fields use compact widths, and the Name field can contract further on narrow editor trays without changing its stored text. Saved DPT values are retained while the selector options load asynchronously.</p>
|
|
7
8
|
<p>Supported functions: lights and grouped lights, plugs, buttons, Tap dial, motion and camera motion, contacts, light level, temperature, humidity, scenes, battery, Zigbee connectivity, and device software update.</p>
|
|
8
9
|
<p>When no <strong>KNX Gateway</strong> is selected (including <code>none</code> and <em>Add new...</em>), KNX mapping fields are hidden while Hue resource selection and flow-only options remain available.</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Device function",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Hue device",
|
|
10
|
+
"hue_device_placeholder": "Search your Hue device or resource",
|
|
11
|
+
"device_type": "Device type",
|
|
12
|
+
"refresh_devices": "Refresh Hue devices",
|
|
13
|
+
"locate_device": "Locate selected Hue light",
|
|
14
|
+
"no_devices": "No Hue resources found. Check the Hue Bridge and press refresh.",
|
|
8
15
|
"legacy_node_notice": "This legacy HUE node is deprecated. Use the new HUE Controller node for new flows.",
|
|
9
16
|
"migration_button": "Convert legacy HUE nodes",
|
|
10
17
|
"migration_button_title": "Convert all legacy HUE nodes in the editor",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
This node controls a commissioned Matter endpoint from KNX. Select the Matter device and the editor detects its capabilities, then shows only the KNX mappings that make sense for that endpoint.
|
|
5
5
|
|
|
6
|
+
Clicking or focusing the Matter device field always opens the complete commissioned endpoint list, even when a device is already selected. Typing continues to filter it.
|
|
7
|
+
|
|
6
8
|
It replaces the unpublished per-device Matter controller nodes and keeps the full light UI when the selected endpoint is a light.
|
|
7
9
|
|
|
8
10
|
## Configuration
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|-|-|
|
|
12
12
|
| Puerta | Seleccione la puerta de enlace KNX para ser utilizada |
|
|
13
13
|
| Lista desplegable Tipo de GA | El tipo de dirección de grupo. **3 niveles** es el valor predeterminado, donde puede escribir la dirección de grupo de nivel _3 o el _group name_ (si ha cargado el archivo ETS), o **global**, para leer el GA al inicio desde una variable global, o **flujo** que hace lo mismo que el _global_, pero a nivel de flujo. Seleccione **$Env variable** para leer la dirección de grupo de una variable de entorno. Seleccione **Modo universal (escuche todas las direcciones de grupo)** para reaccionar a todas las direcciones de grupo. |
|
|
14
|
-
| ADR DE GRUPO. | La dirección de grupo KNX que desea controlar. Si ha importado el archivo de direcciones
|
|
14
|
+
| ADR DE GRUPO. | La dirección de grupo KNX que desea controlar. Si ha importado el archivo de direcciones de grupo ETS, al hacer clic o enfocar este campo se abre la lista completa, aunque ya haya una GA seleccionada; al escribir se filtran las entradas. Puede dejarlo vacío si desea establecerlo con el mensaje de entrada _msg.setConfig_. |
|
|
15
15
|
| Punto de datos | El punto de datos que pertenece a su nodo. |
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate video tutorials (YouTube playlist)</strong></a> Antes de empezar, <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">mira el vídeo explicativo en YouTube</a>.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> es el nodo unificado para recursos de Hue API v2.</p>
|
|
4
|
-
<p>Selecciona
|
|
5
|
-
<p>El selector de
|
|
4
|
+
<p>Selecciona un dispositivo o recurso Hue. HUE Controller detecta automáticamente su tipo y abre el editor correspondiente para mapeos KNX, comportamiento y puertos del nodo.</p>
|
|
5
|
+
<p>El selector de dispositivo, la actualización y el tipo detectado comparten la misma fila compacta, como en Matter Controller.</p>
|
|
6
|
+
<p>Al hacer clic o enfocar el campo del dispositivo siempre se abre la lista completa de recursos Hue, aunque ya haya un dispositivo seleccionado. Al escribir, la lista sigue filtrándose.</p>
|
|
6
7
|
<p>Las filas de mapeo KNX mantienen GA, DPT y Nombre en una sola línea. Los campos DPT y Nombre usan anchos compactos; Nombre puede reducirse aún más en editores estrechos sin modificar el texto guardado. Los valores DPT guardados se conservan mientras las opciones del selector se cargan de forma asíncrona.</p>
|
|
7
8
|
<p>Admite luces y grupos de luces, enchufes, botones, Tap dial, movimiento y movimiento de cámara, contactos, nivel de luz, temperatura, humedad, escenas, batería, conectividad Zigbee y actualización de software.</p>
|
|
8
9
|
<p>Cuando no hay ningún <strong>Gateway KNX</strong> seleccionado (incluidos <code>none</code> y <em>Añadir nuevo...</em>), se ocultan los campos de mapeo KNX; la selección del recurso Hue y las opciones exclusivas del flujo siguen disponibles.</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Función del dispositivo",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Dispositivo Hue",
|
|
10
|
+
"hue_device_placeholder": "Buscar dispositivo o recurso Hue",
|
|
11
|
+
"device_type": "Tipo de dispositivo",
|
|
12
|
+
"refresh_devices": "Actualizar dispositivos Hue",
|
|
13
|
+
"locate_device": "Localizar la luz Hue seleccionada",
|
|
14
|
+
"no_devices": "No se encontraron recursos Hue. Comprueba Hue Bridge y actualiza la lista.",
|
|
8
15
|
"legacy_node_notice": "Este nodo HUE legacy está obsoleto. Usa el nuevo nodo HUE Controller para los flujos nuevos.",
|
|
9
16
|
"migration_button": "Convertir nodos HUE legacy",
|
|
10
17
|
"migration_button_title": "Convertir todos los nodos HUE legacy del editor",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
Este nodo controla desde KNX un endpoint Matter ya emparejado. Selecciona el dispositivo Matter y el editor detecta sus capacidades, mostrando solo las asignaciones KNX coherentes con ese endpoint.
|
|
5
5
|
|
|
6
|
+
Al hacer clic o enfocar el campo del dispositivo Matter siempre se abre la lista completa de endpoints emparejados, aunque ya haya un dispositivo seleccionado. Al escribir, la lista sigue filtrándose.
|
|
7
|
+
|
|
6
8
|
Sustituye a los nodos Matter separados no publicados y conserva toda la UI de luz cuando el endpoint seleccionado es una luz.
|
|
7
9
|
|
|
8
10
|
## Configuración
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|-|-|
|
|
12
12
|
| Porte | Sélectionnez la passerelle KNX à utiliser |
|
|
13
13
|
| Liste déroulante de type GA | Le type d'adresse de groupe. **3 niveaux** est la valeur par défaut, où vous pouvez saisir l'adresse du groupe de niveau _3 ou le _group name_ (si vous avez téléchargé le fichier ETS), ou **global**, pour lire le GA au démarrage à partir d'une variable globale, ou **flux** qui font la même chose que le _Global_, mais au niveau du flux. Sélectionnez **$Env variable** pour lire l'adresse de groupe à partir d'une variable d'environnement. Sélectionnez **Mode universel (écoutez toutes les adresses de groupe)** pour réagir à toutes les adresses de groupe. |
|
|
14
|
-
| Groupe addr. | L'adresse du groupe KNX que vous souhaitez contrôler. Si vous avez importé le fichier d'adresses
|
|
14
|
+
| Groupe addr. | L'adresse du groupe KNX que vous souhaitez contrôler. Si vous avez importé le fichier d'adresses de groupe ETS, cliquer sur ce champ ou lui donner le focus ouvre la liste complète, même lorsqu'une GA est déjà sélectionnée ; la saisie filtre les entrées. Vous pouvez le laisser vide si vous souhaitez le définir avec le message d'entrée _msg.setConfig_. |
|
|
15
15
|
| Point de données | Le point de données appartenant à votre nœud. |
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate video tutorials (YouTube playlist)</strong></a> Avant de commencer, <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">regardez la vidéo explicative sur YouTube</a>.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> est le nœud unifié pour les ressources Hue API v2.</p>
|
|
4
|
-
<p>Sélectionnez
|
|
5
|
-
<p>Le sélecteur
|
|
4
|
+
<p>Sélectionnez un appareil ou une ressource Hue. HUE Controller détecte automatiquement son type et ouvre l'éditeur correspondant pour le mappage KNX, le comportement et les ports du nœud.</p>
|
|
5
|
+
<p>Le sélecteur d'appareil, l'actualisation et le type détecté partagent la même ligne compacte, comme dans Matter Controller.</p>
|
|
6
|
+
<p>Un clic ou le focus sur le champ de l'appareil ouvre toujours la liste complète des ressources Hue, même si un appareil est déjà sélectionné. La saisie continue de filtrer la liste.</p>
|
|
6
7
|
<p>Les lignes de mappage KNX conservent GA, DPT et Nom sur une seule ligne. Les champs DPT et Nom utilisent des largeurs compactes ; le champ Nom peut encore se réduire dans un éditeur étroit sans modifier le texte enregistré. Les valeurs DPT enregistrées sont conservées pendant le chargement asynchrone des options du sélecteur.</p>
|
|
7
8
|
<p>Fonctions prises en charge : lampes et groupes, prises, boutons, Tap dial, mouvement et mouvement de caméra, contacts, niveau de lumière, température, humidité, scènes, batterie, connectivité Zigbee et mise à jour logicielle.</p>
|
|
8
9
|
<p>Lorsqu'aucune <strong>Passerelle KNX</strong> n'est sélectionnée (y compris <code>none</code> et <em>Ajouter...</em>), les champs de mappage KNX sont masqués ; la sélection de la ressource Hue et les options réservées au flow restent disponibles.</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Fonction de l'appareil",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Appareil Hue",
|
|
10
|
+
"hue_device_placeholder": "Rechercher un appareil ou une ressource Hue",
|
|
11
|
+
"device_type": "Type d'appareil",
|
|
12
|
+
"refresh_devices": "Actualiser les appareils Hue",
|
|
13
|
+
"locate_device": "Localiser la lampe Hue sélectionnée",
|
|
14
|
+
"no_devices": "Aucune ressource Hue trouvée. Vérifiez le Hue Bridge et actualisez la liste.",
|
|
8
15
|
"legacy_node_notice": "Ce nœud HUE legacy est obsolète. Utilisez le nouveau nœud HUE Controller pour les nouveaux flows.",
|
|
9
16
|
"migration_button": "Convertir les nœuds HUE legacy",
|
|
10
17
|
"migration_button_title": "Convertir tous les nœuds HUE legacy présents dans l'éditeur",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
Ce nœud contrôle depuis KNX un endpoint Matter déjà appairé. Sélectionnez l'appareil Matter et l'éditeur détecte ses capacités, puis affiche uniquement les mappings KNX adaptés à cet endpoint.
|
|
5
5
|
|
|
6
|
+
Un clic ou le focus sur le champ de l'appareil Matter ouvre toujours la liste complète des endpoints appairés, même si un appareil est déjà sélectionné. La saisie continue de filtrer la liste.
|
|
7
|
+
|
|
6
8
|
Il remplace les nœuds Matter séparés non publiés et conserve toute l'UI lumière lorsque l'endpoint sélectionné est une lumière.
|
|
7
9
|
|
|
8
10
|
## Configuration
|
|
@@ -11,7 +11,7 @@ Questo nodo controlla un Indirizzo di Gruppo KNX; è il nodo più utilizzato.
|
|
|
11
11
|
|--|--|
|
|
12
12
|
| Gateway | Seleziona il gateway KNX da utilizzare |
|
|
13
13
|
| Elenco a discesa tipo GA | Tipo di indirizzo di gruppo. **3-Livelli** è il default, dove puoi digitare il GA a 3 livelli o il nome GA (se hai caricato il file ETS); **Global** legge il GA da una variabile globale all'avvio; **Flow** fa lo stesso a livello di flow. Seleziona **$Env variable** per leggere il GA da una variabile d'ambiente. Seleziona **Modalità universale (ascolta tutti gli Indirizzi di Gruppo)** per reagire a TUTTI i GA. |
|
|
14
|
-
| Ind. Gruppo | L'indirizzo di gruppo KNX da controllare. Se hai importato il file ETS,
|
|
14
|
+
| Ind. Gruppo | L'indirizzo di gruppo KNX da controllare. Se hai importato il file ETS, facendo clic o portando il focus sul campo si apre l'elenco completo, anche quando un GA è già selezionato; digitando puoi filtrare le voci. Puoi lasciarlo vuoto se intendi impostarlo tramite messaggio di ingresso `msg.setConfig`. |
|
|
15
15
|
| Datapoint | Il Datapoint associato al nodo. |
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate video tutorials (YouTube playlist)</strong></a> Prima di iniziare, <a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">guarda il video esplicativo su YouTube</a>.</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> è il nodo unificato per le risorse Hue API v2.</p>
|
|
4
|
-
<p>Seleziona
|
|
5
|
-
<p>
|
|
4
|
+
<p>Seleziona un dispositivo o una risorsa Hue. HUE Controller ne rileva automaticamente il tipo e apre l'editor corretto per mappature KNX, comportamento e pin del nodo.</p>
|
|
5
|
+
<p>Selettore del dispositivo, pulsante di aggiornamento e tipo rilevato condividono la stessa riga compatta, come nel workflow del Matter Controller.</p>
|
|
6
|
+
<p>Un click o il focus sul campo dispositivo apre sempre l'elenco completo delle risorse Hue, anche quando un dispositivo è già selezionato. Digitando, l'elenco continua a essere filtrato.</p>
|
|
6
7
|
<p>Le righe di mappatura KNX mantengono GA, DPT e Nome sulla stessa linea. I campi DPT e Nome usano larghezze compatte e Nome può restringersi ulteriormente negli editor più stretti senza modificare il testo memorizzato. I valori DPT salvati vengono conservati mentre le opzioni del selettore si caricano in modo asincrono.</p>
|
|
7
8
|
<p>Funzioni supportate: luci e gruppi di luci, prese, pulsanti, Tap dial, movimento e movimento telecamera, contatti, livello luce, temperatura, umidità, scene, batteria, connettività Zigbee e aggiornamento software.</p>
|
|
8
9
|
<p>Quando non è selezionato alcun <strong>Gateway KNX</strong> (inclusi <code>none</code> e <em>Aggiungi nuovo...</em>), i campi di mappatura KNX vengono nascosti, mentre la selezione della risorsa Hue e le opzioni per il solo flow restano disponibili.</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "Funzione dispositivo",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Dispositivo Hue",
|
|
10
|
+
"hue_device_placeholder": "Cerca il dispositivo o la risorsa Hue",
|
|
11
|
+
"device_type": "Tipo dispositivo",
|
|
12
|
+
"refresh_devices": "Aggiorna dispositivi Hue",
|
|
13
|
+
"locate_device": "Localizza la luce Hue selezionata",
|
|
14
|
+
"no_devices": "Nessuna risorsa Hue trovata. Controlla Hue Bridge e premi aggiorna.",
|
|
8
15
|
"legacy_node_notice": "Questo nodo HUE legacy è deprecato. Per i nuovi flow usa il nuovo nodo HUE Controller.",
|
|
9
16
|
"migration_button": "Converti nodi HUE legacy",
|
|
10
17
|
"migration_button_title": "Converti tutti i nodi HUE legacy presenti nell'editor",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
Questo nodo controlla da KNX un endpoint Matter già abbinato. Seleziona il dispositivo Matter e l'editor rileva le sue capability, mostrando solo le mappature KNX coerenti con quell'endpoint.
|
|
5
5
|
|
|
6
|
+
Un click o il focus sul campo del dispositivo Matter apre sempre l'elenco completo degli endpoint abbinati, anche quando un dispositivo è già selezionato. Digitando, l'elenco continua a essere filtrato.
|
|
7
|
+
|
|
6
8
|
Sostituisce i nodi Matter separati non pubblicati e mantiene tutta la UI luce quando l'endpoint selezionato è una luce.
|
|
7
9
|
|
|
8
10
|
## Configurazione
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|--|--|
|
|
12
12
|
| Gateway | 选择要使用的 KNX 网关 |
|
|
13
13
|
| GA 类型 | 组地址类型。默认 **3-层级**(可输入三层组地址或名称,若已导入 ETS);**Global** 启动时从全局变量读取;**Flow** 在流程作用域读取;**$Env variable** 从环境变量读取;**通用模式(监听所有组地址)** 对所有 GA 做出响应。|
|
|
14
|
-
| Group Addr. | 需要控制的组地址。若已导入 ETS
|
|
14
|
+
| Group Addr. | 需要控制的组地址。若已导入 ETS,单击此字段或使其获得焦点会打开完整列表,即使已选择了 GA;输入文字可筛选条目。也可留空,之后通过输入消息 `msg.setConfig` 设置。|
|
|
15
15
|
| Datapoint | 与节点关联的 Datapoint。|
|
|
16
16
|
|
|
17
17
|
<br/>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateHueController">
|
|
2
2
|
<p><a href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E" target="_blank" rel="noopener noreferrer"><strong>KNX-Ultimate 视频教程(YouTube 播放列表)</strong></a> 开始前,请<a href="https://youtu.be/f0Evf2QFI7c" target="_blank" rel="noopener noreferrer">在 YouTube 上观看说明视频</a>。</p>
|
|
3
3
|
<p><strong>HUE Controller</strong> 是面向 Hue API v2 资源的统一节点。</p>
|
|
4
|
-
<p
|
|
5
|
-
<p
|
|
4
|
+
<p>选择 Hue 设备或资源后,HUE Controller 会自动检测设备类型,并打开对应的 KNX 映射、行为和节点端口编辑器。</p>
|
|
5
|
+
<p>设备选择、刷新操作和检测到的类型显示在同一紧凑行中,与 Matter Controller 的工作流程一致。</p>
|
|
6
|
+
<p>单击设备字段或使其获得焦点时,即使已经选择了设备,也会始终打开完整的 Hue 资源列表;输入文字仍可筛选列表。</p>
|
|
6
7
|
<p>KNX 映射行会将 GA、DPT 和名称保持在同一行。DPT 与名称字段采用紧凑宽度;在较窄的编辑器中,名称字段还可继续收缩,而不会更改已保存的文本。选择器选项异步加载时,已保存的 DPT 值会被保留。</p>
|
|
7
8
|
<p>支持灯和灯组、插座、按钮、Tap dial、运动和摄像机运动、接触、光照度、温度、湿度、场景、电池、Zigbee 连接和设备软件更新。</p>
|
|
8
9
|
<p>未选择<strong>KNX 网关</strong>时(包括 <code>none</code> 和“新增...”),KNX 映射字段会隐藏,而 Hue 资源选择和仅用于流程的选项仍然可用。</p>
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
},
|
|
6
6
|
"knxUltimateHueController": {
|
|
7
7
|
"device_function": "设备功能",
|
|
8
|
+
"hue_section": "Philips Hue",
|
|
9
|
+
"hue_device": "Hue 设备",
|
|
10
|
+
"hue_device_placeholder": "搜索 Hue 设备或资源",
|
|
11
|
+
"device_type": "设备类型",
|
|
12
|
+
"refresh_devices": "刷新 Hue 设备",
|
|
13
|
+
"locate_device": "定位所选 Hue 灯",
|
|
14
|
+
"no_devices": "未找到 Hue 资源。请检查 Hue Bridge 并刷新列表。",
|
|
8
15
|
"legacy_node_notice": "此旧版 HUE 节点已弃用。新流程请使用新的 HUE Controller 节点。",
|
|
9
16
|
"migration_button": "转换旧版 HUE 节点",
|
|
10
17
|
"migration_button_title": "转换编辑器中的所有旧版 HUE 节点",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Resource queries are intentionally ordered with Plug before Light. Hue plugs
|
|
4
|
+
// often expose an on/off service whose API v2 type is `light`; preferring the
|
|
5
|
+
// plug profile prevents the same resource from appearing twice or being
|
|
6
|
+
// classified as a lamp in the unified Controller picker.
|
|
7
|
+
const HUE_CONTROLLER_RESOURCE_TYPES = Object.freeze([
|
|
8
|
+
'plug',
|
|
9
|
+
'light',
|
|
10
|
+
'button',
|
|
11
|
+
'relative_rotary',
|
|
12
|
+
'motion',
|
|
13
|
+
'area_motion',
|
|
14
|
+
'camera_motion',
|
|
15
|
+
'contact',
|
|
16
|
+
'light_level',
|
|
17
|
+
'temperature',
|
|
18
|
+
'humidity',
|
|
19
|
+
'scene',
|
|
20
|
+
'device_power',
|
|
21
|
+
'zigbee_connectivity',
|
|
22
|
+
'device_software_update'
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
const normalizeHueDeviceValue = (controllerType, item) => {
|
|
26
|
+
const id = String(item?.id || item?.rid || '').trim()
|
|
27
|
+
if (id === '') return ''
|
|
28
|
+
const resourceType = String(item?.type || item?.deviceObject?.type || '').trim().toLowerCase()
|
|
29
|
+
if (controllerType === 'light') {
|
|
30
|
+
return `${id}#${resourceType === 'grouped_light' ? 'grouped_light' : 'light'}`
|
|
31
|
+
}
|
|
32
|
+
if (controllerType === 'plug') {
|
|
33
|
+
return `${id}#${resourceType || 'plug'}`
|
|
34
|
+
}
|
|
35
|
+
return id
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const buildHueControllerResourceCatalog = (resourcesByType = {}) => {
|
|
39
|
+
const catalogById = new Map()
|
|
40
|
+
|
|
41
|
+
HUE_CONTROLLER_RESOURCE_TYPES.forEach((controllerType) => {
|
|
42
|
+
const resources = Array.isArray(resourcesByType[controllerType])
|
|
43
|
+
? resourcesByType[controllerType]
|
|
44
|
+
: []
|
|
45
|
+
resources.forEach((item) => {
|
|
46
|
+
const id = String(item?.id || item?.rid || '').trim()
|
|
47
|
+
if (id === '' || id === 'error' || catalogById.has(id)) return
|
|
48
|
+
const hueDevice = normalizeHueDeviceValue(controllerType, item)
|
|
49
|
+
if (hueDevice === '') return
|
|
50
|
+
catalogById.set(id, {
|
|
51
|
+
...item,
|
|
52
|
+
id,
|
|
53
|
+
hueDevice,
|
|
54
|
+
controllerType
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return Array.from(catalogById.values()).sort((left, right) => {
|
|
60
|
+
return String(left.name || left.id).localeCompare(String(right.name || right.id), undefined, {
|
|
61
|
+
numeric: true,
|
|
62
|
+
sensitivity: 'base'
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
HUE_CONTROLLER_RESOURCE_TYPES,
|
|
69
|
+
buildHueControllerResourceCatalog,
|
|
70
|
+
normalizeHueDeviceValue
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=20.18.1"
|
|
5
5
|
},
|
|
6
|
-
"version": "6.
|
|
6
|
+
"version": "6.3.0",
|
|
7
7
|
"description": "KNX Ultimate is the most advanced KNX integration for Node-RED, providing secure KNX/IP communication, routing, ETS project import, Philips Hue, Matter Controller and Matter Bridge (control matter device via KNX and expose KNX GA via Matter), MQTT, diagnostics with AI, virtual devices, and powerful automation nodes. Build professional, reliable, and scalable smart home and building automation projects with minimal effort.",
|
|
8
8
|
"files": [
|
|
9
9
|
"nodes/",
|
|
@@ -1573,6 +1573,13 @@
|
|
|
1573
1573
|
RED.sidebar.show("help");
|
|
1574
1574
|
} catch (error) { }
|
|
1575
1575
|
onEditPrepare();
|
|
1576
|
+
// HUE Controller owns a device-first picker covering every supported
|
|
1577
|
+
// Hue API v2 resource. onEditPrepare installs the mature Light-only
|
|
1578
|
+
// autocomplete, so let the wrapper restore its unified source after
|
|
1579
|
+
// this asynchronous readiness callback completes.
|
|
1580
|
+
if (typeof node.__configureHueControllerDeviceControl === 'function') {
|
|
1581
|
+
node.__configureHueControllerDeviceControl();
|
|
1582
|
+
}
|
|
1576
1583
|
}
|
|
1577
1584
|
|
|
1578
1585
|
|