node-red-contrib-knx-ultimate 2.2.29 → 2.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +4 -2
- package/nodes/knxUltimateHueBattery.html +1 -1
- package/nodes/knxUltimateHueButton.html +1 -1
- package/nodes/knxUltimateHueLight.html +3 -3
- package/nodes/knxUltimateHueLightSensor.html +1 -1
- package/nodes/knxUltimateHueMotion.html +1 -1
- package/nodes/knxUltimateHueScene.html +323 -73
- package/nodes/knxUltimateHueScene.js +63 -27
- package/nodes/knxUltimateHueTapDial.html +1 -1
- package/nodes/knxUltimateHueTemperatureSensor.html +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.2.30** - December 2023<br/>
|
|
10
|
+
- NEW: HUE Scene node: added a "Multi scene" section, more powerful.<br/>
|
|
11
|
+
- HUE Scene: when selecting a group address for the scene, the scene number dropdown list doesn't show up.<br/>
|
|
12
|
+
- WARNING: the new HUE Light node is to be considered **RELEASED (= production ready, but please report anyway any issue)**.<br/>
|
|
13
|
+
- WARNING: the new HUE Scene node is to be considered **BETA (= in testing with user feedback)**.<br/>
|
|
14
|
+
|
|
9
15
|
**Version 2.2.29** - November 2023<br/>
|
|
10
16
|
This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
|
|
11
17
|
- HUE Light: fixed an issue causing the node status to signal an error. Filtered the groupvalue_read from imbound KNX messages.<br/>
|
package/README.md
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
|
-
Control your KNX intallation via Node-Red
|
|
16
|
+
Control your KNX intallation via Node-Red!<br/>
|
|
17
|
+
A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer.<br/>
|
|
18
|
+
Easy to use and highly configurable.
|
|
17
19
|
|
|
18
20
|
**You can use it immediately!**
|
|
19
21
|
|
|
@@ -331,7 +333,7 @@ List of commercial companies, which have given us permission to be mentioned on
|
|
|
331
333
|
* [](https://knx-user-forum.de/forum/öffentlicher-bereich/knx-eib-forum/1389088-knx-node-for-node-red)
|
|
332
334
|
|
|
333
335
|
**China**
|
|
334
|
-
* [QQ group: 837579219 (加群需要备注
|
|
336
|
+
* [QQ group: 837579219 (加群需要备注 "来自github"](tencent://groupwpa/?subcmd=all¶m=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
|
|
335
337
|
|
|
336
338
|
|
|
337
339
|
|
|
@@ -651,7 +651,7 @@
|
|
|
651
651
|
label: function () {
|
|
652
652
|
return this.name;
|
|
653
653
|
},
|
|
654
|
-
paletteLabel: "Hue Light
|
|
654
|
+
paletteLabel: "Hue Light",
|
|
655
655
|
oneditprepare: function () {
|
|
656
656
|
onEditPrepare(this);
|
|
657
657
|
},
|
|
@@ -758,7 +758,7 @@
|
|
|
758
758
|
<br />
|
|
759
759
|
<br />
|
|
760
760
|
<p align="center">
|
|
761
|
-
<i class="fa-regular fa-lightbulb fa-bounce fa-
|
|
761
|
+
<i class="fa-regular fa-lightbulb fa-bounce fa-4x"></i>
|
|
762
762
|
</p>
|
|
763
763
|
<br />
|
|
764
764
|
<label for="node-input-server">
|
|
@@ -1154,7 +1154,7 @@
|
|
|
1154
1154
|
</div>
|
|
1155
1155
|
<div class="form-row">
|
|
1156
1156
|
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
1157
|
-
<i class="fa fa-
|
|
1157
|
+
<i class="fa fa-circle"></i> Node Input/Output PINs
|
|
1158
1158
|
</label>
|
|
1159
1159
|
<select id="node-input-enableNodePINS">
|
|
1160
1160
|
<option value="no">Hide</option>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
serverHue: { type: "hue-config", required: true },
|
|
11
11
|
name: { value: "" },
|
|
12
12
|
|
|
13
|
+
// Single scene
|
|
13
14
|
namescene: { value: "" },
|
|
14
15
|
GAscene: { value: "" },
|
|
15
16
|
dptscene: { value: "" },
|
|
@@ -20,13 +21,23 @@
|
|
|
20
21
|
inputs: { value: 0 },
|
|
21
22
|
|
|
22
23
|
hueDevice: { value: "" },
|
|
23
|
-
hueSceneRecallType: { value: "active" }
|
|
24
|
+
hueSceneRecallType: { value: "active" },
|
|
25
|
+
|
|
26
|
+
// Multi scene
|
|
27
|
+
GAsceneMulti: { value: "" },
|
|
28
|
+
namesceneMulti: { value: "" },
|
|
29
|
+
dptsceneMulti: { value: "" },
|
|
30
|
+
rules: { value: [{ t: "eq", v: "", vt: "str" }] },
|
|
31
|
+
selectedModeTabNumber: { value: 0 }
|
|
32
|
+
|
|
24
33
|
},
|
|
25
34
|
inputs: 0,
|
|
26
35
|
outputs: 0,
|
|
27
36
|
icon: "node-hue-icon.svg",
|
|
28
37
|
label: function () {
|
|
29
|
-
|
|
38
|
+
if (this.selectedModeTabNumber === undefined) return this.name;
|
|
39
|
+
if (Number(this.selectedModeTabNumber) === 0) return this.name;
|
|
40
|
+
if (Number(this.selectedModeTabNumber) === 1) return this.namesceneMulti;
|
|
30
41
|
},
|
|
31
42
|
paletteLabel: "Hue Scene",
|
|
32
43
|
// button: {
|
|
@@ -89,8 +100,14 @@
|
|
|
89
100
|
.attr("value", dpt.value)
|
|
90
101
|
.text(dpt.text))
|
|
91
102
|
}
|
|
103
|
+
if (dpt.value.startsWith("18.")) {
|
|
104
|
+
$("#node-input-dptsceneMulti").append($("<option></option>")
|
|
105
|
+
.attr("value", dpt.value)
|
|
106
|
+
.text(dpt.text))
|
|
107
|
+
}
|
|
92
108
|
});
|
|
93
109
|
$("#node-input-dptscene").val(this.dptscene)
|
|
110
|
+
$("#node-input-dptsceneMulti").val(this.dptsceneMulti)
|
|
94
111
|
ShowHideValScene();
|
|
95
112
|
})
|
|
96
113
|
|
|
@@ -125,6 +142,40 @@
|
|
|
125
142
|
var optVal = $("#node-input-dptscene option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
126
143
|
// Select the option value
|
|
127
144
|
$("#node-input-dptscene").val(optVal);
|
|
145
|
+
ShowHideValScene();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
$("#node-input-GAsceneMulti").autocomplete({
|
|
149
|
+
minLength: 1,
|
|
150
|
+
source: function (request, response) {
|
|
151
|
+
//$.getJSON("csv", request, function( data, status, xhr ) {
|
|
152
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
|
|
153
|
+
response($.map(data, function (value, key) {
|
|
154
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
155
|
+
if (fullSearch(sSearch, request.term)) {
|
|
156
|
+
if (value.dpt.startsWith("18.")) {
|
|
157
|
+
return {
|
|
158
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
159
|
+
value: value.ga // Value
|
|
160
|
+
}
|
|
161
|
+
} else { return null; }
|
|
162
|
+
} else {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}));
|
|
166
|
+
});
|
|
167
|
+
}, select: function (event, ui) {
|
|
168
|
+
// Sets Datapoint and device name automatically
|
|
169
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
170
|
+
try {
|
|
171
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
}
|
|
174
|
+
$('#node-input-namesceneMulti').val(sDevName);
|
|
175
|
+
var optVal = $("#node-input-dptsceneMulti option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
176
|
+
// Select the option value
|
|
177
|
+
$("#node-input-dptsceneMulti").val(optVal);
|
|
178
|
+
ShowHideValScene();
|
|
128
179
|
}
|
|
129
180
|
});
|
|
130
181
|
// ########################
|
|
@@ -141,6 +192,7 @@
|
|
|
141
192
|
}
|
|
142
193
|
}
|
|
143
194
|
}
|
|
195
|
+
|
|
144
196
|
$("#node-input-dptscene").on("change", function () {
|
|
145
197
|
ShowHideValScene()
|
|
146
198
|
});
|
|
@@ -169,8 +221,138 @@
|
|
|
169
221
|
$('#node-input-hueDevice').val(ui.item.hueDevice);
|
|
170
222
|
}
|
|
171
223
|
});
|
|
172
|
-
// ########################
|
|
224
|
+
// ########################
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
// -----------------------------------------------------------------------
|
|
229
|
+
// MULTI SCENE
|
|
230
|
+
// ########################
|
|
231
|
+
function resizeRule(rule) { }
|
|
232
|
+
function setTableTitle(_selectedIndex) {
|
|
233
|
+
// Save only the tab 0 or 1
|
|
234
|
+
if (_selectedIndex === undefined) _selectedIndex = 0;
|
|
235
|
+
if (Number(_selectedIndex) <= 1) {
|
|
236
|
+
$("#node-input-selectedModeTabNumber").val(Number(_selectedIndex));
|
|
237
|
+
if (Number(_selectedIndex) === 0) {
|
|
238
|
+
$('#tabs ul:first li:eq(0) a').html('<i class="fa fa-check" aria-hidden="true"></i> Single mode');
|
|
239
|
+
$('#tabs ul:first li:eq(1) a').text('Multi mode');
|
|
240
|
+
} else if (Number(_selectedIndex) === 1) {
|
|
241
|
+
$('#tabs ul:first li:eq(0) a').text('Sigle mode');
|
|
242
|
+
$('#tabs ul:first li:eq(1) a').html('<i class="fa fa-check" aria-hidden="true"></i> Multi mode');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
$("#node-input-rule-container").css('min-height', '200px').css('min-width', '450px').editableList({
|
|
248
|
+
scrollOnAdd: true,
|
|
249
|
+
//header: $("<div>").append($.parseHTML("<div style='width:5%; display: inline-grid'>Sort</div><div style='width:15%; display: inline-grid'>KNX Scene number</div><div style='width:60%; display: inline-grid'>HUE Scene name</div><div style='width:15%; display: inline-grid'>Recall scene as</div><div style='width:5%; display: inline-grid'>Delete</div>")),
|
|
250
|
+
addItem: function (container, i, opt) { // row, index, data
|
|
251
|
+
// opt.r is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs}
|
|
252
|
+
|
|
253
|
+
if (!opt.hasOwnProperty('r')) {
|
|
254
|
+
opt.r = {};
|
|
255
|
+
}
|
|
256
|
+
var rule = opt.r;
|
|
257
|
+
if (!opt.hasOwnProperty('i')) {
|
|
258
|
+
opt._i = Math.floor((0x99999 - 0x10000) * Math.random()).toString();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
container.css({
|
|
262
|
+
overflow: 'hidden',
|
|
263
|
+
whiteSpace: 'nowrap'
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
var row = $('<div class="form-row"/>').appendTo(container);
|
|
267
|
+
var rowRuleKNXSceneNumber = $('<select/>', { class: "rowRuleKNXSceneNumber", type: "text", style: "width:25%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
268
|
+
var rowRuleHUESceneName = $("<input/>", { class: "rowRuleHUESceneName", type: "text", placeholder: "HUE device name", style: "width:45%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
269
|
+
var rowRuleHUESceneID = $("<input/>", { class: "rowRuleHUESceneID", type: "hidden", placeholder: "HUE device name", style: "width:0px; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
270
|
+
var rowRuleRecallAs = $('<select/>', { class: "rowRuleRecallAs", type: "text", style: "width:25%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
271
|
+
var finalspan = $('<span/>', { style: "" }).appendTo(row);
|
|
272
|
+
finalspan.append('<span class="node-input-rule-index"></span> ');
|
|
273
|
+
|
|
274
|
+
for (let index = 1; index < 64; index++) {
|
|
275
|
+
rowRuleKNXSceneNumber.append(
|
|
276
|
+
$("<option>")
|
|
277
|
+
.val(index)
|
|
278
|
+
.text("KNX Scene n." + index.toString())
|
|
279
|
+
);
|
|
280
|
+
rowRuleKNXSceneNumber.val(rule.rowRuleKNXSceneNumber);
|
|
281
|
+
}
|
|
282
|
+
rowRuleRecallAs.append(
|
|
283
|
+
$("<option>")
|
|
284
|
+
.val("active")
|
|
285
|
+
.text("Recall as Active")
|
|
286
|
+
);
|
|
287
|
+
rowRuleRecallAs.append(
|
|
288
|
+
$("<option>")
|
|
289
|
+
.val("dynamic_palette")
|
|
290
|
+
.text("Recall as Dynamic")
|
|
291
|
+
);
|
|
292
|
+
rowRuleRecallAs.append(
|
|
293
|
+
$("<option>")
|
|
294
|
+
.val("static")
|
|
295
|
+
.text("Recall as Static")
|
|
296
|
+
);
|
|
297
|
+
rowRuleRecallAs.val(rule.rowRuleRecallAs);
|
|
298
|
+
rowRuleHUESceneName.autocomplete({
|
|
299
|
+
minLength: 1,
|
|
300
|
+
source: function (request, response) {
|
|
301
|
+
$.getJSON("KNXUltimateGetResourcesHUE?rtype=scene&nodeID=" + oNodeServerHue.id, (data) => {
|
|
302
|
+
response($.map(data.devices, function (value, key) {
|
|
303
|
+
//alert(JSON.stringify(value) + " "+ key)
|
|
304
|
+
var sSearch = (value.name);
|
|
305
|
+
if (fullSearch(sSearch, request.term)) {
|
|
306
|
+
return {
|
|
307
|
+
hueDevice: value.id, // Label for Display
|
|
308
|
+
value: value.name // Value
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}));
|
|
314
|
+
});
|
|
315
|
+
}, select: function (event, ui) {
|
|
316
|
+
// Sets Datapoint and device name automatically
|
|
317
|
+
rowRuleHUESceneID.val(ui.item.hueDevice);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
rowRuleRecallAs.val(rule.rowRuleRecallAs)
|
|
321
|
+
rowRuleHUESceneName.val(rule.rowRuleHUESceneName);
|
|
322
|
+
rowRuleHUESceneID.val(rule.rowRuleHUESceneID);
|
|
323
|
+
},
|
|
324
|
+
removeItem: function (opt) {
|
|
325
|
+
|
|
326
|
+
},
|
|
327
|
+
resizeItem: resizeRule,
|
|
328
|
+
sortItems: function (rules) {
|
|
329
|
+
},
|
|
330
|
+
sortable: true,
|
|
331
|
+
removable: true
|
|
332
|
+
});
|
|
173
333
|
|
|
334
|
+
// Put some spaces after the container
|
|
335
|
+
$('<br/><br/><br/>').insertAfter($("#node-input-rule-container"));
|
|
336
|
+
|
|
337
|
+
// For each rule, create a row
|
|
338
|
+
if (this.rules !== undefined) {
|
|
339
|
+
for (var i = 0; i < this.rules.length; i++) {
|
|
340
|
+
var rule = this.rules[i];
|
|
341
|
+
$("#node-input-rule-container").editableList('addItem', { r: rule, i: i });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ########################
|
|
346
|
+
// MULTI SCENE
|
|
347
|
+
// -----------------------------------------------------------------------
|
|
348
|
+
this.selectedModeTabNumber === undefined ? 0 : this.selectedModeTabNumber;
|
|
349
|
+
$("#tabs").tabs({
|
|
350
|
+
activate: function (event, ui) {
|
|
351
|
+
setTableTitle(ui.newTab.index());
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
$("#tabs").tabs("option", "active", this.selectedModeTabNumber);
|
|
355
|
+
setTableTitle(this.selectedModeTabNumber);
|
|
174
356
|
},
|
|
175
357
|
oneditsave: function () {
|
|
176
358
|
if ($("#node-input-enableNodePINS").val() === "yes") {
|
|
@@ -181,9 +363,24 @@
|
|
|
181
363
|
this.inputs = 0;
|
|
182
364
|
}
|
|
183
365
|
|
|
366
|
+
var node = this;
|
|
367
|
+
// opt.r is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs}
|
|
368
|
+
var rules = $("#node-input-rule-container").editableList('items');
|
|
369
|
+
node.rules = [];
|
|
370
|
+
rules.each(function (i) {
|
|
371
|
+
var rule = $(this);
|
|
372
|
+
var rowRuleKNXSceneNumber = rule.find(".rowRuleKNXSceneNumber").val();
|
|
373
|
+
var rowRuleHUESceneName = rule.find(".rowRuleHUESceneName").val();
|
|
374
|
+
var rowRuleHUESceneID = rule.find(".rowRuleHUESceneID").val();
|
|
375
|
+
var rowRuleRecallAs = rule.find(".rowRuleRecallAs").val();
|
|
376
|
+
node.rules.push({ rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID: rowRuleHUESceneID, rowRuleRecallAs: rowRuleRecallAs });
|
|
377
|
+
});
|
|
184
378
|
},
|
|
185
379
|
oneditcancel: function () {
|
|
186
380
|
|
|
381
|
+
},
|
|
382
|
+
oneditresize: function (size) {
|
|
383
|
+
|
|
187
384
|
}
|
|
188
385
|
})
|
|
189
386
|
|
|
@@ -191,14 +388,14 @@
|
|
|
191
388
|
|
|
192
389
|
<script type="text/html" data-template-name="knxUltimateHueScene">
|
|
193
390
|
|
|
194
|
-
|
|
195
391
|
<div class="form-row">
|
|
392
|
+
<input type="hidden" id="node-input-selectedModeTabNumber">
|
|
196
393
|
<b>HUE Scene node</b>  <span style="color:red"
|
|
197
394
|
 <i class="fa fa-youtube"></i></span> <a target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u>Youtube sample</u></a>
|
|
198
395
|
<br />
|
|
199
396
|
<br />
|
|
200
397
|
<p align="center">
|
|
201
|
-
<i class="fa fa-tv fa-fade fa-
|
|
398
|
+
<i class="fa fa-tv fa-fade fa-4x"></i>
|
|
202
399
|
</p>
|
|
203
400
|
<br />
|
|
204
401
|
<label for="node-input-server" >
|
|
@@ -217,96 +414,149 @@
|
|
|
217
414
|
</div>
|
|
218
415
|
|
|
219
416
|
<br/>
|
|
220
|
-
<
|
|
221
|
-
<b>Philips HUE</b>
|
|
222
|
-
</p>
|
|
417
|
+
<div id="tabs">
|
|
223
418
|
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
<
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<
|
|
242
|
-
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
</
|
|
277
|
-
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
<
|
|
284
|
-
<
|
|
285
|
-
|
|
419
|
+
<ul>
|
|
420
|
+
<li><a href="#tabs-1"> Single scene</a></li>
|
|
421
|
+
<li><a href="#tabs-2"> Multi scene</a></li>
|
|
422
|
+
<li><a href="#tabs-3"> Behaviour</a></li>
|
|
423
|
+
</ul>
|
|
424
|
+
<div id="tabs-1">
|
|
425
|
+
<br/>
|
|
426
|
+
<p>
|
|
427
|
+
<b>Philips HUE</b>
|
|
428
|
+
</p>
|
|
429
|
+
|
|
430
|
+
<div class="form-row">
|
|
431
|
+
<label for="node-input-hueDevice" >
|
|
432
|
+
<i class="fa fa-play-circle"></i> Hue Scene</label>
|
|
433
|
+
<input type="text" id="node-input-name" placeholder="Enter your hue device name" />
|
|
434
|
+
<input type="hidden" id="node-input-hueDevice" />
|
|
435
|
+
</div>
|
|
436
|
+
<div class="form-row">
|
|
437
|
+
<label for="node-input-hueSceneRecallType">
|
|
438
|
+
<i class="fa fa-minus-circle"></i> Recall as
|
|
439
|
+
</label>
|
|
440
|
+
<select id="node-input-hueSceneRecallType">
|
|
441
|
+
<option value="active">Recall as Active</option>
|
|
442
|
+
<option value="dynamic_palette">Recall as Dynamic</option>
|
|
443
|
+
<option value="static">Recall as Static</option>
|
|
444
|
+
</select>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<br/>
|
|
448
|
+
<p>
|
|
449
|
+
<b>KNX</b>
|
|
450
|
+
</p>
|
|
451
|
+
<div class="form-row">
|
|
452
|
+
<label for="node-input-namescene" style="width:100px;"><i class="fa fa-play-circle-o"></i> Recall</label>
|
|
453
|
+
|
|
454
|
+
<label for="node-input-GAscene" style="width:20px;">GA</label>
|
|
455
|
+
<input type="text" id="node-input-GAscene" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
456
|
+
|
|
457
|
+
<label for="node-input-dptscene" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
458
|
+
<select id="node-input-dptscene" style="width:140px;"></select>
|
|
459
|
+
|
|
460
|
+
<label for="node-input-namescene" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
461
|
+
<input type="text" id="node-input-namescene" style="width:200px;margin-left: 5px; text-align: left;">
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
<div class="form-row" id="divValScene" hidden>
|
|
465
|
+
<label for="node-input-valscene" style="width:100px;"></label>
|
|
466
|
+
<label for="node-input-valscene" style="width:20px;">#</label>
|
|
467
|
+
<select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
|
|
468
|
+
</div>
|
|
469
|
+
<br/>
|
|
470
|
+
<br/>
|
|
471
|
+
</div> <!-- // End Tab 1 -->
|
|
472
|
+
|
|
473
|
+
<div id="tabs-2">
|
|
474
|
+
<br/>
|
|
475
|
+
<div class="form-row">
|
|
476
|
+
<label for="node-input-namesceneMulti" style="width:100px;"><i class="fa fa-play-circle-o"></i> Recall</label>
|
|
477
|
+
|
|
478
|
+
<label for="node-input-GAsceneMulti" style="width:20px;">GA</label>
|
|
479
|
+
<input type="text" id="node-input-GAsceneMulti" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
480
|
+
|
|
481
|
+
<label for="node-input-dptsceneMulti" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
482
|
+
<select id="node-input-dptsceneMulti" style="width:140px;"></select>
|
|
483
|
+
|
|
484
|
+
<label for="node-input-namesceneMulti" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
485
|
+
<input type="text" id="node-input-namesceneMulti" style="width:200px;margin-left: 5px; text-align: left;">
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
<div>
|
|
489
|
+
<dt><i class="fa fa-code-fork"></i> Scene selector</dt>
|
|
490
|
+
<div class="form-row node-input-rule-container-row">
|
|
491
|
+
<ol id="node-input-rule-container"></ol>
|
|
492
|
+
</div>
|
|
493
|
+
<div class="form-row">
|
|
494
|
+
<p></p>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
<br/>
|
|
498
|
+
</div> <!-- // End Tab 2 -->
|
|
499
|
+
|
|
500
|
+
<div id="tabs-3">
|
|
501
|
+
<br/>
|
|
502
|
+
<div class="form-row">
|
|
503
|
+
<label for="node-input-enableNodePINS" style="width:240px;">
|
|
504
|
+
<i class="fa fa-circle"></i> Node Input/Output PINs
|
|
505
|
+
</label>
|
|
506
|
+
<select id="node-input-enableNodePINS">
|
|
507
|
+
<option value="no">Hide</option>
|
|
508
|
+
<option value="yes">Show node input/output PINs</option>
|
|
509
|
+
</select>
|
|
510
|
+
</div>
|
|
511
|
+
</div> <!-- // End Tab 3 -->
|
|
512
|
+
|
|
513
|
+
</div> <!-- // End TABS -->
|
|
286
514
|
|
|
287
515
|
</script>
|
|
288
516
|
|
|
289
517
|
<script type="text/markdown" data-help-name="knxUltimateHueScene">
|
|
290
518
|
This node lets you recall a HUE scene, via KNX.
|
|
291
519
|
|
|
292
|
-
|
|
520
|
+
The node has 2 operating options: Simple mode and Multi mode.
|
|
521
|
+
**Simple mode** is the simplest mode, where you can activate one HUE scene using either a boolean Datapoint or a KNX scene Datapoint.
|
|
522
|
+
**Multi mode** is the more powerful one, where for multiple KNX scene numbers, you can activate multiple HUE scenes.
|
|
523
|
+
Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
|
|
524
|
+
This works also with the HUE scene text field.
|
|
293
525
|
|
|
294
526
|
**General**
|
|
295
527
|
|Property|Description|
|
|
296
528
|
|--|--|
|
|
297
529
|
| KNX GW | Select the KNX gateway to be used |
|
|
298
530
|
| HUE Bridge | Select the HUE Bridge to be used |
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
**Single mode tab**
|
|
534
|
+
|Property|Description|
|
|
535
|
+
|--|--|
|
|
299
536
|
| Hue Scene | HUE scene to control. The avaiable scenes start showing up while you're typing.|
|
|
537
|
+
| Recall as | This sets the calling mode. |
|
|
300
538
|
|
|
301
539
|
**KNX**
|
|
302
|
-
|
|
303
540
|
|Property|Description|
|
|
304
541
|
|--|--|
|
|
305
542
|
| Recall | Choose your group address to be used for recalling the HUE scene. In case of Datapoint 1.x, send *true* to that group address to recall the scene, *false* to switch off all lights belonging to the scene. |
|
|
306
543
|
| # | Select the KNX scene number. Visible only with datapoint 18.001. |
|
|
307
544
|
|
|
308
|
-
**BEHAVIOUR**
|
|
309
545
|
|
|
546
|
+
**Multi mode tab**
|
|
547
|
+
|Property|Description|
|
|
548
|
+
|--|--|
|
|
549
|
+
| Recall | Choose your group address and datapoint to be used for recalling the HUE scene. |
|
|
550
|
+
|
|
551
|
+
**Scene selector**
|
|
552
|
+
|
|
553
|
+
The scene selector list, contains all scenes, ativated for each KNX Scene number.
|
|
554
|
+
Click the <code>+add</code> button at the bottom of the list, to add a row.
|
|
555
|
+
Click the <code>X</code> on the far right of each row, to delete the row.
|
|
556
|
+
Drag the <code>toast</code> (the icon with three lines) at the far left of a row, to reorder the item in the list.
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
**Behaviour tab**
|
|
310
560
|
|Property|Description|
|
|
311
561
|
|--|--|
|
|
312
562
|
| 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. Please refer to the [official HUE Api page](https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put) |
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
const dptlib = require('./../KNXEngine/src/dptlib');
|
|
2
3
|
|
|
3
4
|
module.exports = function (RED) {
|
|
@@ -26,6 +27,14 @@ module.exports = function (RED) {
|
|
|
26
27
|
node.formatdecimalsvalue = 2;
|
|
27
28
|
node.hueDevice = config.hueDevice;
|
|
28
29
|
node.initializingAtStart = false;
|
|
30
|
+
node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.server.loglevel || 'error' }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
31
|
+
|
|
32
|
+
// Multi scene
|
|
33
|
+
config.GAsceneMulti = config.GAsceneMulti === undefined ? '' : config.GAsceneMulti;
|
|
34
|
+
config.namesceneMulti = config.namesceneMulti === undefined ? '' : config.namesceneMulti;
|
|
35
|
+
config.dptsceneMulti = config.dptsceneMulti === undefined ? '' : config.dptsceneMulti;
|
|
36
|
+
config.rules = config.rules === undefined ? [] : config.rules;
|
|
37
|
+
config.selectedModeTabNumber = config.selectedModeTabNumber === undefined ? 0 : Number(config.selectedModeTabNumber); // Transform as number
|
|
29
38
|
|
|
30
39
|
// Used to call the status update from the config node.
|
|
31
40
|
node.setNodeStatus = ({ fill, shape, text, payload }) => {
|
|
@@ -42,37 +51,64 @@ module.exports = function (RED) {
|
|
|
42
51
|
// This function is called by the knx-hue config node
|
|
43
52
|
node.handleSend = msg => {
|
|
44
53
|
let state = {};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
if (config.selectedModeTabNumber === 0) {
|
|
55
|
+
// Sigle
|
|
56
|
+
try {
|
|
57
|
+
switch (msg.knx.destination) {
|
|
58
|
+
case config.GAscene:
|
|
59
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptscene));
|
|
60
|
+
if (config.dptscene.startsWith("1.")) {
|
|
61
|
+
if (msg.payload === true) {
|
|
62
|
+
state = { recall: { action: config.hueSceneRecallType } };
|
|
63
|
+
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene');
|
|
64
|
+
} else {
|
|
65
|
+
// Turn off all light belonging to the scene
|
|
66
|
+
(async () => {
|
|
67
|
+
try {
|
|
68
|
+
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'stopScene');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('KNXUltimateHUEConfig: classHUE: handleQueue: stopScene: ' + error.message);
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
}
|
|
53
74
|
} else {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} catch (error) {
|
|
59
|
-
RED.log.error('KNXUltimateHUEConfig: classHUE: handleQueue: stopScene: ' + error.message);
|
|
60
|
-
}
|
|
61
|
-
})();
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
if (Number(config.valscene) === msg.payload.scenenumber && msg.payload.save_recall === 0) {
|
|
65
|
-
state = { recall: { action: config.hueSceneRecallType } };
|
|
66
|
-
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene');
|
|
75
|
+
if (Number(config.valscene) === msg.payload.scenenumber && msg.payload.save_recall === 0) {
|
|
76
|
+
state = { recall: { action: config.hueSceneRecallType } };
|
|
77
|
+
node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene');
|
|
78
|
+
}
|
|
67
79
|
}
|
|
80
|
+
node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload });
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE single: ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
87
|
+
}
|
|
88
|
+
} else if (config.selectedModeTabNumber === 1) {
|
|
89
|
+
// Multi
|
|
90
|
+
try {
|
|
91
|
+
if (config.GAsceneMulti !== undefined && config.dptsceneMulti !== undefined && config.rules.length !== 0) {
|
|
92
|
+
if (config.GAsceneMulti === msg.knx.destination) {
|
|
93
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptsceneMulti));
|
|
94
|
+
// row is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs}
|
|
95
|
+
config.rules.forEach(row => {
|
|
96
|
+
if (row.rowRuleKNXSceneNumber !== undefined
|
|
97
|
+
&& row.rowRuleHUESceneID !== undefined
|
|
98
|
+
&& row.rowRuleRecallAs !== undefined
|
|
99
|
+
&& Number(row.rowRuleKNXSceneNumber) === Number(msg.payload.scenenumber)
|
|
100
|
+
&& Number(msg.payload.save_recall) === 0
|
|
101
|
+
&& node.serverHue.hueManager !== undefined) {
|
|
102
|
+
state = { recall: { action: row.rowRuleRecallAs } };
|
|
103
|
+
node.serverHue.hueManager.writeHueQueueAdd(row.rowRuleHUESceneID, state, 'setScene');
|
|
104
|
+
node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
68
107
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
break;
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE multi: ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
73
111
|
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
|
76
112
|
}
|
|
77
113
|
};
|
|
78
114
|
|
package/package.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.2.
|
|
7
|
-
"description": "Control your KNX intallation via Node-Red!
|
|
6
|
+
"version": "2.2.30",
|
|
7
|
+
"description": "Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"binary-parser": "2.2.1",
|
|
10
10
|
"crypto-js": "4.2.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"eib",
|
|
55
55
|
"konnex",
|
|
56
56
|
"IOT",
|
|
57
|
-
"hue"
|
|
57
|
+
"philips hue"
|
|
58
58
|
],
|
|
59
59
|
"author": "Supergiovane",
|
|
60
60
|
"license": "MIT",
|