node-red-contrib-knx-ultimate 5.2.4 → 6.0.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 +21 -8
- package/README.md +10 -3
- package/img/readmemain.png +0 -0
- package/nodes/commonFunctions.js +40 -3
- package/nodes/knxUltimate-config.js +379 -335
- package/nodes/knxUltimate.html +8 -9
- package/nodes/knxUltimate.js +5 -5
- package/nodes/knxUltimateAI.js +1 -1
- package/nodes/knxUltimateHueLight.html +3 -3
- package/nodes/knxUltimateHueLight.js +1 -1
- package/nodes/knxUltimateMatterBridge.html +321 -247
- package/nodes/knxUltimateMatterBridge.js +48 -1
- package/nodes/knxUltimateMatterControllerDevice.html +2119 -0
- package/nodes/knxUltimateMatterControllerDevice.js +1701 -0
- package/nodes/locales/de/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/de/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/de/knxUltimateMatterControllerDevice.json +97 -0
- package/nodes/locales/de/matter-config.json +5 -1
- package/nodes/locales/de/matterbridge-config.json +1 -0
- package/nodes/locales/en/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/en/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/en/knxUltimateMatterControllerDevice.json +97 -0
- package/nodes/locales/en/matter-config.json +6 -1
- package/nodes/locales/en/matterbridge-config.json +1 -0
- package/nodes/locales/es/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/es/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/es/knxUltimateMatterControllerDevice.json +97 -0
- package/nodes/locales/es/matter-config.json +5 -1
- package/nodes/locales/es/matterbridge-config.json +1 -0
- package/nodes/locales/fr/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/fr/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +89 -0
- package/nodes/locales/fr/matter-config.json +5 -1
- package/nodes/locales/fr/matterbridge-config.json +1 -0
- package/nodes/locales/it/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/it/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/it/knxUltimateMatterControllerDevice.json +97 -0
- package/nodes/locales/it/matter-config.json +6 -1
- package/nodes/locales/it/matterbridge-config.json +1 -0
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +6 -2
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +14 -1
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +27 -0
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +89 -0
- package/nodes/locales/zh-CN/matter-config.json +5 -1
- package/nodes/locales/zh-CN/matterbridge-config.json +1 -0
- package/nodes/matter-config.html +58 -6
- package/nodes/matter-config.js +7 -2
- package/nodes/matterbridge-config.html +1 -0
- package/nodes/utils/lightEngines/canonicalLight.js +75 -0
- package/nodes/utils/lightEngines/hueLightEngine.js +51 -0
- package/nodes/utils/lightEngines/index.js +25 -0
- package/nodes/utils/lightEngines/knxLightCommand.js +79 -0
- package/nodes/utils/lightEngines/lightEngine.js +46 -0
- package/nodes/utils/lightEngines/matterHueShim.js +59 -0
- package/nodes/utils/lightEngines/matterLightEngine.js +117 -0
- package/nodes/utils/matterEngine.mjs +185 -14
- package/nodes/utils/matterKnxConverter.js +1 -1
- package/package.json +21 -3
- package/resources/KNXSendSnippets.js +53 -53
- package/resources/htmlUtils.js +6 -4
- package/nodes/knxUltimateMatterDevice.html +0 -632
- package/nodes/knxUltimateMatterDevice.js +0 -308
- package/nodes/locales/de/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/de/knxUltimateMatterDevice.json +0 -109
- package/nodes/locales/en/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/en/knxUltimateMatterDevice.json +0 -109
- package/nodes/locales/es/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/es/knxUltimateMatterDevice.json +0 -109
- package/nodes/locales/fr/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/fr/knxUltimateMatterDevice.json +0 -109
- package/nodes/locales/it/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/it/knxUltimateMatterDevice.json +0 -109
- package/nodes/locales/zh-CN/knxUltimateMatterDevice.html +0 -56
- package/nodes/locales/zh-CN/knxUltimateMatterDevice.json +0 -109
|
@@ -0,0 +1,2119 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
|
|
4
|
+
|
|
5
|
+
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
|
|
6
|
+
|
|
7
|
+
<script type="text/javascript">
|
|
8
|
+
(function () {
|
|
9
|
+
|
|
10
|
+
RED.nodes.registerType("knxUltimateMatterControllerDevice", {
|
|
11
|
+
category: "KNX Ultimate Matter",
|
|
12
|
+
color: "#8FE3D4",
|
|
13
|
+
defaults: {
|
|
14
|
+
//buttonState: {value: true},
|
|
15
|
+
server: { type: "knxUltimate-config", required: false },
|
|
16
|
+
serverMatter: { type: "matter-config", required: true },
|
|
17
|
+
matterNodeId: { value: "" },
|
|
18
|
+
matterEndpointId: { value: 1 },
|
|
19
|
+
matterDeviceName: { value: "" },
|
|
20
|
+
matterDeviceCapabilities: { value: "" },
|
|
21
|
+
name: { value: "" },
|
|
22
|
+
|
|
23
|
+
nameLightSwitch: { value: "" },
|
|
24
|
+
GALightSwitch: { value: "" },
|
|
25
|
+
dptLightSwitch: { value: "" },
|
|
26
|
+
|
|
27
|
+
nameLightState: { value: "" },
|
|
28
|
+
GALightState: { value: "" },
|
|
29
|
+
dptLightState: { value: "" },
|
|
30
|
+
|
|
31
|
+
nameLightDIM: { value: "" },
|
|
32
|
+
GALightDIM: { value: "" },
|
|
33
|
+
dptLightDIM: { value: "" },
|
|
34
|
+
|
|
35
|
+
// TAB Color ---------------------------
|
|
36
|
+
nameLightColor: { value: "" },
|
|
37
|
+
GALightColor: { value: "" },
|
|
38
|
+
dptLightColor: { value: "" },
|
|
39
|
+
|
|
40
|
+
nameLightColorState: { value: "" },
|
|
41
|
+
GALightColorState: { value: "" },
|
|
42
|
+
dptLightColorState: { value: "" },
|
|
43
|
+
|
|
44
|
+
// HSV H hue Color change
|
|
45
|
+
nameLightHSV_H_DIM: { value: "" },
|
|
46
|
+
GALightHSV_H_DIM: { value: "" },
|
|
47
|
+
dptLightHSV_H_DIM: { value: "" },
|
|
48
|
+
|
|
49
|
+
nameLightHSV_H_State: { value: "" },
|
|
50
|
+
GALightHSV_H_State: { value: "" },
|
|
51
|
+
dptLightHSV_H_State: { value: "" },
|
|
52
|
+
|
|
53
|
+
// HSV S saturation change
|
|
54
|
+
nameLightHSV_S_DIM: { value: "" },
|
|
55
|
+
GALightHSV_S_DIM: { value: "" },
|
|
56
|
+
dptLightHSV_S_DIM: { value: "" },
|
|
57
|
+
|
|
58
|
+
nameLightHSV_S_State: { value: "" },
|
|
59
|
+
GALightHSV_S_State: { value: "" },
|
|
60
|
+
dptLightHSV_S_State: { value: "" },
|
|
61
|
+
// -------------------------------------
|
|
62
|
+
|
|
63
|
+
nameLightKelvinDIM: { value: "" },
|
|
64
|
+
GALightKelvinDIM: { value: "" },
|
|
65
|
+
dptLightKelvinDIM: { value: "" },
|
|
66
|
+
|
|
67
|
+
nameLightKelvinPercentage: { value: "" },
|
|
68
|
+
GALightKelvinPercentage: { value: "" },
|
|
69
|
+
dptLightKelvinPercentage: { value: "" },
|
|
70
|
+
|
|
71
|
+
nameLightKelvinPercentageState: { value: "" },
|
|
72
|
+
GALightKelvinPercentageState: { value: "" },
|
|
73
|
+
dptLightKelvinPercentageState: { value: "" },
|
|
74
|
+
|
|
75
|
+
nameLightKelvin: { value: "" },
|
|
76
|
+
GALightKelvin: { value: "" },
|
|
77
|
+
dptLightKelvin: { value: "" },
|
|
78
|
+
|
|
79
|
+
nameLightKelvinState: { value: "" },
|
|
80
|
+
GALightKelvinState: { value: "" },
|
|
81
|
+
dptLightKelvinState: { value: "" },
|
|
82
|
+
|
|
83
|
+
nameLightBrightness: { value: "" },
|
|
84
|
+
GALightBrightness: { value: "" },
|
|
85
|
+
dptLightBrightness: { value: "" },
|
|
86
|
+
|
|
87
|
+
nameLightBrightnessState: { value: "" },
|
|
88
|
+
GALightBrightnessState: { value: "" },
|
|
89
|
+
dptLightBrightnessState: { value: "" },
|
|
90
|
+
|
|
91
|
+
nameLightBlink: { value: "" },
|
|
92
|
+
GALightBlink: { value: "" },
|
|
93
|
+
dptLightBlink: { value: "" },
|
|
94
|
+
|
|
95
|
+
nameLightColorCycle: { value: "" },
|
|
96
|
+
GALightColorCycle: { value: "" },
|
|
97
|
+
dptLightColorCycle: { value: "" },
|
|
98
|
+
|
|
99
|
+
nameDaylightSensor: { value: "" },
|
|
100
|
+
GADaylightSensor: { value: "" },
|
|
101
|
+
dptDaylightSensor: { value: "" },
|
|
102
|
+
|
|
103
|
+
specifySwitchOnBrightness: { value: "temperature" },
|
|
104
|
+
colorAtSwitchOnDayTime: { value: '{"kelvin":3000, "brightness":100 }' },
|
|
105
|
+
|
|
106
|
+
enableDayNightLighting: { value: "no" },
|
|
107
|
+
colorAtSwitchOnNightTime: { value: '{ "kelvin":2700, "brightness":20 }' },
|
|
108
|
+
|
|
109
|
+
invertDayNight: { value: false },
|
|
110
|
+
invertDimTunableWhiteDirection: { value: false },
|
|
111
|
+
|
|
112
|
+
updateKNXBrightnessStatusOnHUEOnOff: { value: "no" },
|
|
113
|
+
dimSpeed: { value: 5000, required: false },
|
|
114
|
+
HSVDimSpeed: { value: 5000, required: false },
|
|
115
|
+
minDimLevelLight: { value: 10, required: false },
|
|
116
|
+
maxDimLevelLight: { value: 100, required: false },
|
|
117
|
+
readStatusAtStartup: { value: "yes" },
|
|
118
|
+
enableNodePINS: { value: "no" },
|
|
119
|
+
|
|
120
|
+
outputs: { value: 0 },
|
|
121
|
+
inputs: { value: 0 },
|
|
122
|
+
|
|
123
|
+
restoreDayMode: { value: "no" }, // Starting from v 4.1.31
|
|
124
|
+
updateLocalStateFromKNXWrite: { value: true } // Starting from v 4.1.31
|
|
125
|
+
},
|
|
126
|
+
inputs: 0,
|
|
127
|
+
outputs: 0,
|
|
128
|
+
icon: "node-matter-icon.svg",
|
|
129
|
+
label: function () {
|
|
130
|
+
return this.name || this.matterDeviceName || "Control Matter from KNX (BETA)";
|
|
131
|
+
},
|
|
132
|
+
paletteLabel: "Control Matter from KNX (BETA)",
|
|
133
|
+
oneditprepare: function () {
|
|
134
|
+
// Go to the help panel
|
|
135
|
+
try {
|
|
136
|
+
RED.sidebar.show("help");
|
|
137
|
+
} catch (error) { }
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
var node = this; // Starting from v 4.1.31
|
|
141
|
+
$("#node-input-updateLocalStateFromKNXWrite").prop("checked", node.updateLocalStateFromKNXWrite === true || node.updateLocalStateFromKNXWrite === "true"); // Starting from v 4.1.31
|
|
142
|
+
const ensureConfigSelection = (selector) => {
|
|
143
|
+
if ($(selector).val() !== "_ADD_") return;
|
|
144
|
+
try {
|
|
145
|
+
$(selector).prop("selectedIndex", 0);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// Ignore UI quirks for legacy Node-RED versions
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
["#node-input-server", "#node-input-serverMatter"].forEach(ensureConfigSelection);
|
|
151
|
+
|
|
152
|
+
function ensureVerticalTabsStyle() {
|
|
153
|
+
if ($('#knxUltimateMatterControllerDeviceVerticalTabs').length) return;
|
|
154
|
+
const style = `
|
|
155
|
+
<style id="knxUltimateMatterControllerDeviceVerticalTabs">
|
|
156
|
+
.hue-vertical-tabs.ui-tabs.ui-widget.ui-widget-content.ui-corner-all {
|
|
157
|
+
display: flex;
|
|
158
|
+
border: none;
|
|
159
|
+
padding: 0;
|
|
160
|
+
}
|
|
161
|
+
.hue-vertical-tabs > ul.ui-tabs-nav {
|
|
162
|
+
flex: 0 0 180px;
|
|
163
|
+
border-right: 1px solid #ccc;
|
|
164
|
+
border-left: none;
|
|
165
|
+
border-top: none;
|
|
166
|
+
border-bottom: none;
|
|
167
|
+
padding: 0.5em 0.3em;
|
|
168
|
+
}
|
|
169
|
+
.hue-vertical-tabs > ul.ui-tabs-nav li {
|
|
170
|
+
float: none;
|
|
171
|
+
width: 100%;
|
|
172
|
+
margin: 0 0 2px 0;
|
|
173
|
+
}
|
|
174
|
+
.hue-vertical-tabs > ul.ui-tabs-nav li a {
|
|
175
|
+
display: block;
|
|
176
|
+
width: 100%;
|
|
177
|
+
white-space: normal;
|
|
178
|
+
position: relative;
|
|
179
|
+
border-bottom: none !important;
|
|
180
|
+
}
|
|
181
|
+
.hue-vertical-tabs > ul.ui-tabs-nav li.ui-tabs-active {
|
|
182
|
+
border-bottom: none !important;
|
|
183
|
+
}
|
|
184
|
+
.hue-vertical-tabs > ul.ui-tabs-nav li.ui-tabs-active a::after {
|
|
185
|
+
content: "";
|
|
186
|
+
position: absolute;
|
|
187
|
+
left: 0;
|
|
188
|
+
bottom: 0;
|
|
189
|
+
width: 50%;
|
|
190
|
+
height: 3px;
|
|
191
|
+
background: currentColor;
|
|
192
|
+
}
|
|
193
|
+
.hue-vertical-tabs .ui-tabs-panel {
|
|
194
|
+
flex: 1;
|
|
195
|
+
padding: 0.8em 1em;
|
|
196
|
+
box-sizing: border-box;
|
|
197
|
+
border: none;
|
|
198
|
+
background: transparent;
|
|
199
|
+
}
|
|
200
|
+
.hue-vertical-tabs .form-row > dt {
|
|
201
|
+
flex: 1 1 auto;
|
|
202
|
+
margin: 0;
|
|
203
|
+
}
|
|
204
|
+
.hue-vertical-tabs hr {
|
|
205
|
+
width: 100%;
|
|
206
|
+
border: 0;
|
|
207
|
+
border-top: 1px solid #ccc;
|
|
208
|
+
margin: 8px 0;
|
|
209
|
+
}
|
|
210
|
+
</style>`;
|
|
211
|
+
$('head').append(style);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function onEditPrepare() {
|
|
215
|
+
ensureVerticalTabsStyle();
|
|
216
|
+
const $knxServerInput = $("#node-input-server");
|
|
217
|
+
const KNX_EMPTY_VALUES = new Set(['', 'none', '_ADD_', '__NONE__']);
|
|
218
|
+
// Variable names kept from the Hue-born code; they now point to the Matter widgets.
|
|
219
|
+
const $hueServerInput = $("#node-input-serverMatter");
|
|
220
|
+
const $hueDeviceInput = $("#node-input-matterNodeId");
|
|
221
|
+
const $deviceNameInput = $("#node-input-matterDeviceName");
|
|
222
|
+
const $matterEndpointInput = $("#node-input-matterEndpointId");
|
|
223
|
+
const $matterCapabilitiesInput = $("#node-input-matterDeviceCapabilities");
|
|
224
|
+
const $nodeNameInput = $("#node-input-name");
|
|
225
|
+
const $refreshDevicesButton = $(".hue-refresh-devices");
|
|
226
|
+
const $locateDeviceButton = $(".hue-locate-device");
|
|
227
|
+
const $hueDevicesLoading = $(".hue-devices-loading");
|
|
228
|
+
let cachedHueDevices = Array.isArray(node._cachedHueLightDevices) ? node._cachedHueLightDevices : [];
|
|
229
|
+
node._cachedHueLightDevices = cachedHueDevices;
|
|
230
|
+
const defaultHueDevicePlaceholder = $deviceNameInput.attr('placeholder') || '';
|
|
231
|
+
if (($hueDeviceInput.val() || '').trim() === '' && node.matterNodeId) {
|
|
232
|
+
$hueDeviceInput.val(node.matterNodeId);
|
|
233
|
+
}
|
|
234
|
+
if (($matterEndpointInput.val() || '').trim() === '' && node.matterEndpointId) {
|
|
235
|
+
$matterEndpointInput.val(node.matterEndpointId);
|
|
236
|
+
}
|
|
237
|
+
if (($matterCapabilitiesInput.val() || '').trim() === '' && node.matterDeviceCapabilities) {
|
|
238
|
+
$matterCapabilitiesInput.val(node.matterDeviceCapabilities);
|
|
239
|
+
}
|
|
240
|
+
if (($nodeNameInput.val() || '').trim() === '' && node.name) {
|
|
241
|
+
$nodeNameInput.val(node.name);
|
|
242
|
+
}
|
|
243
|
+
if (($deviceNameInput.val() || '').trim() === '' && node.matterNodeId && node.name) {
|
|
244
|
+
$deviceNameInput.val(node.name);
|
|
245
|
+
}
|
|
246
|
+
let selectedMatterDeviceName = ($deviceNameInput.val() || '').trim();
|
|
247
|
+
let showingNoHueDevicesPlaceholder = false;
|
|
248
|
+
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__', '__null__', 'null', 'undefined']);
|
|
249
|
+
let locateSessionActive = false;
|
|
250
|
+
let locateAutoResetTimer = null;
|
|
251
|
+
let locatePendingRequest = null;
|
|
252
|
+
node.__stopHueLocateSession = null;
|
|
253
|
+
node.__cleanupNodeRemovalListener = null;
|
|
254
|
+
if (!node.__locateSessionInfo) node.__locateSessionInfo = null;
|
|
255
|
+
node.__hueLocateActive = false;
|
|
256
|
+
|
|
257
|
+
const clearLocateAutoReset = () => {
|
|
258
|
+
if (locateAutoResetTimer !== null) {
|
|
259
|
+
clearTimeout(locateAutoResetTimer);
|
|
260
|
+
locateAutoResetTimer = null;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const scheduleLocateAutoReset = (durationMs) => {
|
|
265
|
+
clearLocateAutoReset();
|
|
266
|
+
const ms = Number(durationMs);
|
|
267
|
+
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
268
|
+
locateAutoResetTimer = setTimeout(() => {
|
|
269
|
+
updateLocateButtonState(false);
|
|
270
|
+
}, ms);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const updateLocateButtonState = (isActive) => {
|
|
274
|
+
locateSessionActive = !!isActive;
|
|
275
|
+
node.__hueLocateActive = locateSessionActive;
|
|
276
|
+
if (!locateSessionActive) {
|
|
277
|
+
clearLocateAutoReset();
|
|
278
|
+
node.__locateSessionInfo = null;
|
|
279
|
+
}
|
|
280
|
+
if (!$locateDeviceButton.length) return;
|
|
281
|
+
const $icon = $locateDeviceButton.find('i').first();
|
|
282
|
+
if ($icon.length) {
|
|
283
|
+
$icon.removeClass('fa-stop fa-play').addClass(locateSessionActive ? 'fa fa-stop' : 'fa fa-play');
|
|
284
|
+
}
|
|
285
|
+
const title = locateSessionActive
|
|
286
|
+
? (node._('knxUltimateMatterControllerDevice.locate_stop_title') || 'Stop Hue locate')
|
|
287
|
+
: (node._('knxUltimateMatterControllerDevice.locate_start_title') || 'Locate selected Hue device');
|
|
288
|
+
$locateDeviceButton.attr('title', title);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const removeNodeRemovalListener = () => {
|
|
292
|
+
if (typeof node.__cleanupNodeRemovalListener === 'function') {
|
|
293
|
+
try {
|
|
294
|
+
RED.events.removeListener('nodes:remove', node.__cleanupNodeRemovalListener);
|
|
295
|
+
} catch (error) { /* empty */ }
|
|
296
|
+
node.__cleanupNodeRemovalListener = null;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const collectRemovedIds = (input, bucket = new Set()) => {
|
|
301
|
+
if (!input) return bucket;
|
|
302
|
+
if (Array.isArray(input)) {
|
|
303
|
+
input.forEach((entry) => collectRemovedIds(entry, bucket));
|
|
304
|
+
return bucket;
|
|
305
|
+
}
|
|
306
|
+
if (typeof input === 'string') {
|
|
307
|
+
bucket.add(input);
|
|
308
|
+
return bucket;
|
|
309
|
+
}
|
|
310
|
+
if (typeof input === 'object') {
|
|
311
|
+
if (input.id) bucket.add(input.id);
|
|
312
|
+
if (input.nodes) collectRemovedIds(input.nodes, bucket);
|
|
313
|
+
}
|
|
314
|
+
return bucket;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const handleNodeRemoved = (payload) => {
|
|
318
|
+
const ids = collectRemovedIds(payload);
|
|
319
|
+
if (ids.has(node.id) && typeof node.__stopHueLocateSession === 'function') {
|
|
320
|
+
node.__stopHueLocateSession();
|
|
321
|
+
removeNodeRemovalListener();
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
RED.events.on('nodes:remove', handleNodeRemoved);
|
|
325
|
+
node.__cleanupNodeRemovalListener = handleNodeRemoved;
|
|
326
|
+
|
|
327
|
+
const buildLocateContext = ({ allowStored = false } = {}) => {
|
|
328
|
+
const hueServerId = resolveHueServerValue({ allowStored });
|
|
329
|
+
const rawDevice = getHueDeviceValue({ allowStored });
|
|
330
|
+
if (!hueServerId || !rawDevice) return null;
|
|
331
|
+
const parts = String(rawDevice).split('#');
|
|
332
|
+
const deviceId = (parts[0] || '').trim();
|
|
333
|
+
if (deviceId === '') return null;
|
|
334
|
+
const deviceType = (parts[1] || 'light').trim() || 'light';
|
|
335
|
+
return { serverId: hueServerId, deviceId, deviceType };
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const performLocateRequest = ({ silent = false, allowStoredContext = false, action } = {}) => {
|
|
339
|
+
const context = buildLocateContext({ allowStored: allowStoredContext });
|
|
340
|
+
if (!context) {
|
|
341
|
+
if (!silent) {
|
|
342
|
+
const message = !resolveHueServerValue({ allowStored: true })
|
|
343
|
+
? (node._('knxUltimateMatterControllerDevice.locate_no_bridge') || 'Select a Hue bridge first')
|
|
344
|
+
: (node._('knxUltimateMatterControllerDevice.locate_no_device') || 'Select a Hue device first');
|
|
345
|
+
RED.notify(message, 'warning');
|
|
346
|
+
}
|
|
347
|
+
updateLocateButtonState(false);
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
if ($locateDeviceButton.length) {
|
|
351
|
+
$locateDeviceButton.prop('disabled', true);
|
|
352
|
+
}
|
|
353
|
+
const effectiveAction = action || (locateSessionActive ? 'stop' : 'start');
|
|
354
|
+
const request = $.ajax({
|
|
355
|
+
type: 'POST',
|
|
356
|
+
url: 'KNXUltimateLocateHueDevice',
|
|
357
|
+
data: {
|
|
358
|
+
serverId: context.serverId,
|
|
359
|
+
deviceId: context.deviceId,
|
|
360
|
+
deviceType: context.deviceType,
|
|
361
|
+
action: effectiveAction
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
locatePendingRequest = request;
|
|
365
|
+
if (!allowStoredContext && effectiveAction === 'start') {
|
|
366
|
+
node.__locateSessionInfo = {
|
|
367
|
+
serverId: context.serverId,
|
|
368
|
+
deviceId: context.deviceId,
|
|
369
|
+
deviceType: context.deviceType
|
|
370
|
+
};
|
|
371
|
+
} else if (!node.__locateSessionInfo && effectiveAction !== 'stop') {
|
|
372
|
+
node.__locateSessionInfo = {
|
|
373
|
+
serverId: context.serverId,
|
|
374
|
+
deviceId: context.deviceId,
|
|
375
|
+
deviceType: context.deviceType
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
request.done((response) => {
|
|
379
|
+
const statusValue = (response && typeof response.status === 'string') ? response.status.toLowerCase() : 'started';
|
|
380
|
+
let messageKey;
|
|
381
|
+
if (effectiveAction === 'stop' || statusValue === 'stopped') {
|
|
382
|
+
messageKey = 'knxUltimateMatterControllerDevice.locate_stopped';
|
|
383
|
+
node.__locateSessionInfo = null;
|
|
384
|
+
updateLocateButtonState(false);
|
|
385
|
+
clearLocateAutoReset();
|
|
386
|
+
} else if (effectiveAction === 'start' || statusValue === 'started') {
|
|
387
|
+
messageKey = 'knxUltimateMatterControllerDevice.locate_started';
|
|
388
|
+
node.__locateSessionInfo = {
|
|
389
|
+
serverId: context.serverId,
|
|
390
|
+
deviceId: context.deviceId,
|
|
391
|
+
deviceType: context.deviceType
|
|
392
|
+
};
|
|
393
|
+
updateLocateButtonState(true);
|
|
394
|
+
scheduleLocateAutoReset(response && typeof response.expiresInMs === 'number' ? response.expiresInMs : 600000);
|
|
395
|
+
} else {
|
|
396
|
+
messageKey = 'knxUltimateMatterControllerDevice.locate_success';
|
|
397
|
+
node.__locateSessionInfo = null;
|
|
398
|
+
updateLocateButtonState(false);
|
|
399
|
+
clearLocateAutoReset();
|
|
400
|
+
}
|
|
401
|
+
if (!silent) {
|
|
402
|
+
const message = node._(messageKey) || (statusValue === 'stopped' ? 'Locate stopped' : 'Locate command sent');
|
|
403
|
+
RED.notify(message, 'success');
|
|
404
|
+
}
|
|
405
|
+
}).fail((xhr) => {
|
|
406
|
+
let message;
|
|
407
|
+
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
|
|
408
|
+
message = xhr.responseJSON.error;
|
|
409
|
+
}
|
|
410
|
+
updateLocateButtonState(false);
|
|
411
|
+
clearLocateAutoReset();
|
|
412
|
+
if (!silent) {
|
|
413
|
+
RED.notify(message || (node._('knxUltimateMatterControllerDevice.locate_error') || 'Unable to locate Hue device'), 'error');
|
|
414
|
+
}
|
|
415
|
+
}).always(() => {
|
|
416
|
+
locatePendingRequest = null;
|
|
417
|
+
if ($locateDeviceButton.length) {
|
|
418
|
+
$locateDeviceButton.prop('disabled', false);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
return request;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const resolveKnxServerValue = () => {
|
|
425
|
+
const domValue = $knxServerInput.val();
|
|
426
|
+
if (domValue !== undefined && domValue !== null && domValue !== '') {
|
|
427
|
+
return domValue;
|
|
428
|
+
}
|
|
429
|
+
if (node.server !== undefined && node.server !== null) {
|
|
430
|
+
return node.server;
|
|
431
|
+
}
|
|
432
|
+
return '';
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const hasKnxServerSelected = () => {
|
|
436
|
+
const val = resolveKnxServerValue();
|
|
437
|
+
if (val === undefined || val === null) return false;
|
|
438
|
+
return !KNX_EMPTY_VALUES.has(val);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const resolveHueServerValue = ({ allowStored = false } = {}) => {
|
|
442
|
+
if ($hueServerInput.length) {
|
|
443
|
+
const domValue = $hueServerInput.val();
|
|
444
|
+
if (domValue !== undefined && domValue !== null) {
|
|
445
|
+
const trimmed = String(domValue).trim();
|
|
446
|
+
if (trimmed !== '' && !HUE_EMPTY_SERVER_VALUES.has(trimmed.toLowerCase())) return trimmed;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (node.serverMatter !== undefined && node.serverMatter !== null) {
|
|
450
|
+
const stored = String(node.serverMatter).trim();
|
|
451
|
+
if (stored !== '' && !HUE_EMPTY_SERVER_VALUES.has(stored.toLowerCase())) return stored;
|
|
452
|
+
}
|
|
453
|
+
if (allowStored && node.__locateSessionInfo && node.__locateSessionInfo.serverId) {
|
|
454
|
+
return node.__locateSessionInfo.serverId;
|
|
455
|
+
}
|
|
456
|
+
return '';
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const getHueServerConfig = () => {
|
|
460
|
+
const id = resolveHueServerValue();
|
|
461
|
+
if (id === '') return null;
|
|
462
|
+
try {
|
|
463
|
+
return RED.nodes.node(id) || null;
|
|
464
|
+
} catch (error) {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const resolveDeviceTypeSuffix = (deviceType) => {
|
|
470
|
+
const normalized = (deviceType || '').toLowerCase();
|
|
471
|
+
return normalized === 'grouped_light' ? '#grouped_light' : '#light';
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const applyHueDevicesPlaceholder = (hasDevices) => {
|
|
475
|
+
if (!$deviceNameInput.length) return;
|
|
476
|
+
if (hasDevices) {
|
|
477
|
+
if (showingNoHueDevicesPlaceholder) {
|
|
478
|
+
showingNoHueDevicesPlaceholder = false;
|
|
479
|
+
$deviceNameInput.attr('placeholder', defaultHueDevicePlaceholder);
|
|
480
|
+
}
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const message = node._('knxUltimateMatterControllerDevice.no_devices') || defaultHueDevicePlaceholder;
|
|
484
|
+
showingNoHueDevicesPlaceholder = true;
|
|
485
|
+
$deviceNameInput.attr('placeholder', message);
|
|
486
|
+
if (($deviceNameInput.val() || '').trim() === '') {
|
|
487
|
+
$deviceNameInput.val('');
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Human-readable name of a bridged Matter light: the nodeLabel attribute
|
|
492
|
+
// (Bridged Device Basic Information), then productLabel, then a pretty type.
|
|
493
|
+
const matterEndpointLabel = (ep) => {
|
|
494
|
+
var val = null;
|
|
495
|
+
(ep.clusters || []).forEach((c) => {
|
|
496
|
+
(c.attributes || []).forEach((a) => {
|
|
497
|
+
if (val === null && a.name === 'nodeLabel' && a.value != null && String(a.value).trim() !== '') val = String(a.value).trim();
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
if (val === null) {
|
|
501
|
+
(ep.clusters || []).forEach((c) => {
|
|
502
|
+
(c.attributes || []).forEach((a) => {
|
|
503
|
+
if (val === null && a.name === 'productLabel' && a.value != null && String(a.value).trim() !== '') val = String(a.value).trim();
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return val;
|
|
508
|
+
};
|
|
509
|
+
const matterTypePretty = { 'ma-extendedcolorlight': 'Colour light', 'ma-colortemperaturelight': 'Tunable white', 'ma-dimmablelight': 'Dimmable light', 'ma-onofflight': 'On/off light', 'ma-onoffpluginunit': 'Plug', 'ma-dimmablepluginunit': 'Dimmable plug' };
|
|
510
|
+
const matterPrettyType = (raw) => {
|
|
511
|
+
const key = String(raw || '').toLowerCase();
|
|
512
|
+
if (matterTypePretty[key]) return matterTypePretty[key];
|
|
513
|
+
return String(raw || '').replace(/^ma-/i, '').replace(/light$/i, ' light') || 'Light';
|
|
514
|
+
};
|
|
515
|
+
const normalizeMatterToken = (value) => String(value || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
516
|
+
const getMatterCluster = (ep, clusterId) => (ep.clusters || []).find((c) => Number(c.id) === Number(clusterId));
|
|
517
|
+
const hasMatterAttribute = (cluster, names) => {
|
|
518
|
+
const wanted = new Set(names.map(normalizeMatterToken));
|
|
519
|
+
return !!cluster && (cluster.attributes || []).some((a) => wanted.has(normalizeMatterToken(a.name)));
|
|
520
|
+
};
|
|
521
|
+
const hasMatterCommand = (cluster, names) => {
|
|
522
|
+
const wanted = new Set(names.map(normalizeMatterToken));
|
|
523
|
+
return !!cluster && (cluster.commands || []).some((cmd) => wanted.has(normalizeMatterToken(cmd.name)));
|
|
524
|
+
};
|
|
525
|
+
const matterCapabilitiesFromEndpoint = (ep) => {
|
|
526
|
+
const deviceTypes = (ep.deviceTypes || []).map(normalizeMatterToken);
|
|
527
|
+
const typeText = deviceTypes.join(' ');
|
|
528
|
+
const onOffCluster = getMatterCluster(ep, 6);
|
|
529
|
+
const levelCluster = getMatterCluster(ep, 8);
|
|
530
|
+
const colorCluster = getMatterCluster(ep, 768);
|
|
531
|
+
const hasColorTemperatureType = /colortemperature|extendedcolor/.test(typeText);
|
|
532
|
+
const hasColorType = /extendedcolor/.test(typeText);
|
|
533
|
+
const hasLevelType = /dimmable|colortemperature|extendedcolor/.test(typeText);
|
|
534
|
+
const hasColorTemperature = !!colorCluster && (
|
|
535
|
+
hasColorTemperatureType ||
|
|
536
|
+
hasMatterAttribute(colorCluster, ['colorTemperatureMireds', 'colorTempPhysicalMinMireds', 'colorTempPhysicalMaxMireds']) ||
|
|
537
|
+
hasMatterCommand(colorCluster, ['moveToColorTemperature'])
|
|
538
|
+
);
|
|
539
|
+
const hasColor = !!colorCluster && (
|
|
540
|
+
hasColorType ||
|
|
541
|
+
hasMatterCommand(colorCluster, ['moveToColor', 'moveToHue', 'moveToSaturation', 'moveToHueAndSaturation'])
|
|
542
|
+
);
|
|
543
|
+
return {
|
|
544
|
+
known: true,
|
|
545
|
+
colorDetectionVersion: 2,
|
|
546
|
+
onOff: !!onOffCluster || /onoff|dimmable|colortemperature|extendedcolor/.test(typeText),
|
|
547
|
+
level: !!levelCluster || hasLevelType,
|
|
548
|
+
colorTemperature: hasColorTemperature,
|
|
549
|
+
color: hasColor
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
const parseMatterCapabilities = (raw) => {
|
|
553
|
+
if (raw && typeof raw === 'object') {
|
|
554
|
+
if (raw.known === true && raw.color === true && raw.colorDetectionVersion !== 2) raw.color = false;
|
|
555
|
+
return raw;
|
|
556
|
+
}
|
|
557
|
+
if (typeof raw !== 'string' || raw.trim() === '') return null;
|
|
558
|
+
try {
|
|
559
|
+
const parsed = JSON.parse(raw);
|
|
560
|
+
if (parsed && parsed.known === true && parsed.color === true && parsed.colorDetectionVersion !== 2) {
|
|
561
|
+
parsed.color = false;
|
|
562
|
+
}
|
|
563
|
+
return parsed;
|
|
564
|
+
} catch (error) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
const serializeMatterCapabilities = (capabilities) => JSON.stringify(capabilities || {});
|
|
569
|
+
const getJSONPromise = (url) => new Promise((resolve, reject) => { $.getJSON(url, resolve).fail(reject); });
|
|
570
|
+
|
|
571
|
+
// Same name/contract as the Hue filter, on Matter items { value, matterNodeId, matterEndpointId }.
|
|
572
|
+
const filterHueDevices = (devices, term) => {
|
|
573
|
+
const cleaned = (term || '').replace(/exactmatch/gi, '').trim();
|
|
574
|
+
return $.map(devices, (item) => {
|
|
575
|
+
if (!item || !item.matterNodeId) return null;
|
|
576
|
+
if (cleaned !== '' && !htmlUtilsfullCSVSearch(item.value, cleaned)) return null;
|
|
577
|
+
return item;
|
|
578
|
+
});
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// Matter counterpart of the Hue device fetch: every commissioned node is
|
|
582
|
+
// expanded into its light endpoints (clusters OnOff 6 / Level 8 / Color 768),
|
|
583
|
+
// so a bridge (e.g. a Hue Bridge paired via Matter) lists each lamp.
|
|
584
|
+
const fetchHueDevices = (term, respond, { forceRefresh = false } = {}) => {
|
|
585
|
+
const matterServer = getHueServerConfig();
|
|
586
|
+
if (!matterServer) {
|
|
587
|
+
applyHueDevicesPlaceholder(true);
|
|
588
|
+
if (typeof respond === 'function') respond([]);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (!forceRefresh && Array.isArray(cachedHueDevices) && cachedHueDevices.length > 0) {
|
|
592
|
+
applyHueDevicesPlaceholder(true);
|
|
593
|
+
if (typeof respond === 'function') respond(filterHueDevices(cachedHueDevices, term));
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if ($hueDevicesLoading.length) {
|
|
597
|
+
$hueDevicesLoading.show();
|
|
598
|
+
}
|
|
599
|
+
getJSONPromise(`KNXUltimateMatterGetNodes?serverId=${encodeURIComponent(matterServer.id)}&_=${Date.now()}`)
|
|
600
|
+
.then((data) => {
|
|
601
|
+
const devices = (data && Array.isArray(data.devices)) ? data.devices : [];
|
|
602
|
+
return Promise.all(devices.map((d) =>
|
|
603
|
+
getJSONPromise(`KNXUltimateMatterGetStructure?serverId=${encodeURIComponent(matterServer.id)}&nodeId=${encodeURIComponent(d.nodeId)}&_=${Date.now()}`)
|
|
604
|
+
.then((s) => ({ device: d, endpoints: (s && Array.isArray(s.endpoints)) ? s.endpoints : [] }))
|
|
605
|
+
.catch(() => ({ device: d, endpoints: [] }))
|
|
606
|
+
));
|
|
607
|
+
})
|
|
608
|
+
.then((results) => {
|
|
609
|
+
const items = [];
|
|
610
|
+
results.forEach((entry) => {
|
|
611
|
+
const device = entry.device;
|
|
612
|
+
const lightEps = entry.endpoints.filter((ep) => Array.isArray(ep.clusters) && ep.clusters.some((c) => [6, 8, 768].indexOf(Number(c.id)) !== -1));
|
|
613
|
+
const eps = lightEps.length ? lightEps : entry.endpoints.filter((ep) => Number(ep.endpointId) !== 0);
|
|
614
|
+
const many = eps.length > 1;
|
|
615
|
+
eps.forEach((ep) => {
|
|
616
|
+
const label = matterEndpointLabel(ep);
|
|
617
|
+
const typeRaw = (Array.isArray(ep.deviceTypes) && ep.deviceTypes.length) ? ep.deviceTypes[0] : ep.name;
|
|
618
|
+
var text;
|
|
619
|
+
if (label) text = device.name + ' › ' + label;
|
|
620
|
+
else if (many) text = device.name + ' › ' + matterPrettyType(typeRaw) + ' ' + ep.endpointId;
|
|
621
|
+
else text = device.name + ' (' + matterPrettyType(typeRaw) + ')';
|
|
622
|
+
items.push({
|
|
623
|
+
value: text,
|
|
624
|
+
matterNodeId: String(device.nodeId),
|
|
625
|
+
matterEndpointId: String(ep.endpointId),
|
|
626
|
+
matterCapabilities: matterCapabilitiesFromEndpoint(ep)
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
cachedHueDevices = items;
|
|
631
|
+
node._cachedHueLightDevices = cachedHueDevices;
|
|
632
|
+
const selectedNodeId = String($hueDeviceInput.val() || node.matterNodeId || '');
|
|
633
|
+
const selectedEndpointId = String($matterEndpointInput.val() || node.matterEndpointId || '');
|
|
634
|
+
const selectedItem = items.find((item) => String(item.matterNodeId) === selectedNodeId && String(item.matterEndpointId) === selectedEndpointId);
|
|
635
|
+
if (selectedItem && selectedItem.matterCapabilities) {
|
|
636
|
+
currentMatterCapabilities = selectedItem.matterCapabilities;
|
|
637
|
+
$matterCapabilitiesInput.val(serializeMatterCapabilities(currentMatterCapabilities));
|
|
638
|
+
node.matterDeviceCapabilities = $matterCapabilitiesInput.val();
|
|
639
|
+
applyMatterCapabilities(currentMatterCapabilities);
|
|
640
|
+
}
|
|
641
|
+
applyHueDevicesPlaceholder(cachedHueDevices.length > 0);
|
|
642
|
+
if (typeof respond === 'function') respond(filterHueDevices(items, term));
|
|
643
|
+
})
|
|
644
|
+
.catch(() => {
|
|
645
|
+
cachedHueDevices = [];
|
|
646
|
+
node._cachedHueLightDevices = cachedHueDevices;
|
|
647
|
+
applyHueDevicesPlaceholder(false);
|
|
648
|
+
if (typeof respond === 'function') respond([]);
|
|
649
|
+
})
|
|
650
|
+
.then(() => {
|
|
651
|
+
if ($hueDevicesLoading.length) {
|
|
652
|
+
$hueDevicesLoading.hide();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// TIMER BLINK ####################################################
|
|
658
|
+
let blinkStatus = 2;
|
|
659
|
+
let timerBlinkBackground;
|
|
660
|
+
function blinkBackground(_elementIDwithHashAtTheBeginning) {
|
|
661
|
+
if (timerBlinkBackground !== undefined) clearInterval(timerBlinkBackground);
|
|
662
|
+
timerBlinkBackground = setInterval(() => {
|
|
663
|
+
if (isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "lightgreen");
|
|
664
|
+
if (!isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "");
|
|
665
|
+
blinkStatus += 1;
|
|
666
|
+
if (blinkStatus >= 14) {
|
|
667
|
+
clearInterval(timerBlinkBackground);
|
|
668
|
+
blinkStatus = 2;
|
|
669
|
+
$(_elementIDwithHashAtTheBeginning).css("background-color", "");
|
|
670
|
+
}
|
|
671
|
+
}, 100);
|
|
672
|
+
}
|
|
673
|
+
function isEven(n) {
|
|
674
|
+
return (n % 2 == 0);
|
|
675
|
+
}
|
|
676
|
+
// ################################################################
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
const $tabs = $("#tabs");
|
|
680
|
+
const $pinSectionRow = $("#node-input-enableNodePINS").closest('.form-row');
|
|
681
|
+
const $pinSelect = $("#node-input-enableNodePINS");
|
|
682
|
+
const $pinInfoRow = $pinSectionRow.next('.form-tips');
|
|
683
|
+
let currentMatterCapabilities = parseMatterCapabilities($matterCapabilitiesInput.val() || node.matterDeviceCapabilities);
|
|
684
|
+
$tabs.addClass('hue-vertical-tabs');
|
|
685
|
+
$tabs.tabs(); // Tabs gestione KNX
|
|
686
|
+
$tabs.find('ul').addClass('ui-tabs-nav');
|
|
687
|
+
$tabs.find('li').removeClass('ui-corner-top').addClass('ui-corner-left');
|
|
688
|
+
|
|
689
|
+
function getDPT(_dpt, _destinationWidget) {
|
|
690
|
+
// DPT Switch command
|
|
691
|
+
// ########################
|
|
692
|
+
const prefixes = Array.isArray(_dpt) ? _dpt : [_dpt];
|
|
693
|
+
$(_destinationWidget).empty();
|
|
694
|
+
if (!hasKnxServerSelected()) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const serverId = resolveKnxServerValue();
|
|
698
|
+
$.getJSON(`knxUltimateDpts?serverId=${serverId}&_=${Date.now()}`, (data) => {
|
|
699
|
+
data.forEach((dpt) => {
|
|
700
|
+
if (prefixes.some((prefix) => prefix === "" || dpt.value.startsWith(prefix))) {
|
|
701
|
+
// Adjustment for HUE Temperature
|
|
702
|
+
if (dpt.value.startsWith("7.600")) {
|
|
703
|
+
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - KNX Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute)"));
|
|
704
|
+
} else if (dpt.value.startsWith("9.002")) {
|
|
705
|
+
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - HUE Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute_float)"));
|
|
706
|
+
} else if (dpt.value.startsWith("5.001")) {
|
|
707
|
+
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - Homeassistant color_temperature_mode: relative"));
|
|
708
|
+
} else {
|
|
709
|
+
$(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text));
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
// Eval
|
|
714
|
+
const format = "node." + _destinationWidget.replace("#node-input-", "");
|
|
715
|
+
try {
|
|
716
|
+
if (format !== undefined) $(_destinationWidget).val(eval(format).toString());
|
|
717
|
+
} catch (error) { }
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function getGroupAddress(_sourceWidgetAutocomplete, _destinationWidgetName, _destinationWidgetDPT, _additionalSearchTerm) {
|
|
722
|
+
$(_sourceWidgetAutocomplete).autocomplete({
|
|
723
|
+
minLength: 0,
|
|
724
|
+
source: function (request, response) {
|
|
725
|
+
if (!hasKnxServerSelected()) {
|
|
726
|
+
response([]);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
const serverId = resolveKnxServerValue();
|
|
730
|
+
$.getJSON(`knxUltimatecsv?nodeID=${serverId}&_=${Date.now()}`, (data) => {
|
|
731
|
+
response(
|
|
732
|
+
$.map(data, function (value, key) {
|
|
733
|
+
var sSearch = value.ga + " (" + value.devicename + ") DPT" + value.dpt;
|
|
734
|
+
for (let index = 0; index < _additionalSearchTerm.length; index++) {
|
|
735
|
+
const sDPT = _additionalSearchTerm[index];
|
|
736
|
+
if (htmlUtilsfullCSVSearch(sSearch, request.term + " " + sDPT)) {
|
|
737
|
+
return {
|
|
738
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
739
|
+
value: value.ga, // Value
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
})
|
|
744
|
+
);
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
select: function (event, ui) {
|
|
748
|
+
// Sets Datapoint and device name automatically
|
|
749
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
750
|
+
try {
|
|
751
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
752
|
+
} catch (error) { }
|
|
753
|
+
$(_destinationWidgetName).val(sDevName);
|
|
754
|
+
var optVal = $(_destinationWidgetDPT + " option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr("value");
|
|
755
|
+
const $dptSelect = $(_destinationWidgetDPT);
|
|
756
|
+
if (optVal !== undefined && optVal !== null) {
|
|
757
|
+
$dptSelect.val(optVal).trigger('change');
|
|
758
|
+
} else {
|
|
759
|
+
// Ensure downstream widgets refresh even when the DPT is missing
|
|
760
|
+
$dptSelect.trigger('change');
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
}).focus(function () {
|
|
764
|
+
$(this).autocomplete('search', $(this).val() + 'exactmatch');
|
|
765
|
+
});
|
|
766
|
+
try {
|
|
767
|
+
if (hasKnxServerSelected()) {
|
|
768
|
+
const srv = RED.nodes.node(resolveKnxServerValue());
|
|
769
|
+
if (srv && srv.id) KNX_enableSecureFormatting($(_sourceWidgetAutocomplete), srv.id);
|
|
770
|
+
}
|
|
771
|
+
} catch (e) { }
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
const refreshKnxBindings = () => {
|
|
775
|
+
getDPT("1.", "#node-input-dptLightSwitch");
|
|
776
|
+
getGroupAddress("#node-input-GALightSwitch", "#node-input-nameLightSwitch", "#node-input-dptLightSwitch", ["1."]);
|
|
777
|
+
|
|
778
|
+
getDPT("1.", "#node-input-dptLightState");
|
|
779
|
+
getGroupAddress("#node-input-GALightState", "#node-input-nameLightState", "#node-input-dptLightState", ["1."]);
|
|
780
|
+
|
|
781
|
+
getDPT("3.007", "#node-input-dptLightDIM");
|
|
782
|
+
getGroupAddress("#node-input-GALightDIM", "#node-input-nameLightDIM", "#node-input-dptLightDIM", ["3.007"]);
|
|
783
|
+
|
|
784
|
+
getDPT("5.001", "#node-input-dptLightBrightness");
|
|
785
|
+
getGroupAddress("#node-input-GALightBrightness", "#node-input-nameLightBrightness", "#node-input-dptLightBrightness", ["5.001"]);
|
|
786
|
+
|
|
787
|
+
getDPT("5.001", "#node-input-dptLightBrightnessState");
|
|
788
|
+
getGroupAddress("#node-input-GALightBrightnessState", "#node-input-nameLightBrightnessState", "#node-input-dptLightBrightnessState", ["5.001"]);
|
|
789
|
+
|
|
790
|
+
getDPT("232.600", "#node-input-dptLightColor");
|
|
791
|
+
getGroupAddress("#node-input-GALightColor", "#node-input-nameLightColor", "#node-input-dptLightColor", ["232.600"]);
|
|
792
|
+
|
|
793
|
+
getDPT("232.600", "#node-input-dptLightColorState");
|
|
794
|
+
getGroupAddress("#node-input-GALightColorState", "#node-input-nameLightColorState", "#node-input-dptLightColorState", ["232.600"]);
|
|
795
|
+
|
|
796
|
+
getDPT("3.007", "#node-input-dptLightKelvinDIM");
|
|
797
|
+
getGroupAddress("#node-input-GALightKelvinDIM", "#node-input-nameLightKelvinDIM", "#node-input-dptLightKelvinDIM", ["3.007"]);
|
|
798
|
+
|
|
799
|
+
getDPT("5.001", "#node-input-dptLightKelvinPercentage");
|
|
800
|
+
getGroupAddress("#node-input-GALightKelvinPercentage", "#node-input-nameLightKelvinPercentage", "#node-input-dptLightKelvinPercentage", ["5.001"]);
|
|
801
|
+
|
|
802
|
+
getDPT("5.001", "#node-input-dptLightKelvinPercentageState");
|
|
803
|
+
getGroupAddress("#node-input-GALightKelvinPercentageState", "#node-input-nameLightKelvinPercentageState", "#node-input-dptLightKelvinPercentageState", ["5.001"]);
|
|
804
|
+
|
|
805
|
+
getDPT("1.", "#node-input-dptLightBlink");
|
|
806
|
+
getGroupAddress("#node-input-GALightBlink", "#node-input-nameLightBlink", "#node-input-dptLightBlink", ["1."]);
|
|
807
|
+
|
|
808
|
+
getDPT("1.", "#node-input-dptLightColorCycle");
|
|
809
|
+
getGroupAddress("#node-input-GALightColorCycle", "#node-input-nameLightColorCycle", "#node-input-dptLightColorCycle", ["1."]);
|
|
810
|
+
|
|
811
|
+
getDPT("1.", "#node-input-dptDaylightSensor");
|
|
812
|
+
getGroupAddress("#node-input-GADaylightSensor", "#node-input-nameDaylightSensor", "#node-input-dptDaylightSensor", ["1."]);
|
|
813
|
+
|
|
814
|
+
getDPT("7.600", "#node-input-dptLightKelvin");
|
|
815
|
+
getDPT("9.002", "#node-input-dptLightKelvin");
|
|
816
|
+
getDPT("9.002", "#node-input-dptLightKelvinState");
|
|
817
|
+
getDPT("7.600", "#node-input-dptLightKelvinState");
|
|
818
|
+
getGroupAddress("#node-input-GALightKelvin", "#node-input-nameLightKelvin", "#node-input-dptLightKelvin", ["7.600", "9.002"]);
|
|
819
|
+
getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", ["7.600", "9.002"]);
|
|
820
|
+
|
|
821
|
+
// HSV ----------------------
|
|
822
|
+
// H
|
|
823
|
+
getDPT("3.007", "#node-input-dptLightHSV_H_DIM");
|
|
824
|
+
getGroupAddress("#node-input-GALightHSV_H_DIM", "#node-input-nameLightHSV_H_DIM", "#node-input-dptLightHSV_H_DIM", ["3.007"]);
|
|
825
|
+
|
|
826
|
+
getDPT("5.001", "#node-input-dptLightHSV_H_State");
|
|
827
|
+
getGroupAddress("#node-input-GALightHSV_H_State", "#node-input-nameLightHSV_H_State", "#node-input-dptLightHSV_H_State", ["5.001"]);
|
|
828
|
+
|
|
829
|
+
// S
|
|
830
|
+
getDPT("3.007", "#node-input-dptLightHSV_S_DIM");
|
|
831
|
+
getGroupAddress("#node-input-GALightHSV_S_DIM", "#node-input-nameLightHSV_S_DIM", "#node-input-dptLightHSV_S_DIM", ["3.007"]);
|
|
832
|
+
|
|
833
|
+
getDPT("5.001", "#node-input-dptLightHSV_S_State");
|
|
834
|
+
getGroupAddress("#node-input-GALightHSV_S_State", "#node-input-nameLightHSV_S_State", "#node-input-dptLightHSV_S_State", ["5.001"]);
|
|
835
|
+
|
|
836
|
+
// V
|
|
837
|
+
getDPT("3.007", "#node-input-dptLightHSV_V_DIM");
|
|
838
|
+
getGroupAddress("#node-input-GALightHSV_V_DIM", "#node-input-nameLightHSV_V_DIM", "#node-input-dptLightHSV_V_DIM", ["3.007"]);
|
|
839
|
+
|
|
840
|
+
getDPT("5.001", "#node-input-dptLightHSV_V_State");
|
|
841
|
+
getGroupAddress("#node-input-GALightHSV_V_State", "#node-input-nameLightHSV_V_State", "#node-input-dptLightHSV_V_State", ["5.001"]);
|
|
842
|
+
// END HSV ----------------------
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
refreshKnxBindings();
|
|
846
|
+
|
|
847
|
+
const getHueDeviceValue = ({ allowStored = false } = {}) => {
|
|
848
|
+
if ($hueDeviceInput.length) {
|
|
849
|
+
const domValue = $hueDeviceInput.val();
|
|
850
|
+
if (domValue !== undefined && domValue !== null && domValue !== '') {
|
|
851
|
+
return domValue;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (node.matterNodeId !== undefined && node.matterNodeId !== null && node.matterNodeId !== '') {
|
|
855
|
+
return node.matterNodeId;
|
|
856
|
+
}
|
|
857
|
+
return '';
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
const getEffectiveMatterCapabilities = () => {
|
|
861
|
+
return currentMatterCapabilities || {
|
|
862
|
+
known: false,
|
|
863
|
+
onOff: true,
|
|
864
|
+
level: true,
|
|
865
|
+
colorTemperature: true,
|
|
866
|
+
color: true
|
|
867
|
+
};
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
const setMatterTabVisible = (tabId, visible) => {
|
|
871
|
+
const $link = $tabs.find(`a[href="#${tabId}"]`);
|
|
872
|
+
$link.closest('li').toggle(!!visible);
|
|
873
|
+
$(`#${tabId}`).toggle(!!visible);
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const ensureVisibleMatterTabSelected = () => {
|
|
877
|
+
const $active = $tabs.find('ul.ui-tabs-nav li.ui-tabs-active');
|
|
878
|
+
if ($active.length && $active.is(':visible')) return;
|
|
879
|
+
const $firstVisible = $tabs.find('ul.ui-tabs-nav li:visible').first();
|
|
880
|
+
if (!$firstVisible.length) return;
|
|
881
|
+
const visibleIndex = $tabs.find('ul.ui-tabs-nav li').index($firstVisible);
|
|
882
|
+
if (visibleIndex >= 0) {
|
|
883
|
+
try { $tabs.tabs('option', 'active', visibleIndex); } catch (error) { /* empty */ }
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const restrictSelectOptions = ($select, allowedValues, fallbackValue) => {
|
|
888
|
+
$select.find('option').each(function () {
|
|
889
|
+
const allowed = allowedValues.indexOf($(this).val()) !== -1;
|
|
890
|
+
$(this).prop('disabled', !allowed).toggle(allowed);
|
|
891
|
+
});
|
|
892
|
+
if (allowedValues.indexOf($select.val()) === -1) {
|
|
893
|
+
$select.val(fallbackValue);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const refreshBehaviourVisibility = () => {
|
|
898
|
+
const switchOnMode = $("#node-input-specifySwitchOnBrightness").val();
|
|
899
|
+
if (switchOnMode === "yes") {
|
|
900
|
+
$("#divColorsAtSwitchOn").show();
|
|
901
|
+
$("#divTemperatureAtSwitchOn").hide();
|
|
902
|
+
} else if (switchOnMode === "temperature") {
|
|
903
|
+
$("#divColorsAtSwitchOn").hide();
|
|
904
|
+
$("#divTemperatureAtSwitchOn").show();
|
|
905
|
+
} else {
|
|
906
|
+
$("#divColorsAtSwitchOn").hide();
|
|
907
|
+
$("#divTemperatureAtSwitchOn").hide();
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const dayNightMode = $("#node-input-enableDayNightLighting").val();
|
|
911
|
+
if (dayNightMode === "yes") {
|
|
912
|
+
$("#divEnableDayNightLighting").show();
|
|
913
|
+
$("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" });
|
|
914
|
+
$("#divColorsAtSwitchOnNightTime").show();
|
|
915
|
+
$("#divTemperatureAtSwitchOnNightTime").hide();
|
|
916
|
+
} else if (dayNightMode === "temperature") {
|
|
917
|
+
$("#divEnableDayNightLighting").show();
|
|
918
|
+
$("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" });
|
|
919
|
+
$("#divColorsAtSwitchOnNightTime").hide();
|
|
920
|
+
$("#divTemperatureAtSwitchOnNightTime").show();
|
|
921
|
+
} else {
|
|
922
|
+
$("#divEnableDayNightLighting").hide();
|
|
923
|
+
$("#divCCSBoxAtNightLighting").css({ border: "", "border-radius": "", padding: "" });
|
|
924
|
+
$("#divColorsAtSwitchOnNightTime").hide();
|
|
925
|
+
$("#divTemperatureAtSwitchOnNightTime").hide();
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const applyMatterCapabilities = (capabilities) => {
|
|
930
|
+
currentMatterCapabilities = capabilities || null;
|
|
931
|
+
const caps = getEffectiveMatterCapabilities();
|
|
932
|
+
const hasHueDevice = getHueDeviceValue() !== '';
|
|
933
|
+
const supportsSwitch = hasHueDevice && caps.onOff !== false;
|
|
934
|
+
const supportsDim = hasHueDevice && caps.level !== false;
|
|
935
|
+
const supportsTemperature = hasHueDevice && caps.colorTemperature === true;
|
|
936
|
+
const supportsColor = hasHueDevice && caps.color === true;
|
|
937
|
+
const hasRichBehaviour = supportsDim && (supportsTemperature || supportsColor);
|
|
938
|
+
|
|
939
|
+
setMatterTabVisible('tabs-1', supportsSwitch);
|
|
940
|
+
setMatterTabVisible('tabs-2', supportsDim);
|
|
941
|
+
setMatterTabVisible('tabs-3', supportsTemperature);
|
|
942
|
+
setMatterTabVisible('tabs-4', supportsColor);
|
|
943
|
+
setMatterTabVisible('tabs-6', hasHueDevice);
|
|
944
|
+
try { $tabs.tabs('refresh'); } catch (error) { /* empty */ }
|
|
945
|
+
ensureVisibleMatterTabSelected();
|
|
946
|
+
|
|
947
|
+
$("#divUpdateKNXBrightnessStatusOnHUEOnOff").toggle(supportsDim);
|
|
948
|
+
$("#divBehaviourBrightness").toggle(hasRichBehaviour);
|
|
949
|
+
const switchOnAllowed = ['no'];
|
|
950
|
+
const nightAllowed = ['no'];
|
|
951
|
+
if (supportsColor) {
|
|
952
|
+
switchOnAllowed.push('yes');
|
|
953
|
+
nightAllowed.push('yes');
|
|
954
|
+
}
|
|
955
|
+
if (supportsTemperature) {
|
|
956
|
+
switchOnAllowed.push('temperature');
|
|
957
|
+
nightAllowed.push('temperature');
|
|
958
|
+
}
|
|
959
|
+
restrictSelectOptions($("#node-input-specifySwitchOnBrightness"), switchOnAllowed, 'no');
|
|
960
|
+
restrictSelectOptions($("#node-input-enableDayNightLighting"), nightAllowed, 'no');
|
|
961
|
+
refreshBehaviourVisibility();
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
const updateTabsVisibility = () => {
|
|
965
|
+
const knxSelected = hasKnxServerSelected();
|
|
966
|
+
const hueDeviceSelected = getHueDeviceValue() !== '';
|
|
967
|
+
const shouldShowTabs = knxSelected && hueDeviceSelected;
|
|
968
|
+
|
|
969
|
+
if (shouldShowTabs) {
|
|
970
|
+
$tabs.show();
|
|
971
|
+
try { $tabs.tabs('refresh'); } catch (error) { /* empty */ }
|
|
972
|
+
} else {
|
|
973
|
+
$tabs.hide();
|
|
974
|
+
}
|
|
975
|
+
applyMatterCapabilities(currentMatterCapabilities);
|
|
976
|
+
|
|
977
|
+
if ($pinSelect.length) {
|
|
978
|
+
const desiredPins = knxSelected ? 'no' : 'yes';
|
|
979
|
+
if ($pinSelect.val() !== desiredPins) {
|
|
980
|
+
$pinSelect.val(desiredPins).trigger('change');
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if ($pinSectionRow.length) {
|
|
985
|
+
$pinSectionRow.show();
|
|
986
|
+
}
|
|
987
|
+
if ($pinInfoRow.length) {
|
|
988
|
+
$pinInfoRow.show();
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
updateTabsVisibility();
|
|
992
|
+
|
|
993
|
+
$knxServerInput.on('change.knxUltimateMatterControllerDevice', () => {
|
|
994
|
+
refreshKnxBindings();
|
|
995
|
+
updateTabsVisibility();
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
$hueDeviceInput.on('change.knxUltimateMatterControllerDevice input.knxUltimateMatterControllerDevice', updateTabsVisibility);
|
|
999
|
+
|
|
1000
|
+
if (($deviceNameInput.val() || '').trim() !== '') {
|
|
1001
|
+
applyHueDevicesPlaceholder(true);
|
|
1002
|
+
} else {
|
|
1003
|
+
applyHueDevicesPlaceholder(cachedHueDevices.length > 0);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if ($deviceNameInput.length) {
|
|
1007
|
+
if ($deviceNameInput.data('ui-autocomplete')) {
|
|
1008
|
+
try { $deviceNameInput.autocomplete('destroy'); } catch (error) { /* empty */ }
|
|
1009
|
+
}
|
|
1010
|
+
$deviceNameInput.autocomplete({
|
|
1011
|
+
minLength: 0,
|
|
1012
|
+
source(request, response) {
|
|
1013
|
+
fetchHueDevices(request.term, response);
|
|
1014
|
+
},
|
|
1015
|
+
select(event, ui) {
|
|
1016
|
+
if (!ui.item || !ui.item.matterNodeId) {
|
|
1017
|
+
event.preventDefault();
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
$deviceNameInput.val(ui.item.value || '');
|
|
1021
|
+
selectedMatterDeviceName = ui.item.value || '';
|
|
1022
|
+
node.matterDeviceName = selectedMatterDeviceName;
|
|
1023
|
+
node.name = selectedMatterDeviceName || node.name;
|
|
1024
|
+
$nodeNameInput.val(node.name);
|
|
1025
|
+
$hueDeviceInput.val(ui.item.matterNodeId);
|
|
1026
|
+
node.matterNodeId = ui.item.matterNodeId;
|
|
1027
|
+
$matterEndpointInput.val(ui.item.matterEndpointId);
|
|
1028
|
+
node.matterEndpointId = ui.item.matterEndpointId;
|
|
1029
|
+
currentMatterCapabilities = ui.item.matterCapabilities || null;
|
|
1030
|
+
$matterCapabilitiesInput.val(currentMatterCapabilities ? serializeMatterCapabilities(currentMatterCapabilities) : '');
|
|
1031
|
+
node.matterDeviceCapabilities = $matterCapabilitiesInput.val();
|
|
1032
|
+
applyMatterCapabilities(currentMatterCapabilities);
|
|
1033
|
+
updateTabsVisibility();
|
|
1034
|
+
setTimeout(() => {
|
|
1035
|
+
try { $deviceNameInput.autocomplete('close'); } catch (error) { /* empty */ }
|
|
1036
|
+
}, 0);
|
|
1037
|
+
},
|
|
1038
|
+
focus(event, ui) {
|
|
1039
|
+
event.preventDefault();
|
|
1040
|
+
}
|
|
1041
|
+
}).focus(function () {
|
|
1042
|
+
$(this).autocomplete('search', `${$(this).val()}exactmatch`);
|
|
1043
|
+
}).on('input.knxUltimateMatterControllerDevice', function () {
|
|
1044
|
+
const typedValue = $(this).val().trim();
|
|
1045
|
+
if (typedValue === '' || typedValue !== selectedMatterDeviceName) {
|
|
1046
|
+
$hueDeviceInput.val('');
|
|
1047
|
+
$matterEndpointInput.val('');
|
|
1048
|
+
$matterCapabilitiesInput.val('');
|
|
1049
|
+
node.matterNodeId = '';
|
|
1050
|
+
node.matterEndpointId = '';
|
|
1051
|
+
node.matterDeviceCapabilities = '';
|
|
1052
|
+
currentMatterCapabilities = null;
|
|
1053
|
+
updateTabsVisibility();
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if ($refreshDevicesButton.length) {
|
|
1059
|
+
$refreshDevicesButton.off('.knxUltimateMatterControllerDevice').on('click.knxUltimateMatterControllerDevice', () => {
|
|
1060
|
+
cachedHueDevices = [];
|
|
1061
|
+
node._cachedHueLightDevices = cachedHueDevices;
|
|
1062
|
+
fetchHueDevices('', () => {
|
|
1063
|
+
if ($deviceNameInput.length) {
|
|
1064
|
+
$deviceNameInput.autocomplete('search', `${$deviceNameInput.val()}exactmatch`);
|
|
1065
|
+
}
|
|
1066
|
+
}, { forceRefresh: true });
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
if ($locateDeviceButton.length) {
|
|
1071
|
+
$locateDeviceButton.off('.knxUltimateMatterControllerDevice').on('click.knxUltimateMatterControllerDevice', () => {
|
|
1072
|
+
const desiredAction = locateSessionActive ? 'stop' : 'start';
|
|
1073
|
+
performLocateRequest({ silent: false, action: desiredAction });
|
|
1074
|
+
});
|
|
1075
|
+
updateLocateButtonState(false);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if ($hueServerInput.length) {
|
|
1079
|
+
$hueServerInput.off('.knxUltimateMatterControllerDeviceDevices').on('change.knxUltimateMatterControllerDeviceDevices', () => {
|
|
1080
|
+
cachedHueDevices = [];
|
|
1081
|
+
node._cachedHueLightDevices = cachedHueDevices;
|
|
1082
|
+
showingNoHueDevicesPlaceholder = false;
|
|
1083
|
+
applyHueDevicesPlaceholder(true);
|
|
1084
|
+
node.__locateSessionInfo = null;
|
|
1085
|
+
updateLocateButtonState(false);
|
|
1086
|
+
if ($hueDevicesLoading.length) {
|
|
1087
|
+
$hueDevicesLoading.hide();
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
node.__stopHueLocateSession = () => {
|
|
1093
|
+
clearLocateAutoReset();
|
|
1094
|
+
removeNodeRemovalListener();
|
|
1095
|
+
const pending = locatePendingRequest;
|
|
1096
|
+
if (pending && typeof pending.always === 'function') {
|
|
1097
|
+
pending.always(() => {
|
|
1098
|
+
if (node.__locateSessionInfo || locateSessionActive) {
|
|
1099
|
+
const followUp = performLocateRequest({ silent: true, allowStoredContext: true, action: 'stop' });
|
|
1100
|
+
if (!followUp || typeof followUp.then !== 'function') {
|
|
1101
|
+
updateLocateButtonState(false);
|
|
1102
|
+
}
|
|
1103
|
+
} else {
|
|
1104
|
+
updateLocateButtonState(false);
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
if (locateSessionActive || (node.__locateSessionInfo && node.__locateSessionInfo.deviceId)) {
|
|
1110
|
+
const request = performLocateRequest({ silent: true, allowStoredContext: true, action: 'stop' });
|
|
1111
|
+
if (!request || typeof request.then !== 'function') {
|
|
1112
|
+
updateLocateButtonState(false);
|
|
1113
|
+
}
|
|
1114
|
+
} else {
|
|
1115
|
+
updateLocateButtonState(false);
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// Matter lights: expose the full switch-on options statically. The runtime
|
|
1120
|
+
// sends only what the device supports; unsupported commands are ignored.
|
|
1121
|
+
const populateSwitchOnCombos = () => {
|
|
1122
|
+
$("#node-input-specifySwitchOnBrightness").empty()
|
|
1123
|
+
.append($("<option>").val("no").text(node._("knxUltimateMatterControllerDevice.none")))
|
|
1124
|
+
.append($("<option>").val("yes").text(node._("knxUltimateMatterControllerDevice.select_color")))
|
|
1125
|
+
.append($("<option>").val("temperature").text(node._("knxUltimateMatterControllerDevice.select_temperature_brightness")));
|
|
1126
|
+
$("#node-input-enableDayNightLighting").empty()
|
|
1127
|
+
.append($("<option>").val("no").text(node._("knxUltimateMatterControllerDevice.opt_no")))
|
|
1128
|
+
.append($("<option>").val("yes").text(node._("knxUltimateMatterControllerDevice.select_color")))
|
|
1129
|
+
.append($("<option>").val("temperature").text(node._("knxUltimateMatterControllerDevice.select_temperature_brightness")));
|
|
1130
|
+
$("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness);
|
|
1131
|
+
$("#node-input-enableDayNightLighting").val(node.enableDayNightLighting);
|
|
1132
|
+
// "Get current" reads the state from the Hue bridge: not available on Matter.
|
|
1133
|
+
$("#getColorAtSwitchOnDayTimeButton").hide();
|
|
1134
|
+
$("#getColorAtSwitchOnNightTimeButton").hide();
|
|
1135
|
+
applyMatterCapabilities(currentMatterCapabilities);
|
|
1136
|
+
};
|
|
1137
|
+
populateSwitchOnCombos();
|
|
1138
|
+
|
|
1139
|
+
const initialHueDeviceRaw = getHueDeviceValue();
|
|
1140
|
+
if (initialHueDeviceRaw === '') {
|
|
1141
|
+
updateTabsVisibility();
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
$hueDeviceInput.val(initialHueDeviceRaw);
|
|
1145
|
+
updateTabsVisibility();
|
|
1146
|
+
fetchHueDevices('', () => { }, { forceRefresh: true });
|
|
1147
|
+
$("#node-input-specifySwitchOnBrightness").on("change", function () {
|
|
1148
|
+
refreshBehaviourVisibility();
|
|
1149
|
+
if ($("#node-input-specifySwitchOnBrightness").val() === "yes") blinkBackground("#colorPickerDay");
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
$("#node-input-enableDayNightLighting").on("change", function () {
|
|
1153
|
+
refreshBehaviourVisibility();
|
|
1154
|
+
if ($("#node-input-enableDayNightLighting").val() === "yes") blinkBackground("#colorPickerNight");
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
// "Get current" (reading the live state from the Hue bridge) does not exist on
|
|
1158
|
+
// Matter: the buttons are hidden by populateSwitchOnCombos above.
|
|
1159
|
+
|
|
1160
|
+
// Fill options for minDimLevel and maxDimLevel and comboBrightnessAtSwitchOn (for color brightness at switch on, with temperature toghedher)
|
|
1161
|
+
for (let index = 100; index >= 0; index -= 5) {
|
|
1162
|
+
if (index === 0) {
|
|
1163
|
+
$("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "% " + node._("knxUltimateMatterControllerDevice.switch_off")));
|
|
1164
|
+
$("#comboBrightnessAtSwitchOn").append($("<option>").val(index).text(index.toString() + "% " + node._("knxUltimateMatterControllerDevice.switch_off")));
|
|
1165
|
+
$("#comboBrightnessAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "% " + node._("knxUltimateMatterControllerDevice.switch_off")));
|
|
1166
|
+
} else {
|
|
1167
|
+
$("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "%"));
|
|
1168
|
+
$("#comboBrightnessAtSwitchOn").append($("<option>").val(index).text(index.toString() + "%"));
|
|
1169
|
+
$("#comboBrightnessAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "%"));
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
// Temperatures, from 2000 to 6535K (circa)
|
|
1173
|
+
for (let index = 2000; index <= 6500; index += 100) {
|
|
1174
|
+
if (index === 2200) {
|
|
1175
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_2200")));
|
|
1176
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_2200")));
|
|
1177
|
+
} else if (index === 2700) {
|
|
1178
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_2700")));
|
|
1179
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_2700")));
|
|
1180
|
+
} else if (index === 3000) {
|
|
1181
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_3000")));
|
|
1182
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_3000")));
|
|
1183
|
+
} else if (index === 3500) {
|
|
1184
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_3500_day")));
|
|
1185
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_3500_night")));
|
|
1186
|
+
} else if (index === 4100) {
|
|
1187
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_4100_day")));
|
|
1188
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_4100_night")));
|
|
1189
|
+
} else if (index === 5000) {
|
|
1190
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_5000_day")));
|
|
1191
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_5000_night")));
|
|
1192
|
+
} else if (index === 6500) {
|
|
1193
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_6500_day")));
|
|
1194
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix") + " " + node._("knxUltimateMatterControllerDevice.temp_desc_6500_night")));
|
|
1195
|
+
} else {
|
|
1196
|
+
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix")));
|
|
1197
|
+
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateMatterControllerDevice.k_suffix")));
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
// Calculate kelvin/color
|
|
1203
|
+
let json;
|
|
1204
|
+
node.colorAtSwitchOnDayTime = node.colorAtSwitchOnDayTime.replace("geen", "green"); // Old bug in "geen" property
|
|
1205
|
+
node.colorAtSwitchOnNightTime = node.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property
|
|
1206
|
+
try {
|
|
1207
|
+
json = JSON.parse(node.colorAtSwitchOnDayTime);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
console.log("json = JSON.parse(node.colorAtSwitchOnDayTime) in HTML: " + error.message)
|
|
1210
|
+
}
|
|
1211
|
+
if (json !== undefined && json.kelvin !== undefined) {
|
|
1212
|
+
// Kelvin
|
|
1213
|
+
$("#comboTemperatureAtSwitchOn").val(json.kelvin);
|
|
1214
|
+
$("#comboBrightnessAtSwitchOn").val(json.brightness);
|
|
1215
|
+
if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('temperature'); // Adjust in case of mismatch (from old geen bug)
|
|
1216
|
+
} else if (json !== undefined && json.red !== undefined) {
|
|
1217
|
+
// Must transform RGB into HTML HEX color
|
|
1218
|
+
try {
|
|
1219
|
+
$("#node-input-colorAtSwitchOnDayTime").val("#" + rgbHex(json.red, json.green, json.blue));
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
}
|
|
1222
|
+
$("#colorPickerDay").val($("#node-input-colorAtSwitchOnDayTime").val());
|
|
1223
|
+
if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('yes'); // Adjust in case of mismatch (from old geen bug)
|
|
1224
|
+
} else {
|
|
1225
|
+
// It's already an HEX color
|
|
1226
|
+
$("#colorPickerDay").val(node.colorAtSwitchOnDayTime);
|
|
1227
|
+
if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('yes'); // Adjust in case of mismatch (from old geen bug)
|
|
1228
|
+
}
|
|
1229
|
+
//Night
|
|
1230
|
+
json = undefined;
|
|
1231
|
+
try {
|
|
1232
|
+
json = JSON.parse(node.colorAtSwitchOnNightTime);
|
|
1233
|
+
} catch (error) { }
|
|
1234
|
+
if (json !== undefined && json.kelvin !== undefined) {
|
|
1235
|
+
// Kelvin
|
|
1236
|
+
$("#comboTemperatureAtSwitchOnNightTime").val(json.kelvin);
|
|
1237
|
+
$("#comboBrightnessAtSwitchOnNightTime").val(json.brightness);
|
|
1238
|
+
if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('temperature'); // Adjust in case of mismatch (from old geen bug)
|
|
1239
|
+
} else if (json !== undefined && json.red !== undefined) {
|
|
1240
|
+
// Must transform RGB into HTML HEX color
|
|
1241
|
+
try {
|
|
1242
|
+
$("#node-input-colorAtSwitchOnNightTime").val("#" + rgbHex(json.red, json.green, json.blue));
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
}
|
|
1245
|
+
$("#colorPickerNight").val($("#node-input-colorAtSwitchOnNightTime").val());
|
|
1246
|
+
if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('yes'); // Adjust in case of mismatch (from old geen bug)
|
|
1247
|
+
} else {
|
|
1248
|
+
// It's already an HEX color
|
|
1249
|
+
$("#colorPickerNight").val(node.colorAtSwitchOnNightTime);
|
|
1250
|
+
if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('yes'); // Adjust in case of mismatch (from old geen bug)
|
|
1251
|
+
}
|
|
1252
|
+
applyMatterCapabilities(currentMatterCapabilities);
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
$("#comboTemperatureAtSwitchOn, #comboBrightnessAtSwitchOn").on("change", function () {
|
|
1256
|
+
$("#node-input-colorAtSwitchOnDayTime").val('{ "kelvin":' + $("#comboTemperatureAtSwitchOn").val() + ', "brightness":' + $("#comboBrightnessAtSwitchOn").val() + ' }');
|
|
1257
|
+
});
|
|
1258
|
+
$("#comboTemperatureAtSwitchOnNightTime, #comboBrightnessAtSwitchOnNightTime").on("change", function () {
|
|
1259
|
+
$("#node-input-colorAtSwitchOnNightTime").val('{ "kelvin":' + $("#comboTemperatureAtSwitchOnNightTime").val() + ', "brightness":' + $("#comboBrightnessAtSwitchOnNightTime").val() + ' }');
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
// Create and put the JSON to node-input-colorAtSwitchOnDayTime
|
|
1263
|
+
$("#colorPickerDay").on("change", function () {
|
|
1264
|
+
$("#node-input-colorAtSwitchOnDayTime").val(this.value);
|
|
1265
|
+
});
|
|
1266
|
+
$("#colorPickerNight").on("change", function () {
|
|
1267
|
+
$("#node-input-colorAtSwitchOnNightTime").val(this.value);
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
$("#node-input-minDimLevelLight").val(node.minDimLevelLight);
|
|
1272
|
+
for (let index = 100; index >= 10; index--) {
|
|
1273
|
+
$("#node-input-maxDimLevelLight").append(
|
|
1274
|
+
$("<option>")
|
|
1275
|
+
.val(index)
|
|
1276
|
+
.text(index.toString() + "%")
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
$("#node-input-maxDimLevelLight").val(node.maxDimLevelLight);
|
|
1280
|
+
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function Go() {
|
|
1284
|
+
$("#waitWindow").hide();
|
|
1285
|
+
$("#mainWindow").show();
|
|
1286
|
+
// $.post("banana", { func: "getNameAndTime" }, function (data) {
|
|
1287
|
+
// //alert(data.body); // John
|
|
1288
|
+
// }, "json");
|
|
1289
|
+
try {
|
|
1290
|
+
RED.sidebar.show("help");
|
|
1291
|
+
} catch (error) { }
|
|
1292
|
+
onEditPrepare();
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
// The Matter controller needs no readiness poll here: the light list is fetched
|
|
1297
|
+
// on demand by the autocomplete. Start the editor immediately.
|
|
1298
|
+
$("#node-input-serverMatter").change(function () {
|
|
1299
|
+
try {
|
|
1300
|
+
node._cachedHueLightDevices = [];
|
|
1301
|
+
} catch (error) { }
|
|
1302
|
+
});
|
|
1303
|
+
Go();
|
|
1304
|
+
// ################################################################
|
|
1305
|
+
|
|
1306
|
+
},
|
|
1307
|
+
|
|
1308
|
+
oneditsave: function () {
|
|
1309
|
+
// Return to the info tab
|
|
1310
|
+
try {
|
|
1311
|
+
RED.sidebar.show("info");
|
|
1312
|
+
} catch (error) { }
|
|
1313
|
+
|
|
1314
|
+
//RED.sidebar.removeTab("tabNRColor");
|
|
1315
|
+
if ($("#node-input-enableNodePINS").val() === "yes") {
|
|
1316
|
+
this.outputs = 1;
|
|
1317
|
+
this.inputs = 1;
|
|
1318
|
+
} else {
|
|
1319
|
+
this.outputs = 0;
|
|
1320
|
+
this.inputs = 0;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
this.updateLocalStateFromKNXWrite = $("#node-input-updateLocalStateFromKNXWrite").is(":checked"); // Starting from v 4.1.31
|
|
1324
|
+
this.matterDeviceName = ($("#node-input-matterDeviceName").val() || '').trim();
|
|
1325
|
+
this.matterNodeId = ($("#node-input-matterNodeId").val() || '').trim();
|
|
1326
|
+
this.matterEndpointId = Number($("#node-input-matterEndpointId").val() || 1);
|
|
1327
|
+
this.matterDeviceCapabilities = ($("#node-input-matterDeviceCapabilities").val() || '').trim();
|
|
1328
|
+
this.name = this.matterDeviceName || this.name || '';
|
|
1329
|
+
$("#node-input-name").val(this.name);
|
|
1330
|
+
const parseSavedMatterCapabilities = (raw) => {
|
|
1331
|
+
if (typeof raw !== 'string' || raw.trim() === '') return null;
|
|
1332
|
+
try {
|
|
1333
|
+
return JSON.parse(raw);
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
const clearUnsupportedFields = (fields) => {
|
|
1339
|
+
fields.forEach((field) => {
|
|
1340
|
+
this[field] = '';
|
|
1341
|
+
$(`#node-input-${field}`).val('');
|
|
1342
|
+
});
|
|
1343
|
+
};
|
|
1344
|
+
const savedCapabilities = parseSavedMatterCapabilities(this.matterDeviceCapabilities);
|
|
1345
|
+
if (savedCapabilities && savedCapabilities.known === true) {
|
|
1346
|
+
if (savedCapabilities.onOff === false) {
|
|
1347
|
+
clearUnsupportedFields(['nameLightSwitch', 'GALightSwitch', 'dptLightSwitch', 'nameLightState', 'GALightState', 'dptLightState', 'nameLightBlink', 'GALightBlink', 'dptLightBlink']);
|
|
1348
|
+
}
|
|
1349
|
+
if (savedCapabilities.level === false) {
|
|
1350
|
+
clearUnsupportedFields(['nameLightDIM', 'GALightDIM', 'dptLightDIM', 'nameLightBrightness', 'GALightBrightness', 'dptLightBrightness', 'nameLightBrightnessState', 'GALightBrightnessState', 'dptLightBrightnessState']);
|
|
1351
|
+
this.updateKNXBrightnessStatusOnHUEOnOff = 'no';
|
|
1352
|
+
$("#node-input-updateKNXBrightnessStatusOnHUEOnOff").val('no');
|
|
1353
|
+
}
|
|
1354
|
+
if (savedCapabilities.colorTemperature !== true) {
|
|
1355
|
+
clearUnsupportedFields(['nameLightKelvinDIM', 'GALightKelvinDIM', 'dptLightKelvinDIM', 'nameLightKelvinPercentage', 'GALightKelvinPercentage', 'dptLightKelvinPercentage', 'nameLightKelvinPercentageState', 'GALightKelvinPercentageState', 'dptLightKelvinPercentageState', 'nameLightKelvin', 'GALightKelvin', 'dptLightKelvin', 'nameLightKelvinState', 'GALightKelvinState', 'dptLightKelvinState']);
|
|
1356
|
+
this.invertDimTunableWhiteDirection = false;
|
|
1357
|
+
$("#node-input-invertDimTunableWhiteDirection").prop('checked', false);
|
|
1358
|
+
}
|
|
1359
|
+
if (savedCapabilities.color !== true) {
|
|
1360
|
+
clearUnsupportedFields(['nameLightColor', 'GALightColor', 'dptLightColor', 'nameLightColorState', 'GALightColorState', 'dptLightColorState', 'nameLightHSV_H_DIM', 'GALightHSV_H_DIM', 'dptLightHSV_H_DIM', 'nameLightHSV_H_State', 'GALightHSV_H_State', 'dptLightHSV_H_State', 'nameLightHSV_S_DIM', 'GALightHSV_S_DIM', 'dptLightHSV_S_DIM', 'nameLightHSV_S_State', 'GALightHSV_S_State', 'dptLightHSV_S_State', 'nameLightColorCycle', 'GALightColorCycle', 'dptLightColorCycle']);
|
|
1361
|
+
}
|
|
1362
|
+
if (savedCapabilities.level === false || (savedCapabilities.color !== true && savedCapabilities.colorTemperature !== true)) {
|
|
1363
|
+
this.specifySwitchOnBrightness = 'no';
|
|
1364
|
+
this.enableDayNightLighting = 'no';
|
|
1365
|
+
$("#node-input-specifySwitchOnBrightness").val('no');
|
|
1366
|
+
$("#node-input-enableDayNightLighting").val('no');
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
this._cachedHueLightDevices = [];
|
|
1370
|
+
if (typeof this.__stopHueLocateSession === 'function') {
|
|
1371
|
+
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
|
|
1372
|
+
this.__stopHueLocateSession = null;
|
|
1373
|
+
}
|
|
1374
|
+
if (typeof this.__cleanupNodeRemovalListener === 'function') {
|
|
1375
|
+
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
|
|
1376
|
+
this.__cleanupNodeRemovalListener = null;
|
|
1377
|
+
}
|
|
1378
|
+
this.__locateSessionInfo = null;
|
|
1379
|
+
},
|
|
1380
|
+
oneditcancel: function () {
|
|
1381
|
+
// Return to the info tab
|
|
1382
|
+
try {
|
|
1383
|
+
RED.sidebar.show("info");
|
|
1384
|
+
} catch (error) { }
|
|
1385
|
+
//RED.sidebar.removeTab("tabNRColor");
|
|
1386
|
+
//RED.sidebar.show("help");
|
|
1387
|
+
|
|
1388
|
+
this._cachedHueLightDevices = [];
|
|
1389
|
+
if (typeof this.__stopHueLocateSession === 'function') {
|
|
1390
|
+
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
|
|
1391
|
+
this.__stopHueLocateSession = null;
|
|
1392
|
+
}
|
|
1393
|
+
if (typeof this.__cleanupNodeRemovalListener === 'function') {
|
|
1394
|
+
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
|
|
1395
|
+
this.__cleanupNodeRemovalListener = null;
|
|
1396
|
+
}
|
|
1397
|
+
this.__locateSessionInfo = null;
|
|
1398
|
+
},
|
|
1399
|
+
oneditdelete: function () {
|
|
1400
|
+
if (typeof this.oneditclose === 'function') {
|
|
1401
|
+
try { this.oneditclose(); } catch (error) { /* empty */ }
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
oneditclose: function () {
|
|
1405
|
+
if (typeof this.__stopHueLocateSession === 'function') {
|
|
1406
|
+
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
|
|
1407
|
+
}
|
|
1408
|
+
if (typeof this.__cleanupNodeRemovalListener === 'function') {
|
|
1409
|
+
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
|
|
1410
|
+
this.__cleanupNodeRemovalListener = null;
|
|
1411
|
+
}
|
|
1412
|
+
this.__stopHueLocateSession = null;
|
|
1413
|
+
this.__hueLocateActive = false;
|
|
1414
|
+
this.__locateSessionInfo = null;
|
|
1415
|
+
},
|
|
1416
|
+
oneditresize: function (size) {
|
|
1417
|
+
//var height = size.height;
|
|
1418
|
+
//$('.editor-tray-content').css({ "width": "2700px" });
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
|
|
1422
|
+
function rgbHex(red, green, blue, alpha) {
|
|
1423
|
+
const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
|
|
1424
|
+
const parseCssRgbString = (input) => {
|
|
1425
|
+
const parts = input.replace(/rgba?\(([^)]+)\)/, '$1').split(/[,\s/]+/).filter(Boolean);
|
|
1426
|
+
if (parts.length < 3) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
const parseValue = (value, max) => {
|
|
1431
|
+
value = value.trim();
|
|
1432
|
+
|
|
1433
|
+
if (value.endsWith('%')) {
|
|
1434
|
+
return Math.min(Number.parseFloat(value) * max / 100, max);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
return Math.min(Number.parseFloat(value), max);
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
const red = parseValue(parts[0], 255);
|
|
1441
|
+
const green = parseValue(parts[1], 255);
|
|
1442
|
+
const blue = parseValue(parts[2], 255);
|
|
1443
|
+
let alpha;
|
|
1444
|
+
|
|
1445
|
+
if (parts.length === 4) {
|
|
1446
|
+
alpha = parseValue(parts[3], 1);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
return [red, green, blue, alpha];
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
let isPercent = (red + (alpha || '')).toString().includes('%');
|
|
1453
|
+
|
|
1454
|
+
if (typeof red === 'string' && !green) { // Single string parameter.
|
|
1455
|
+
const parsed = parseCssRgbString(red);
|
|
1456
|
+
if (!parsed) {
|
|
1457
|
+
throw new TypeError('Invalid or unsupported color format.');
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
isPercent = false;
|
|
1461
|
+
[red, green, blue, alpha] = parsed;
|
|
1462
|
+
} else if (alpha !== undefined) {
|
|
1463
|
+
alpha = Number.parseFloat(alpha);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
if (typeof red !== 'number'
|
|
1467
|
+
|| typeof green !== 'number'
|
|
1468
|
+
|| typeof blue !== 'number'
|
|
1469
|
+
|| red > 255
|
|
1470
|
+
|| green > 255
|
|
1471
|
+
|| blue > 255
|
|
1472
|
+
) {
|
|
1473
|
+
throw new TypeError('Expected three numbers below 256');
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
if (typeof alpha === 'number') {
|
|
1477
|
+
if (!isPercent && alpha >= 0 && alpha <= 1) {
|
|
1478
|
+
alpha = Math.round(255 * alpha);
|
|
1479
|
+
} else if (isPercent && alpha >= 0 && alpha <= 100) {
|
|
1480
|
+
alpha = Math.round(255 * alpha / 100);
|
|
1481
|
+
} else {
|
|
1482
|
+
throw new TypeError(`Expected alpha value (${alpha}) as a fraction or percentage`);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
alpha = (alpha | 1 << 8).toString(16).slice(1); // eslint-disable-line no-mixed-operators
|
|
1486
|
+
} else {
|
|
1487
|
+
alpha = '';
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
return toHex(red, green, blue, alpha);
|
|
1491
|
+
}
|
|
1492
|
+
}())
|
|
1493
|
+
|
|
1494
|
+
</script>
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
<script type="text/html" data-template-name="knxUltimateMatterControllerDevice">
|
|
1499
|
+
<div class="form-row" style="margin-bottom:10px;">
|
|
1500
|
+
<span style="color:#ff0000"><i class="fa fa-youtube"></i></span> <a target="_blank" href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E"><b>KNX-Ultimate video tutorials (YouTube playlist)</b></a>
|
|
1501
|
+
</div>
|
|
1502
|
+
<div id="waitWindow">
|
|
1503
|
+
<br/><br/>
|
|
1504
|
+
<p align="center">
|
|
1505
|
+
<i class="fa-solid fa-hourglass-start fa-spin-pulse fa-2x"></i><br/><br/>
|
|
1506
|
+
<span data-i18n="hue-config.properties.wait_message"></span>
|
|
1507
|
+
</p>
|
|
1508
|
+
</div>
|
|
1509
|
+
<div id="mainWindow" hidden>
|
|
1510
|
+
<div class="form-row">
|
|
1511
|
+
<b><span data-i18n="knxUltimateMatterControllerDevice.title"></span></b>  <span style="color:red"    <i class="fa fa-youtube"></i></span> <a
|
|
1512
|
+
target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u><span data-i18n="common.youtube_sample"></span></u></a>
|
|
1513
|
+
<br />
|
|
1514
|
+
<br />
|
|
1515
|
+
<p align="center">
|
|
1516
|
+
<i class="fa-regular fa-lightbulb fa-bounce fa-2x"></i>
|
|
1517
|
+
</p>
|
|
1518
|
+
<br />
|
|
1519
|
+
<label for="node-input-server">
|
|
1520
|
+
<img
|
|
1521
|
+
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img>
|
|
1522
|
+
<span data-i18n="common.knx_gw"></span>
|
|
1523
|
+
</label>
|
|
1524
|
+
<input type="text" id="node-input-server" />
|
|
1525
|
+
</div>
|
|
1526
|
+
|
|
1527
|
+
<div class="form-row">
|
|
1528
|
+
<label for="node-input-serverMatter">
|
|
1529
|
+
<i class="fa fa-circle-nodes"></i>
|
|
1530
|
+
<span data-i18n="knxUltimateMatterControllerDevice.matter_controller"></span>
|
|
1531
|
+
</label>
|
|
1532
|
+
<input type="text" id="node-input-serverMatter" />
|
|
1533
|
+
</div>
|
|
1534
|
+
|
|
1535
|
+
<br />
|
|
1536
|
+
<p>
|
|
1537
|
+
<b><span data-i18n="knxUltimateMatterControllerDevice.matter_section"></span></b>
|
|
1538
|
+
</p>
|
|
1539
|
+
|
|
1540
|
+
<div class="form-row">
|
|
1541
|
+
<label for="node-input-matterDeviceName">
|
|
1542
|
+
<i class="fa fa-play-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.matter_device"></span>
|
|
1543
|
+
</label>
|
|
1544
|
+
<input type="text" id="node-input-matterDeviceName" placeholder="Search your Matter light name"
|
|
1545
|
+
style="flex:1 1 240px; min-width:240px; max-width:240px;" />
|
|
1546
|
+
<button type="button" class="red-ui-button hue-refresh-devices"
|
|
1547
|
+
style="margin-left:6px; color:#1b7d33; border-color:#1b7d33;" title="Refresh Matter lights">
|
|
1548
|
+
<i class="fa fa-sync"></i>
|
|
1549
|
+
</button>
|
|
1550
|
+
<span class="hue-devices-loading" style="margin-left:6px; display:none; color:#1b7d33;">
|
|
1551
|
+
<i class="fa fa-circle-notch fa-spin"></i>
|
|
1552
|
+
</span>
|
|
1553
|
+
<input type="hidden" id="node-input-matterNodeId" />
|
|
1554
|
+
<input type="hidden" id="node-input-matterEndpointId" />
|
|
1555
|
+
<input type="hidden" id="node-input-matterDeviceCapabilities" />
|
|
1556
|
+
<input type="hidden" id="node-input-name" />
|
|
1557
|
+
</div>
|
|
1558
|
+
|
|
1559
|
+
<br />
|
|
1560
|
+
|
|
1561
|
+
<div id="tabs" style="display:none; width: 900px;">
|
|
1562
|
+
<ul>
|
|
1563
|
+
<li><a href="#tabs-1"><i class="fa-solid fa-toggle-on"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.switch"></span></a></li>
|
|
1564
|
+
<li><a href="#tabs-2"><i class="fa-solid fa-arrow-up-wide-short"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.dim"></span></a></li>
|
|
1565
|
+
<li><a href="#tabs-3"><i class="fa-solid fa-temperature-quarter"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.tunable_white"></span></a></li>
|
|
1566
|
+
<li><a href="#tabs-4"><i class="fa-solid fa-palette"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.rgb_hsv"></span></a></li>
|
|
1567
|
+
<li><a href="#tabs-6"><i class="fa-solid fa-code-merge"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.behaviour"></span></a></li>
|
|
1568
|
+
</ul>
|
|
1569
|
+
<div id="tabs-1">
|
|
1570
|
+
<p>
|
|
1571
|
+
<div class="form-row">
|
|
1572
|
+
<label for="node-input-nameLightSwitch" style="width:110px;"><i class="fa fa-play-circle-o"></i> <span data-i18n="knxUltimateMatterControllerDevice.control"></span></label>
|
|
1573
|
+
|
|
1574
|
+
<label for="node-input-GALightSwitch" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1575
|
+
<input type="text" id="node-input-GALightSwitch" placeholder="Ex: 1/1/1"
|
|
1576
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1577
|
+
|
|
1578
|
+
<label for="node-input-dptLightSwitch" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1579
|
+
<select id="node-input-dptLightSwitch" style="width:140px;"></select>
|
|
1580
|
+
|
|
1581
|
+
<label for="node-input-nameLightSwitch" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
|
|
1582
|
+
<input type="text" id="node-input-nameLightSwitch" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1583
|
+
</div>
|
|
1584
|
+
<div class="form-row">
|
|
1585
|
+
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-question-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.status"></span></label>
|
|
1586
|
+
|
|
1587
|
+
<label for="node-input-GALightState" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1588
|
+
<input type="text" id="node-input-GALightState" placeholder="Ex: 1/1/1"
|
|
1589
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1590
|
+
|
|
1591
|
+
<label for="node-input-dptLightState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1592
|
+
<select id="node-input-dptLightState" style="width:140px;"></select>
|
|
1593
|
+
|
|
1594
|
+
<label for="node-input-nameLightState" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1595
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1596
|
+
<input type="text" id="node-input-nameLightState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1597
|
+
</div>
|
|
1598
|
+
</p>
|
|
1599
|
+
</div>
|
|
1600
|
+
<div id="tabs-2">
|
|
1601
|
+
<p>
|
|
1602
|
+
<img src="resources/node-red-contrib-knx-ultimate/dim.png" style="width: 900px;">
|
|
1603
|
+
<div class="form-row">
|
|
1604
|
+
<label for="node-input-nameLightDIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
|
|
1605
|
+
|
|
1606
|
+
<label for="node-input-GALightDIM" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1607
|
+
<input type="text" id="node-input-GALightDIM" placeholder="Ex: 1/1/1"
|
|
1608
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1609
|
+
|
|
1610
|
+
<label for="node-input-dptLightDIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1611
|
+
<select id="node-input-dptLightDIM" style="width:140px;"></select>
|
|
1612
|
+
|
|
1613
|
+
<label for="node-input-nameLightDIM" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1614
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1615
|
+
<input type="text" id="node-input-nameLightDIM" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1616
|
+
</div>
|
|
1617
|
+
<div class="form-row">
|
|
1618
|
+
<label for="node-input-nameLightBrightness" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
|
|
1619
|
+
%</label>
|
|
1620
|
+
|
|
1621
|
+
<label for="node-input-GALightBrightness" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1622
|
+
<input type="text" id="node-input-GALightBrightness" placeholder="Ex: 1/1/1"
|
|
1623
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1624
|
+
|
|
1625
|
+
<label for="node-input-dptLightBrightness" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1626
|
+
<select id="node-input-dptLightBrightness" style="width:140px;"></select>
|
|
1627
|
+
|
|
1628
|
+
<label for="node-input-nameLightBrightness" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1629
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1630
|
+
<input type="text" id="node-input-nameLightBrightness" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1631
|
+
</div>
|
|
1632
|
+
|
|
1633
|
+
<div class="form-row">
|
|
1634
|
+
<label for="node-input-nameLightBrightnessState" style="width:110px;"><i class="fa fa-question-circle"></i> Status %</label>
|
|
1635
|
+
|
|
1636
|
+
<label for="node-input-GALightBrightnessState" style="width:20px;"><span
|
|
1637
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1638
|
+
<input type="text" id="node-input-GALightBrightnessState" placeholder="Ex: 1/1/1"
|
|
1639
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1640
|
+
|
|
1641
|
+
<label for="node-input-dptLightBrightnessState"
|
|
1642
|
+
style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
1643
|
+
<select id="node-input-dptLightBrightnessState" style="width:140px;"></select>
|
|
1644
|
+
|
|
1645
|
+
<label for="node-input-nameLightBrightnessState" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1646
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1647
|
+
<input type="text" id="node-input-nameLightBrightnessState"
|
|
1648
|
+
style="width:190px;margin-left: 5px; text-align: left;">
|
|
1649
|
+
</div>
|
|
1650
|
+
<div class="form-row">
|
|
1651
|
+
<label for="node-input-dimSpeed" style="width:260px">
|
|
1652
|
+
<i class="fa fa-bolt"></i> Dim Speed (ms)
|
|
1653
|
+
</label>
|
|
1654
|
+
<input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:210px">
|
|
1655
|
+
</div>
|
|
1656
|
+
<div class="form-row">
|
|
1657
|
+
<label for="node-input-minDimLevelLight" style="width:260px;">
|
|
1658
|
+
<i class="fa fa-clone"></i> Min Dim Brightness
|
|
1659
|
+
</label>
|
|
1660
|
+
<select id="node-input-minDimLevelLight">
|
|
1661
|
+
<option value="useHueLightLevel" data-i18n="knxUltimateMatterControllerDevice.use_min_brightness"></option>
|
|
1662
|
+
</select>
|
|
1663
|
+
</div>
|
|
1664
|
+
<div class="form-row">
|
|
1665
|
+
<label for="node-input-maxDimLevelLight" style="width:260px;">
|
|
1666
|
+
<i class="fa fa-clone"></i> Max Dim Brightness
|
|
1667
|
+
</label>
|
|
1668
|
+
<select id="node-input-maxDimLevelLight"></select>
|
|
1669
|
+
</div>
|
|
1670
|
+
</p>
|
|
1671
|
+
</div>
|
|
1672
|
+
<div id="tabs-3">
|
|
1673
|
+
<p>
|
|
1674
|
+
<img src="resources/node-red-contrib-knx-ultimate/tunablewhite.png" style="width: 900px;">
|
|
1675
|
+
<div class="form-row">
|
|
1676
|
+
<label for="node-input-nameLightKelvinDIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
|
|
1677
|
+
|
|
1678
|
+
<label for="node-input-GALightKelvinDIM" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1679
|
+
<input type="text" id="node-input-GALightKelvinDIM" placeholder="Ex: 1/1/1"
|
|
1680
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1681
|
+
|
|
1682
|
+
<label for="node-input-dptLightKelvinDIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1683
|
+
<select id="node-input-dptLightKelvinDIM" style="width:140px;"></select>
|
|
1684
|
+
|
|
1685
|
+
<label for="node-input-nameLightKelvinDIM" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
|
|
1686
|
+
<input type="text" id="node-input-nameLightKelvinDIM" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1687
|
+
</div>
|
|
1688
|
+
<div class="form-row">
|
|
1689
|
+
<label for="node-input-nameLightKelvinPercentage" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
|
|
1690
|
+
%</label>
|
|
1691
|
+
|
|
1692
|
+
<label for="node-input-GALightKelvinPercentage" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1693
|
+
<input type="text" id="node-input-GALightKelvinPercentage" placeholder="Ex: 1/1/1"
|
|
1694
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1695
|
+
|
|
1696
|
+
<label for="node-input-dptLightKelvinPercentage" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1697
|
+
<select id="node-input-dptLightKelvinPercentage" style="width:140px;"></select>
|
|
1698
|
+
|
|
1699
|
+
<label for="node-input-nameLightKelvinPercentage"
|
|
1700
|
+
style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
1701
|
+
<input type="text" id="node-input-nameLightKelvinPercentage" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1702
|
+
</div>
|
|
1703
|
+
<div class="form-row">
|
|
1704
|
+
<label for="node-input-nameLightKelvinPercentageState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
|
|
1705
|
+
%</label>
|
|
1706
|
+
|
|
1707
|
+
<label for="node-input-GALightKelvinPercentageState" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1708
|
+
<input type="text" id="node-input-GALightKelvinPercentageState" placeholder="Ex: 1/1/1"
|
|
1709
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1710
|
+
|
|
1711
|
+
<label for="node-input-dptLightKelvinPercentageState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1712
|
+
<select id="node-input-dptLightKelvinPercentageState" style="width:140px;"></select>
|
|
1713
|
+
|
|
1714
|
+
<label for="node-input-nameLightKelvinPercentageState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
|
|
1715
|
+
<input type="text" id="node-input-nameLightKelvinPercentageState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1716
|
+
</div>
|
|
1717
|
+
<div class="form-row">
|
|
1718
|
+
<label for="node-input-nameLightKelvin" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
|
|
1719
|
+
Kelvin</label>
|
|
1720
|
+
|
|
1721
|
+
<label for="node-input-GALightKelvin" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1722
|
+
<input type="text" id="node-input-GALightKelvin" placeholder="Ex: 1/1/1"
|
|
1723
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1724
|
+
|
|
1725
|
+
<label for="node-input-dptLightKelvin" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1726
|
+
<select id="node-input-dptLightKelvin" style="width:140px;"></select>
|
|
1727
|
+
|
|
1728
|
+
<label for="node-input-nameLightKelvin" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
|
|
1729
|
+
<input type="text" id="node-input-nameLightKelvin" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1730
|
+
</div>
|
|
1731
|
+
<div class="form-row">
|
|
1732
|
+
<label for="node-input-nameLightKelvinState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
|
|
1733
|
+
Kelvin</label>
|
|
1734
|
+
<label for="node-input-GALightKelvinState" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1735
|
+
<input type="text" id="node-input-GALightKelvinState" placeholder="Ex: 1/1/1"
|
|
1736
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1737
|
+
|
|
1738
|
+
<label for="node-input-dptLightKelvinState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1739
|
+
<select id="node-input-dptLightKelvinState" style="width:140px;"></select>
|
|
1740
|
+
|
|
1741
|
+
<label for="node-input-nameLightKelvinState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
|
|
1742
|
+
<input type="text" id="node-input-nameLightKelvinState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1743
|
+
</div>
|
|
1744
|
+
<div class="form-row">
|
|
1745
|
+
<label style="width:170px" for="node-input-invertDimTunableWhiteDirection">
|
|
1746
|
+
<i class="fa fa-shuffle"></i> Invert dim direction
|
|
1747
|
+
</label>
|
|
1748
|
+
<input type="checkbox" id="node-input-invertDimTunableWhiteDirection"
|
|
1749
|
+
style="display:inline-block; width:auto; vertical-align:top;" />
|
|
1750
|
+
</div>
|
|
1751
|
+
</p>
|
|
1752
|
+
</div>
|
|
1753
|
+
<div id="tabs-4">
|
|
1754
|
+
<p>
|
|
1755
|
+
<img src="resources/node-red-contrib-knx-ultimate/rgb.png" style="width: 900px;">
|
|
1756
|
+
<p><b> RGB section</b></p>
|
|
1757
|
+
<div class="form-row">
|
|
1758
|
+
<label for="node-input-nameLightColor" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control rgb</label>
|
|
1759
|
+
|
|
1760
|
+
<label for="node-input-GALightColor" style="width:20px;"><span
|
|
1761
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1762
|
+
<input type="text" id="node-input-GALightColor" placeholder="Ex: 1/1/1"
|
|
1763
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1764
|
+
|
|
1765
|
+
<label for="node-input-dptLightColor" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1766
|
+
<select id="node-input-dptLightColor" style="width:140px;"></select>
|
|
1767
|
+
|
|
1768
|
+
<label for="node-input-nameLightColor" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1769
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1770
|
+
<input type="text" id="node-input-nameLightColor" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1771
|
+
</div>
|
|
1772
|
+
<div class="form-row">
|
|
1773
|
+
<label for="node-input-nameLightColorState" style="width:110px;"><i class="fa fa-question-circle"></i> Status rgb</label>
|
|
1774
|
+
|
|
1775
|
+
<label for="node-input-GALightColorState" style="width:20px;"><span
|
|
1776
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1777
|
+
<input type="text" id="node-input-GALightColorState" placeholder="Ex: 1/1/1"
|
|
1778
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1779
|
+
|
|
1780
|
+
<label for="node-input-dptLightColorState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1781
|
+
<select id="node-input-dptLightColorState" style="width:140px;"></select>
|
|
1782
|
+
|
|
1783
|
+
<label for="node-input-nameLightColorState" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1784
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1785
|
+
<input type="text" id="node-input-nameLightColorState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1786
|
+
</div>
|
|
1787
|
+
|
|
1788
|
+
<p><b> HSV section</b></p>
|
|
1789
|
+
<!-- // HSV Color change
|
|
1790
|
+
// nameLightHSV_H_DIM: { value: "" },
|
|
1791
|
+
// GALightHSV_H_DIM: { value: "" },
|
|
1792
|
+
// dptLightHSV_H_DIM: { value: "" }, -->
|
|
1793
|
+
<div class="form-row">
|
|
1794
|
+
<label for="node-input-nameLightHSV_H_DIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control H dim</label>
|
|
1795
|
+
|
|
1796
|
+
<label for="node-input-GALightHSV_H_DIM" style="width:20px;"><span
|
|
1797
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1798
|
+
<input type="text" id="node-input-GALightHSV_H_DIM" placeholder="Ex: 1/1/1"
|
|
1799
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1800
|
+
|
|
1801
|
+
<label for="node-input-dptLightHSV_H_DIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1802
|
+
<select id="node-input-dptLightHSV_H_DIM" style="width:140px;"></select>
|
|
1803
|
+
|
|
1804
|
+
<label for="node-input-nameLightHSV_H_DIM" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1805
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1806
|
+
<input type="text" id="node-input-nameLightHSV_H_DIM" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1807
|
+
</div>
|
|
1808
|
+
<div class="form-row">
|
|
1809
|
+
<label for="node-input-nameLightHSV_H_State" style="width:110px;"><i class="fa fa-question-circle"></i> Status H %</label>
|
|
1810
|
+
|
|
1811
|
+
<label for="node-input-GALightHSV_H_State" style="width:20px;"><span
|
|
1812
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1813
|
+
<input type="text" id="node-input-GALightHSV_H_State" placeholder="Ex: 1/1/1"
|
|
1814
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1815
|
+
|
|
1816
|
+
<label for="node-input-dptLightHSV_H_State" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1817
|
+
<select id="node-input-dptLightHSV_H_State" style="width:140px;"></select>
|
|
1818
|
+
|
|
1819
|
+
<label for="node-input-nameLightHSV_H_State" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1820
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1821
|
+
<input type="text" id="node-input-nameLightHSV_H_State" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1822
|
+
</div>
|
|
1823
|
+
|
|
1824
|
+
<!-- // HSV Saturation change
|
|
1825
|
+
nameLightHSV_S_DIM: { value: "" },
|
|
1826
|
+
GALightHSV_S_DIM: { value: "" },
|
|
1827
|
+
dptLightHSV_S_DIM: { value: "" },
|
|
1828
|
+
nameLightHSV_S_State: { value: "" },
|
|
1829
|
+
GALightHSV_S_State: { value: "" },
|
|
1830
|
+
dptLightHSV_S_State: { value: "" }, -->
|
|
1831
|
+
<div class="form-row">
|
|
1832
|
+
<label for="node-input-nameLightHSV_S_DIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control S dim</label>
|
|
1833
|
+
|
|
1834
|
+
<label for="node-input-GALightHSV_S_DIM" style="width:20px;"><span
|
|
1835
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1836
|
+
<input type="text" id="node-input-GALightHSV_S_DIM" placeholder="Ex: 1/1/1"
|
|
1837
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1838
|
+
|
|
1839
|
+
<label for="node-input-dptLightHSV_S_DIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1840
|
+
<select id="node-input-dptLightHSV_S_DIM" style="width:140px;"></select>
|
|
1841
|
+
|
|
1842
|
+
<label for="node-input-nameLightHSV_S_DIM" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1843
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1844
|
+
<input type="text" id="node-input-nameLightHSV_S_DIM" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1845
|
+
</div>
|
|
1846
|
+
<div class="form-row">
|
|
1847
|
+
<label for="node-input-nameLightHSV_S_State" style="width:110px;"><i class="fa fa-question-circle"></i> Status S %</label>
|
|
1848
|
+
|
|
1849
|
+
<label for="node-input-GALightHSV_S_State" style="width:20px;"><span
|
|
1850
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-GALightState"></span></label>
|
|
1851
|
+
<input type="text" id="node-input-GALightHSV_S_State" placeholder="Ex: 1/1/1"
|
|
1852
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1853
|
+
|
|
1854
|
+
<label for="node-input-dptLightHSV_S_State" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1855
|
+
<select id="node-input-dptLightHSV_S_State" style="width:140px;"></select>
|
|
1856
|
+
|
|
1857
|
+
<label for="node-input-nameLightHSV_S_State" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1858
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1859
|
+
<input type="text" id="node-input-nameLightHSV_S_State" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1860
|
+
</div>
|
|
1861
|
+
|
|
1862
|
+
<!-- // HSV V Brightness change
|
|
1863
|
+
nameLightHSV_V_DIM: { value: "" },
|
|
1864
|
+
GALightHSV_V_DIM: { value: "" },
|
|
1865
|
+
dptLightHSV_V_DIM: { value: "" },
|
|
1866
|
+
nameLightHSV_V_State: { value: "" },
|
|
1867
|
+
GALightHSV_V_State: { value: "" },
|
|
1868
|
+
dptLightHSV_V_State: { value: "" }, -->
|
|
1869
|
+
<div class="form-row">
|
|
1870
|
+
<label for="node-input-HSVDimSpeed" style="width:260px">
|
|
1871
|
+
<i class="fa fa-bolt"></i> Dim Speed (ms)
|
|
1872
|
+
</label>
|
|
1873
|
+
<input type="text" id="node-input-HSVDimSpeed" placeholder='Default is 5000' style="width:210px">
|
|
1874
|
+
</div>
|
|
1875
|
+
|
|
1876
|
+
</p>
|
|
1877
|
+
</div>
|
|
1878
|
+
<div id="tabs-6">
|
|
1879
|
+
<p>
|
|
1880
|
+
<div class="form-row">
|
|
1881
|
+
<label style="width:260px;" for="node-input-readStatusAtStartup"><i class="fa fa-question-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.read_status_startup"></span></label>
|
|
1882
|
+
<select id="node-input-readStatusAtStartup">
|
|
1883
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.opt_no"></option>
|
|
1884
|
+
<option value="yes" data-i18n="knxUltimateMatterControllerDevice.opt_yes_emit"></option>
|
|
1885
|
+
</select>
|
|
1886
|
+
</div>
|
|
1887
|
+
<div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
|
|
1888
|
+
<label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
|
|
1889
|
+
<i class="fa fa-tag"></i> <span data-i18n="knxUltimateMatterControllerDevice.knx_brightness_status"></span>
|
|
1890
|
+
</label>
|
|
1891
|
+
<select id="node-input-updateKNXBrightnessStatusOnHUEOnOff">
|
|
1892
|
+
<option value="onhueoff" data-i18n="knxUltimateMatterControllerDevice.knx_brightness_onhueoff"></option>
|
|
1893
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.knx_brightness_no"></option>
|
|
1894
|
+
</select>
|
|
1895
|
+
</div>
|
|
1896
|
+
<div class="form-row">
|
|
1897
|
+
<label style="width:260px;" for="node-input-updateLocalStateFromKNXWrite">
|
|
1898
|
+
<i class="fa fa-database"></i> <span data-i18n="knxUltimateMatterControllerDevice.update_local_state_from_knx_write"></span>
|
|
1899
|
+
</label>
|
|
1900
|
+
<input type="checkbox" id="node-input-updateLocalStateFromKNXWrite" style="display:inline-block; width:auto; vertical-align:top;" />
|
|
1901
|
+
</div>
|
|
1902
|
+
<div class="form-tips" style="margin-left:260px;" data-i18n="knxUltimateMatterControllerDevice.update_local_state_from_knx_write_hint"></div><br/>
|
|
1903
|
+
<div id ="divBehaviourBrightness">
|
|
1904
|
+
<div class="form-row">
|
|
1905
|
+
<label for="node-input-specifySwitchOnBrightness" style="width:260px;">
|
|
1906
|
+
<i class="fa fa-tag"></i> <span data-i18n="knxUltimateMatterControllerDevice.switch_on_behaviour"></span>
|
|
1907
|
+
</label>
|
|
1908
|
+
<select id="node-input-specifySwitchOnBrightness">
|
|
1909
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.none"></option>
|
|
1910
|
+
<option value="temperature" data-i18n="knxUltimateMatterControllerDevice.select_temperature_brightness"></option>
|
|
1911
|
+
<option value="yes" data-i18n="knxUltimateMatterControllerDevice.select_color"></option>
|
|
1912
|
+
</select>
|
|
1913
|
+
</div>
|
|
1914
|
+
|
|
1915
|
+
<div class="form-row" id="divColorsAtSwitchOn">
|
|
1916
|
+
<label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
|
|
1917
|
+
</label>
|
|
1918
|
+
<input type="hidden" id="node-input-colorAtSwitchOnDayTime">
|
|
1919
|
+
<input type="color" id="colorPickerDay" style="width:260px">
|
|
1920
|
+
<button id="getColorAtSwitchOnDayTimeButton" type="button" class="red-ui-button"><span data-i18n="knxUltimateMatterControllerDevice.get_current"></span></button>
|
|
1921
|
+
</div>
|
|
1922
|
+
<div class="form-row" id="divTemperatureAtSwitchOn" hidden>
|
|
1923
|
+
<label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
|
|
1924
|
+
</label>
|
|
1925
|
+
<select style="width:12%;" id="comboTemperatureAtSwitchOn"></select>
|
|
1926
|
+
<select style="width:25%;" id="comboBrightnessAtSwitchOn"></select>
|
|
1927
|
+
</div>
|
|
1928
|
+
|
|
1929
|
+
<div id="divCCSBoxAtNightLighting">
|
|
1930
|
+
<div class="form-row">
|
|
1931
|
+
<label for="node-input-enableDayNightLighting" style="width:260px;">
|
|
1932
|
+
<i class="fa fa-clone"></i> <span data-i18n="knxUltimateMatterControllerDevice.night_lighting"></span>
|
|
1933
|
+
</label>
|
|
1934
|
+
<select id="node-input-enableDayNightLighting">
|
|
1935
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.no_night_lighting"></option>
|
|
1936
|
+
<!-- <option value="temperature">Select temperature and brightness</option>
|
|
1937
|
+
<option value="yes">Select color</option> -->
|
|
1938
|
+
</select>
|
|
1939
|
+
</div>
|
|
1940
|
+
|
|
1941
|
+
<div id="divEnableDayNightLighting">
|
|
1942
|
+
|
|
1943
|
+
<div class="form-row" id="divColorsAtSwitchOnNightTime">
|
|
1944
|
+
<label for="node-input-colorAtSwitchOnNightTime" style="width:260px"></label>
|
|
1945
|
+
<input type="hidden" id="node-input-colorAtSwitchOnNightTime">
|
|
1946
|
+
<input type="color" id="colorPickerNight" style="width:260px">
|
|
1947
|
+
<button id="getColorAtSwitchOnNightTimeButton" type="button" class="red-ui-button"><span data-i18n="knxUltimateMatterControllerDevice.get_current"></span></button>
|
|
1948
|
+
</div>
|
|
1949
|
+
<div class="form-row" id="divTemperatureAtSwitchOnNightTime" hidden>
|
|
1950
|
+
<label for="node-input-colorAtSwitchOnNightTime" style="width:260px"></label>
|
|
1951
|
+
<select style="width:25%;" id="comboTemperatureAtSwitchOnNightTime"></select>
|
|
1952
|
+
<select style="width:25%;" id="comboBrightnessAtSwitchOnNightTime"></select>
|
|
1953
|
+
</div>
|
|
1954
|
+
|
|
1955
|
+
<div class="form-row">
|
|
1956
|
+
<label for="node-input-nameDaylightSensor" style="width:110px;"><i class="fa fa-clock-o"></i>
|
|
1957
|
+
<span data-i18n="knxUltimateMatterControllerDevice.day_night"></span></label>
|
|
1958
|
+
<label for="node-input-GADaylightSensor" style="width:20px;"><span data-i18n="common.ga"></span></label>
|
|
1959
|
+
<input type="text" id="node-input-GADaylightSensor" placeholder="Ex: 1/1/1"
|
|
1960
|
+
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1961
|
+
|
|
1962
|
+
<label for="node-input-dptDaylightSensor" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
|
|
1963
|
+
<select id="node-input-dptDaylightSensor" style="width:140px;"></select>
|
|
1964
|
+
|
|
1965
|
+
<label for="node-input-nameDaylightSensor" style="width:50px; margin-left: 0px; text-align: right;"><span
|
|
1966
|
+
data-i18n="knxUltimateMatterControllerDevice.node-input-name"></span></label>
|
|
1967
|
+
<input type="text" id="node-input-nameDaylightSensor" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1968
|
+
</div>
|
|
1969
|
+
<div class="form-row">
|
|
1970
|
+
<label style="width:170px" for="node-input-invertDayNight">
|
|
1971
|
+
<i class="fa fa-shuffle"></i> <span data-i18n="knxUltimateMatterControllerDevice.invert_day_night"></span>
|
|
1972
|
+
</label>
|
|
1973
|
+
<input type="checkbox" id="node-input-invertDayNight"
|
|
1974
|
+
style="display:inline-block; width:auto; vertical-align:top;" />
|
|
1975
|
+
</div>
|
|
1976
|
+
<div class="form-row">
|
|
1977
|
+
<label for="node-input-restoreDayMode" style="width:260px;">
|
|
1978
|
+
<i class="fa fa-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.override_night_mode"></span>
|
|
1979
|
+
</label>
|
|
1980
|
+
<select id="node-input-restoreDayMode">
|
|
1981
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.override_no"></option>
|
|
1982
|
+
<option value="setDayByFastSwitchLightSingle" data-i18n="knxUltimateMatterControllerDevice.override_set_day_fast_this"></option>
|
|
1983
|
+
<option value="setDayByFastSwitchLightALL" data-i18n="knxUltimateMatterControllerDevice.override_set_day_fast_all"></option>
|
|
1984
|
+
</select>
|
|
1985
|
+
</div>
|
|
1986
|
+
</div>
|
|
1987
|
+
</div>
|
|
1988
|
+
<br/>
|
|
1989
|
+
</div>
|
|
1990
|
+
<div class="form-row">
|
|
1991
|
+
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
1992
|
+
<i class="fa fa-circle"></i> <span data-i18n="knxUltimateMatterControllerDevice.node_pins"></span>
|
|
1993
|
+
</label>
|
|
1994
|
+
<select id="node-input-enableNodePINS">
|
|
1995
|
+
<option value="no" data-i18n="knxUltimateMatterControllerDevice.node_pins_hide"></option>
|
|
1996
|
+
<option value="yes" data-i18n="knxUltimateMatterControllerDevice.node_pins_show"></option>
|
|
1997
|
+
</select>
|
|
1998
|
+
</div>
|
|
1999
|
+
</p>
|
|
2000
|
+
</div>
|
|
2001
|
+
</div>
|
|
2002
|
+
</div>
|
|
2003
|
+
|
|
2004
|
+
<br/>
|
|
2005
|
+
<br/>
|
|
2006
|
+
<br/>
|
|
2007
|
+
<br/>
|
|
2008
|
+
<br/>
|
|
2009
|
+
|
|
2010
|
+
</script>
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
<script type="text/markdown" data-help-name="knxUltimateMatterControllerDevice">
|
|
2014
|
+
<p>This node controls Philips Hue lights (single or grouped) and maps their commands/states to KNX.</p>
|
|
2015
|
+
|
|
2016
|
+
**General**
|
|
2017
|
+
|
|
2018
|
+
This node was born from the Hue Light node: same KNX group-address model and behaviour,
|
|
2019
|
+
with the Philips Hue engine replaced by the **Matter controller** engine. A Matter bridge
|
|
2020
|
+
(for example a Hue Bridge paired via Matter) is expanded into its lamps in the picker.
|
|
2021
|
+
Hue-only features (native effects, locate) are not available on Matter.
|
|
2022
|
+
|
|
2023
|
+
|Property|Description|
|
|
2024
|
+
|--|--|
|
|
2025
|
+
| KNX GW | Select the KNX gateway to be used |
|
|
2026
|
+
| Matter controller | Select the Matter controller configuration to be used |
|
|
2027
|
+
| Matter device | Matter light or plug to use (autocomplete while typing).|
|
|
2028
|
+
|
|
2029
|
+
<br/>
|
|
2030
|
+
|
|
2031
|
+
**OPTIONS**
|
|
2032
|
+
|
|
2033
|
+
Here you can link KNX Group Addresses to the available Hue commands/states.<br/>
|
|
2034
|
+
Start typing in the GA field (name or Group Address); suggestions appear while you type.
|
|
2035
|
+
|
|
2036
|
+
**Switch**
|
|
2037
|
+
|
|
2038
|
+
|Property|Description|
|
|
2039
|
+
|--|--|
|
|
2040
|
+
| Control | This GA is used to turn on/off the Hue light via a boolean KNX value true/false|
|
|
2041
|
+
| Status | Link this to the light's switch status group address|
|
|
2042
|
+
|
|
2043
|
+
<br/>
|
|
2044
|
+
|
|
2045
|
+
**Dim**
|
|
2046
|
+
|
|
2047
|
+
|Property|Description|
|
|
2048
|
+
|--|--|
|
|
2049
|
+
| Control dim | Relative DIM of the Hue light. You can set the dimming speed in the **Behaviour** tab. |
|
|
2050
|
+
| Control % | Changes the absolute Hue light's brightness (0-100%)|
|
|
2051
|
+
| Status % | Link this to the light's brightness status KNX group address |
|
|
2052
|
+
| Dim Speed (ms) | Dimming speed in milliseconds. Applies to both the light brightness and the tunable-white datapoints. Calculated over the 0%→100% range. |
|
|
2053
|
+
| 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 %. |
|
|
2054
|
+
| Max Dim brightness | Tha Maximum brightness that the lamp can reach. For example, if you are dimming the light up, the light will stop dimming at the specified brightness %. |
|
|
2055
|
+
|
|
2056
|
+
<br/>
|
|
2057
|
+
|
|
2058
|
+
**Tunable White**
|
|
2059
|
+
|
|
2060
|
+
|Property|Description|
|
|
2061
|
+
|--|--|
|
|
2062
|
+
| Control dim | Change white temperature using DPT 3.007 dimming. Speed is set in the **Behaviour** tab.|
|
|
2063
|
+
| Control % | Change white temperature using DPT 5.001. 0 = full warm, 100 = full cold.|
|
|
2064
|
+
| Status %| Temperature status GA. DPT 5.001 absolute value: 0 = full warm, 100 = full cold.|
|
|
2065
|
+
| Control kelvin | **DPT 7.600: ** set temperature in Kelvin using the KNX range 2000-6535 (converted to Hue mirek).<br/>**DPT 9.002:** set temperature using Hue range 2000-6535 K (Ambiance starts at 2200 K). Conversions may introduce small deviations.|
|
|
2066
|
+
| Status kelvin | **DPT 7.600: ** read temperature in Kelvin using KNX range 2000-6535 (converted from Hue).<br/>**DPT 9.002:** read temperature using Hue range 2000-6535 K (Ambiance starts at 2200 K). Conversions may introduce small deviations. |
|
|
2067
|
+
| Invert dim direction | Inverts the DIM direction. |
|
|
2068
|
+
<br/>
|
|
2069
|
+
|
|
2070
|
+
**RGB/HSV**
|
|
2071
|
+
|
|
2072
|
+
|Property|Description|
|
|
2073
|
+
|--|--|
|
|
2074
|
+
| **RGB section** ||
|
|
2075
|
+
| Control rgb| Change color using RGB triplet (r,g,b). Gamut correction is handled. Sending a color turns the light on and sets color/brightness (perceptual). Sending r,g,b = 0 turns the light off. |
|
|
2076
|
+
| Status rgb | The light's color status group address. Accepted datapoint is RGB triplet (r,g,b)|
|
|
2077
|
+
| **HSV section** ||
|
|
2078
|
+
| Color H dim | Cycle through HSV hue using DPT 3.007 dimming. Speed is set in the **Behaviour** tab.|
|
|
2079
|
+
| Status H %| Status of the HSV chromatic circle.|
|
|
2080
|
+
| Control S dim | Changes the light's color saturation, using DPT 3.007 dimming. You can set the dimming speed in the **_Behaviour_** tab.|
|
|
2081
|
+
| Status S %| The light color saturation status group address.|
|
|
2082
|
+
| Dim Speed (ms) | The dimming speed, in Milliseconds, from bottom to top scale. |
|
|
2083
|
+
|
|
2084
|
+
For controlling the HSV "V” (brightness), use the standard controls under the **Dim** tab.
|
|
2085
|
+
|
|
2086
|
+
<br/>
|
|
2087
|
+
|
|
2088
|
+
**Effects**
|
|
2089
|
+
|
|
2090
|
+
_Non-Hue basic effects_
|
|
2091
|
+
|
|
2092
|
+
|Property|Description|
|
|
2093
|
+
|--|--|
|
|
2094
|
+
| Blink | _true_ Blink the light, _false_ Stop blinking. Blinks the light on and off. Useful for signalling. Works with all Hue lights. |
|
|
2095
|
+
| Color Cycle | _true_ start cycle, _false_ Stop cycle. Randomly changes the Hue light's color at regular interval. Works with all Hue lights having color capabilities. The color effect will start 10 seconds after set. |
|
|
2096
|
+
|
|
2097
|
+
<br/>
|
|
2098
|
+
|
|
2099
|
+
**Behaviour**
|
|
2100
|
+
|
|
2101
|
+
| Property | Description |
|
|
2102
|
+
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2103
|
+
| Read status at startup | Read the Hue light status at node-red's startup or node-red's full deploy, and send that status to the KNX BUS |
|
|
2104
|
+
| KNX Brightness Status | Updates the KNX brightness group address status, whenever the Hue lamp is switched ON/OFF. The options are **When Hue light is Off send 0%. When Hue On, restore previous value (Default KNX behaviour) ** and**Leave as is (default Hue behaviour) ** . If you have KNX dimmer with brightness status, like MDT, the suggested option is _**When Hue light is Off send 0%. When Hue On, restore previous value (Default KNX behaviour)** _ |
|
|
2105
|
+
| Update local cached Hue state from KNX bus writes | Advanced option, enabled by default. When enabled, writes arriving from the KNX bus also update the node's local cached Hue state immediately, without waiting for feedback/events from the Hue bridge. This gives faster local reactions and more consistent immediate KNX read responses, especially while the light or grouped light is OFF. Disable it if you prefer the cache to follow only real feedback/events from the Hue bridge. |
|
|
2106
|
+
| 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. |
|
|
2107
|
+
| 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.|
|
|
2108
|
+
| Day/Night | Select the group address used to set the day/night behaviour. The group address value is _true_ if daytime, _false_ if nighttime. |
|
|
2109
|
+
| Invert day/night value | Invert the values of _Day/Night_ group address. Default value is **unchecked** . |
|
|
2110
|
+
| Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
|
|
2111
|
+
| 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. |
|
|
2112
|
+
| Node Input/Output PINs | Hide or show the input/output PINs. Input/output PINS allow the node to accept msg input from the flow and send msg output to the flow. Input msg must follow the Hue API v.2 Standards. This is an example msg, that turns on the light: <code>msg.on = {"on":true}</code>. Please refer to the [official Hue Api page](https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put) |
|
|
2113
|
+
|
|
2114
|
+
### Note
|
|
2115
|
+
|
|
2116
|
+
The Dimming function works in **KNX mode `start` and`stop` ** . To start dimming, send only one "start" KNX telegram. To stop dimming, send a "stop" KNX telegram. Please**remember that** , when you set your wall swiches properties.
|
|
2117
|
+
|
|
2118
|
+
<br/>
|
|
2119
|
+
</script>
|