node-red-contrib-knx-ultimate 2.2.29 → 2.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/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/knxUltimateHueMotion.js +0 -2
- package/nodes/knxUltimateHueScene.html +378 -74
- package/nodes/knxUltimateHueScene.js +96 -30
- package/nodes/knxUltimateHueTapDial.html +1 -1
- package/nodes/knxUltimateHueTemperatureSensor.html +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.2.31** - December 2023<br/>
|
|
10
|
+
- NEW: HUE Scene node: added the status GA and Datapoint, for the scene to send true/false if active/not active. This currently works only for "Single mode".<br/>
|
|
11
|
+
- WARNING: the new HUE Scene node is to be considered **BETA (= in testing with user feedback)**.<br/>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
**Version 2.2.30** - December 2023<br/>
|
|
15
|
+
- NEW: HUE Scene node: added a "Multi scene" section, more powerful.<br/>
|
|
16
|
+
- HUE Scene: when selecting a group address for the scene, the scene number dropdown list doesn't show up.<br/>
|
|
17
|
+
- WARNING: the new HUE Light node is to be considered **RELEASED (= production ready, but please report anyway any issue)**.<br/>
|
|
18
|
+
- WARNING: the new HUE Scene node is to be considered **BETA (= in testing with user feedback)**.<br/>
|
|
19
|
+
|
|
9
20
|
**Version 2.2.29** - November 2023<br/>
|
|
10
21
|
This is an interim version, to quick fix some issues. Please report any issue with HUE Nodes, on gitHub.<br/>
|
|
11
22
|
- 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>
|
|
@@ -43,8 +43,6 @@ module.exports = function (RED) {
|
|
|
43
43
|
if (_event.id === config.hueDevice) {
|
|
44
44
|
|
|
45
45
|
if (!_event.hasOwnProperty("motion") || _event.motion.motion === undefined) return;
|
|
46
|
-
|
|
47
|
-
|
|
48
46
|
const knxMsgPayload = {};
|
|
49
47
|
knxMsgPayload.topic = config.GAmotion;
|
|
50
48
|
knxMsgPayload.dpt = config.dptmotion;
|
|
@@ -10,25 +10,39 @@
|
|
|
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: "" },
|
|
16
17
|
valscene: { value: 0 }, // the scene number or true/false
|
|
18
|
+
namesceneStatus: { value: "" },
|
|
19
|
+
GAsceneStatus: { value: "" },
|
|
20
|
+
dptsceneStatus: { value: "" },
|
|
17
21
|
|
|
18
22
|
enableNodePINS: { value: "no" },
|
|
19
23
|
outputs: { value: 0 },
|
|
20
24
|
inputs: { value: 0 },
|
|
21
25
|
|
|
22
26
|
hueDevice: { value: "" },
|
|
23
|
-
hueSceneRecallType: { value: "active" }
|
|
27
|
+
hueSceneRecallType: { value: "active" },
|
|
28
|
+
|
|
29
|
+
// Multi scene
|
|
30
|
+
GAsceneMulti: { value: "" },
|
|
31
|
+
namesceneMulti: { value: "" },
|
|
32
|
+
dptsceneMulti: { value: "" },
|
|
33
|
+
rules: { value: [{ t: "eq", v: "", vt: "str" }] },
|
|
34
|
+
selectedModeTabNumber: { value: 0 }
|
|
35
|
+
|
|
24
36
|
},
|
|
25
37
|
inputs: 0,
|
|
26
38
|
outputs: 0,
|
|
27
39
|
icon: "node-hue-icon.svg",
|
|
28
40
|
label: function () {
|
|
29
|
-
|
|
41
|
+
if (this.selectedModeTabNumber === undefined) return this.name;
|
|
42
|
+
if (Number(this.selectedModeTabNumber) === 0) return this.name;
|
|
43
|
+
if (Number(this.selectedModeTabNumber) === 1) return this.namesceneMulti;
|
|
30
44
|
},
|
|
31
|
-
paletteLabel: "Hue Scene",
|
|
45
|
+
paletteLabel: "Hue Scene (Beta)",
|
|
32
46
|
// button: {
|
|
33
47
|
// enabled: function() {
|
|
34
48
|
// // return whether or not the button is enabled, based on the current
|
|
@@ -89,8 +103,20 @@
|
|
|
89
103
|
.attr("value", dpt.value)
|
|
90
104
|
.text(dpt.text))
|
|
91
105
|
}
|
|
106
|
+
if (dpt.value.startsWith("18.")) {
|
|
107
|
+
$("#node-input-dptsceneMulti").append($("<option></option>")
|
|
108
|
+
.attr("value", dpt.value)
|
|
109
|
+
.text(dpt.text))
|
|
110
|
+
}
|
|
111
|
+
if (dpt.value.startsWith("1.")) {
|
|
112
|
+
$("#node-input-dptsceneStatus").append($("<option></option>")
|
|
113
|
+
.attr("value", dpt.value)
|
|
114
|
+
.text(dpt.text))
|
|
115
|
+
}
|
|
92
116
|
});
|
|
93
117
|
$("#node-input-dptscene").val(this.dptscene)
|
|
118
|
+
$("#node-input-dptsceneStatus").val(this.dptsceneStatus)
|
|
119
|
+
$("#node-input-dptsceneMulti").val(this.dptsceneMulti)
|
|
94
120
|
ShowHideValScene();
|
|
95
121
|
})
|
|
96
122
|
|
|
@@ -125,6 +151,73 @@
|
|
|
125
151
|
var optVal = $("#node-input-dptscene option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
126
152
|
// Select the option value
|
|
127
153
|
$("#node-input-dptscene").val(optVal);
|
|
154
|
+
ShowHideValScene();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
$("#node-input-GAsceneStatus").autocomplete({
|
|
158
|
+
minLength: 1,
|
|
159
|
+
source: function (request, response) {
|
|
160
|
+
//$.getJSON("csv", request, function( data, status, xhr ) {
|
|
161
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
|
|
162
|
+
response($.map(data, function (value, key) {
|
|
163
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
164
|
+
if (fullSearch(sSearch, request.term)) {
|
|
165
|
+
if (value.dpt.startsWith("1.")) {
|
|
166
|
+
return {
|
|
167
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
168
|
+
value: value.ga // Value
|
|
169
|
+
}
|
|
170
|
+
} else { return null; }
|
|
171
|
+
} else {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
});
|
|
176
|
+
}, select: function (event, ui) {
|
|
177
|
+
// Sets Datapoint and device name automatically
|
|
178
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
179
|
+
try {
|
|
180
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
}
|
|
183
|
+
$('#node-input-namesceneStatus').val(sDevName);
|
|
184
|
+
var optVal = $("#node-input-dptsceneStatus option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
185
|
+
// Select the option value
|
|
186
|
+
$("#node-input-dptsceneStatus").val(optVal);
|
|
187
|
+
ShowHideValScene();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
$("#node-input-GAsceneMulti").autocomplete({
|
|
191
|
+
minLength: 1,
|
|
192
|
+
source: function (request, response) {
|
|
193
|
+
//$.getJSON("csv", request, function( data, status, xhr ) {
|
|
194
|
+
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
|
|
195
|
+
response($.map(data, function (value, key) {
|
|
196
|
+
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
|
|
197
|
+
if (fullSearch(sSearch, request.term)) {
|
|
198
|
+
if (value.dpt.startsWith("18.")) {
|
|
199
|
+
return {
|
|
200
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
201
|
+
value: value.ga // Value
|
|
202
|
+
}
|
|
203
|
+
} else { return null; }
|
|
204
|
+
} else {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}));
|
|
208
|
+
});
|
|
209
|
+
}, select: function (event, ui) {
|
|
210
|
+
// Sets Datapoint and device name automatically
|
|
211
|
+
var sDevName = ui.item.label.split("#")[1].trim();
|
|
212
|
+
try {
|
|
213
|
+
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
}
|
|
216
|
+
$('#node-input-namesceneMulti').val(sDevName);
|
|
217
|
+
var optVal = $("#node-input-dptsceneMulti option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
|
|
218
|
+
// Select the option value
|
|
219
|
+
$("#node-input-dptsceneMulti").val(optVal);
|
|
220
|
+
ShowHideValScene();
|
|
128
221
|
}
|
|
129
222
|
});
|
|
130
223
|
// ########################
|
|
@@ -141,6 +234,7 @@
|
|
|
141
234
|
}
|
|
142
235
|
}
|
|
143
236
|
}
|
|
237
|
+
|
|
144
238
|
$("#node-input-dptscene").on("change", function () {
|
|
145
239
|
ShowHideValScene()
|
|
146
240
|
});
|
|
@@ -169,8 +263,138 @@
|
|
|
169
263
|
$('#node-input-hueDevice').val(ui.item.hueDevice);
|
|
170
264
|
}
|
|
171
265
|
});
|
|
172
|
-
// ########################
|
|
266
|
+
// ########################
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
// -----------------------------------------------------------------------
|
|
271
|
+
// MULTI SCENE
|
|
272
|
+
// ########################
|
|
273
|
+
function resizeRule(rule) { }
|
|
274
|
+
function setTableTitle(_selectedIndex) {
|
|
275
|
+
// Save only the tab 0 or 1
|
|
276
|
+
if (_selectedIndex === undefined) _selectedIndex = 0;
|
|
277
|
+
if (Number(_selectedIndex) <= 1) {
|
|
278
|
+
$("#node-input-selectedModeTabNumber").val(Number(_selectedIndex));
|
|
279
|
+
if (Number(_selectedIndex) === 0) {
|
|
280
|
+
$('#tabs ul:first li:eq(0) a').html('<i class="fa fa-check" aria-hidden="true"></i> Single mode');
|
|
281
|
+
$('#tabs ul:first li:eq(1) a').text('Multi mode');
|
|
282
|
+
} else if (Number(_selectedIndex) === 1) {
|
|
283
|
+
$('#tabs ul:first li:eq(0) a').text('Sigle mode');
|
|
284
|
+
$('#tabs ul:first li:eq(1) a').html('<i class="fa fa-check" aria-hidden="true"></i> Multi mode');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
$("#node-input-rule-container").css('min-height', '200px').css('min-width', '450px').editableList({
|
|
290
|
+
scrollOnAdd: true,
|
|
291
|
+
//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>")),
|
|
292
|
+
addItem: function (container, i, opt) { // row, index, data
|
|
293
|
+
// opt.r is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs}
|
|
294
|
+
|
|
295
|
+
if (!opt.hasOwnProperty('r')) {
|
|
296
|
+
opt.r = {};
|
|
297
|
+
}
|
|
298
|
+
var rule = opt.r;
|
|
299
|
+
if (!opt.hasOwnProperty('i')) {
|
|
300
|
+
opt._i = Math.floor((0x99999 - 0x10000) * Math.random()).toString();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
container.css({
|
|
304
|
+
overflow: 'hidden',
|
|
305
|
+
whiteSpace: 'nowrap'
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
var row = $('<div class="form-row"/>').appendTo(container);
|
|
309
|
+
var rowRuleKNXSceneNumber = $('<select/>', { class: "rowRuleKNXSceneNumber", type: "text", style: "width:25%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
310
|
+
var rowRuleHUESceneName = $("<input/>", { class: "rowRuleHUESceneName", type: "text", placeholder: "HUE device name", style: "width:45%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
311
|
+
var rowRuleHUESceneID = $("<input/>", { class: "rowRuleHUESceneID", type: "hidden", placeholder: "HUE device name", style: "width:0px; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
312
|
+
var rowRuleRecallAs = $('<select/>', { class: "rowRuleRecallAs", type: "text", style: "width:25%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
313
|
+
var finalspan = $('<span/>', { style: "" }).appendTo(row);
|
|
314
|
+
finalspan.append('<span class="node-input-rule-index"></span> ');
|
|
315
|
+
|
|
316
|
+
for (let index = 1; index < 64; index++) {
|
|
317
|
+
rowRuleKNXSceneNumber.append(
|
|
318
|
+
$("<option>")
|
|
319
|
+
.val(index)
|
|
320
|
+
.text("KNX Scene n." + index.toString())
|
|
321
|
+
);
|
|
322
|
+
rowRuleKNXSceneNumber.val(rule.rowRuleKNXSceneNumber);
|
|
323
|
+
}
|
|
324
|
+
rowRuleRecallAs.append(
|
|
325
|
+
$("<option>")
|
|
326
|
+
.val("active")
|
|
327
|
+
.text("Recall as Active")
|
|
328
|
+
);
|
|
329
|
+
rowRuleRecallAs.append(
|
|
330
|
+
$("<option>")
|
|
331
|
+
.val("dynamic_palette")
|
|
332
|
+
.text("Recall as Dynamic")
|
|
333
|
+
);
|
|
334
|
+
rowRuleRecallAs.append(
|
|
335
|
+
$("<option>")
|
|
336
|
+
.val("static")
|
|
337
|
+
.text("Recall as Static")
|
|
338
|
+
);
|
|
339
|
+
rowRuleRecallAs.val(rule.rowRuleRecallAs);
|
|
340
|
+
rowRuleHUESceneName.autocomplete({
|
|
341
|
+
minLength: 1,
|
|
342
|
+
source: function (request, response) {
|
|
343
|
+
$.getJSON("KNXUltimateGetResourcesHUE?rtype=scene&nodeID=" + oNodeServerHue.id, (data) => {
|
|
344
|
+
response($.map(data.devices, function (value, key) {
|
|
345
|
+
//alert(JSON.stringify(value) + " "+ key)
|
|
346
|
+
var sSearch = (value.name);
|
|
347
|
+
if (fullSearch(sSearch, request.term)) {
|
|
348
|
+
return {
|
|
349
|
+
hueDevice: value.id, // Label for Display
|
|
350
|
+
value: value.name // Value
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}));
|
|
356
|
+
});
|
|
357
|
+
}, select: function (event, ui) {
|
|
358
|
+
// Sets Datapoint and device name automatically
|
|
359
|
+
rowRuleHUESceneID.val(ui.item.hueDevice);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
rowRuleRecallAs.val(rule.rowRuleRecallAs)
|
|
363
|
+
rowRuleHUESceneName.val(rule.rowRuleHUESceneName);
|
|
364
|
+
rowRuleHUESceneID.val(rule.rowRuleHUESceneID);
|
|
365
|
+
},
|
|
366
|
+
removeItem: function (opt) {
|
|
367
|
+
|
|
368
|
+
},
|
|
369
|
+
resizeItem: resizeRule,
|
|
370
|
+
sortItems: function (rules) {
|
|
371
|
+
},
|
|
372
|
+
sortable: true,
|
|
373
|
+
removable: true
|
|
374
|
+
});
|
|
173
375
|
|
|
376
|
+
// Put some spaces after the container
|
|
377
|
+
$('<br/><br/><br/>').insertAfter($("#node-input-rule-container"));
|
|
378
|
+
|
|
379
|
+
// For each rule, create a row
|
|
380
|
+
if (this.rules !== undefined) {
|
|
381
|
+
for (var i = 0; i < this.rules.length; i++) {
|
|
382
|
+
var rule = this.rules[i];
|
|
383
|
+
$("#node-input-rule-container").editableList('addItem', { r: rule, i: i });
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ########################
|
|
388
|
+
// MULTI SCENE
|
|
389
|
+
// -----------------------------------------------------------------------
|
|
390
|
+
this.selectedModeTabNumber === undefined ? 0 : this.selectedModeTabNumber;
|
|
391
|
+
$("#tabs").tabs({
|
|
392
|
+
activate: function (event, ui) {
|
|
393
|
+
setTableTitle(ui.newTab.index());
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
$("#tabs").tabs("option", "active", this.selectedModeTabNumber);
|
|
397
|
+
setTableTitle(this.selectedModeTabNumber);
|
|
174
398
|
},
|
|
175
399
|
oneditsave: function () {
|
|
176
400
|
if ($("#node-input-enableNodePINS").val() === "yes") {
|
|
@@ -181,9 +405,24 @@
|
|
|
181
405
|
this.inputs = 0;
|
|
182
406
|
}
|
|
183
407
|
|
|
408
|
+
var node = this;
|
|
409
|
+
// opt.r is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs}
|
|
410
|
+
var rules = $("#node-input-rule-container").editableList('items');
|
|
411
|
+
node.rules = [];
|
|
412
|
+
rules.each(function (i) {
|
|
413
|
+
var rule = $(this);
|
|
414
|
+
var rowRuleKNXSceneNumber = rule.find(".rowRuleKNXSceneNumber").val();
|
|
415
|
+
var rowRuleHUESceneName = rule.find(".rowRuleHUESceneName").val();
|
|
416
|
+
var rowRuleHUESceneID = rule.find(".rowRuleHUESceneID").val();
|
|
417
|
+
var rowRuleRecallAs = rule.find(".rowRuleRecallAs").val();
|
|
418
|
+
node.rules.push({ rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID: rowRuleHUESceneID, rowRuleRecallAs: rowRuleRecallAs });
|
|
419
|
+
});
|
|
184
420
|
},
|
|
185
421
|
oneditcancel: function () {
|
|
186
422
|
|
|
423
|
+
},
|
|
424
|
+
oneditresize: function (size) {
|
|
425
|
+
|
|
187
426
|
}
|
|
188
427
|
})
|
|
189
428
|
|
|
@@ -191,14 +430,14 @@
|
|
|
191
430
|
|
|
192
431
|
<script type="text/html" data-template-name="knxUltimateHueScene">
|
|
193
432
|
|
|
194
|
-
|
|
195
433
|
<div class="form-row">
|
|
434
|
+
<input type="hidden" id="node-input-selectedModeTabNumber">
|
|
196
435
|
<b>HUE Scene node</b>  <span style="color:red"
|
|
197
436
|
 <i class="fa fa-youtube"></i></span> <a target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u>Youtube sample</u></a>
|
|
198
437
|
<br />
|
|
199
438
|
<br />
|
|
200
439
|
<p align="center">
|
|
201
|
-
<i class="fa fa-tv fa-fade fa-
|
|
440
|
+
<i class="fa fa-tv fa-fade fa-4x"></i>
|
|
202
441
|
</p>
|
|
203
442
|
<br />
|
|
204
443
|
<label for="node-input-server" >
|
|
@@ -217,96 +456,161 @@
|
|
|
217
456
|
</div>
|
|
218
457
|
|
|
219
458
|
<br/>
|
|
220
|
-
<
|
|
221
|
-
<b>Philips HUE</b>
|
|
222
|
-
</p>
|
|
459
|
+
<div id="tabs">
|
|
223
460
|
|
|
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
|
-
<div class="form-row">
|
|
274
|
-
<label for="node-input-
|
|
275
|
-
|
|
276
|
-
</label>
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
<
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
461
|
+
<ul>
|
|
462
|
+
<li><a href="#tabs-1"> Single scene</a></li>
|
|
463
|
+
<li><a href="#tabs-2"> Multi scene</a></li>
|
|
464
|
+
<li><a href="#tabs-3"> Behaviour</a></li>
|
|
465
|
+
</ul>
|
|
466
|
+
<div id="tabs-1">
|
|
467
|
+
<br/>
|
|
468
|
+
<p>
|
|
469
|
+
<b>Philips HUE</b>
|
|
470
|
+
</p>
|
|
471
|
+
|
|
472
|
+
<div class="form-row">
|
|
473
|
+
<label for="node-input-hueDevice" >
|
|
474
|
+
<i class="fa fa-play-circle"></i> Hue Scene</label>
|
|
475
|
+
<input type="text" id="node-input-name" placeholder="Enter your hue device name" />
|
|
476
|
+
<input type="hidden" id="node-input-hueDevice" />
|
|
477
|
+
</div>
|
|
478
|
+
<div class="form-row">
|
|
479
|
+
<label for="node-input-hueSceneRecallType">
|
|
480
|
+
<i class="fa fa-minus-circle"></i> Recall as
|
|
481
|
+
</label>
|
|
482
|
+
<select id="node-input-hueSceneRecallType">
|
|
483
|
+
<option value="active">Recall as Active</option>
|
|
484
|
+
<option value="dynamic_palette">Recall as Dynamic</option>
|
|
485
|
+
<option value="static">Recall as Static</option>
|
|
486
|
+
</select>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<br/>
|
|
490
|
+
<p>
|
|
491
|
+
<b>KNX</b>
|
|
492
|
+
</p>
|
|
493
|
+
<div class="form-row">
|
|
494
|
+
<label for="node-input-namescene" style="width:100px;"><i class="fa fa-play-circle-o"></i> Recall</label>
|
|
495
|
+
|
|
496
|
+
<label for="node-input-GAscene" style="width:20px;">GA</label>
|
|
497
|
+
<input type="text" id="node-input-GAscene" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
498
|
+
|
|
499
|
+
<label for="node-input-dptscene" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
500
|
+
<select id="node-input-dptscene" style="width:140px;"></select>
|
|
501
|
+
|
|
502
|
+
<label for="node-input-namescene" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
503
|
+
<input type="text" id="node-input-namescene" style="width:200px;margin-left: 5px; text-align: left;">
|
|
504
|
+
</div>
|
|
505
|
+
<div class="form-row" id="divValScene" hidden>
|
|
506
|
+
<label for="node-input-valscene" style="width:100px;"></label>
|
|
507
|
+
<label for="node-input-valscene" style="width:20px;">#</label>
|
|
508
|
+
<select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
|
|
509
|
+
</div>
|
|
510
|
+
<div class="form-row">
|
|
511
|
+
<label for="node-input-namesceneStatus" style="width:100px;"><i class="fa fa-play-circle-o"></i> Status</label>
|
|
512
|
+
|
|
513
|
+
<label for="node-input-GAsceneStatus" style="width:20px;">GA</label>
|
|
514
|
+
<input type="text" id="node-input-GAsceneStatus" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
515
|
+
|
|
516
|
+
<label for="node-input-dptsceneStatus" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
517
|
+
<select id="node-input-dptsceneStatus" style="width:140px;"></select>
|
|
518
|
+
|
|
519
|
+
<label for="node-input-namesceneStatus" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
520
|
+
<input type="text" id="node-input-namesceneStatus" style="width:200px;margin-left: 5px; text-align: left;">
|
|
521
|
+
</div>
|
|
522
|
+
<br/>
|
|
523
|
+
<br/>
|
|
524
|
+
</div> <!-- // End Tab 1 -->
|
|
525
|
+
|
|
526
|
+
<div id="tabs-2">
|
|
527
|
+
<br/>
|
|
528
|
+
<div class="form-row">
|
|
529
|
+
<label for="node-input-namesceneMulti" style="width:100px;"><i class="fa fa-play-circle-o"></i> Recall</label>
|
|
530
|
+
|
|
531
|
+
<label for="node-input-GAsceneMulti" style="width:20px;">GA</label>
|
|
532
|
+
<input type="text" id="node-input-GAsceneMulti" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
533
|
+
|
|
534
|
+
<label for="node-input-dptsceneMulti" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
535
|
+
<select id="node-input-dptsceneMulti" style="width:140px;"></select>
|
|
536
|
+
|
|
537
|
+
<label for="node-input-namesceneMulti" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
538
|
+
<input type="text" id="node-input-namesceneMulti" style="width:200px;margin-left: 5px; text-align: left;">
|
|
539
|
+
</div>
|
|
540
|
+
|
|
541
|
+
<div>
|
|
542
|
+
<dt><i class="fa fa-code-fork"></i> Scene selector</dt>
|
|
543
|
+
<div class="form-row node-input-rule-container-row">
|
|
544
|
+
<ol id="node-input-rule-container"></ol>
|
|
545
|
+
</div>
|
|
546
|
+
<div class="form-row">
|
|
547
|
+
<p></p>
|
|
548
|
+
</div>
|
|
549
|
+
</div>
|
|
550
|
+
<br/>
|
|
551
|
+
</div> <!-- // End Tab 2 -->
|
|
552
|
+
|
|
553
|
+
<div id="tabs-3">
|
|
554
|
+
<br/>
|
|
555
|
+
<div class="form-row">
|
|
556
|
+
<label for="node-input-enableNodePINS" style="width:240px;">
|
|
557
|
+
<i class="fa fa-circle"></i> Node Input/Output PINs
|
|
558
|
+
</label>
|
|
559
|
+
<select id="node-input-enableNodePINS">
|
|
560
|
+
<option value="no">Hide</option>
|
|
561
|
+
<option value="yes">Show node input/output PINs</option>
|
|
562
|
+
</select>
|
|
563
|
+
</div>
|
|
564
|
+
</div> <!-- // End Tab 3 -->
|
|
565
|
+
|
|
566
|
+
</div> <!-- // End TABS -->
|
|
286
567
|
|
|
287
568
|
</script>
|
|
288
569
|
|
|
289
570
|
<script type="text/markdown" data-help-name="knxUltimateHueScene">
|
|
290
571
|
This node lets you recall a HUE scene, via KNX.
|
|
291
572
|
|
|
292
|
-
|
|
573
|
+
The node has 2 operating options: Simple mode and Multi mode.
|
|
574
|
+
**Simple mode** is the simplest mode, where you can activate one HUE scene using either a boolean Datapoint or a KNX scene Datapoint.
|
|
575
|
+
**Multi mode** is the more powerful one, where for multiple KNX scene numbers, you can activate multiple HUE scenes.
|
|
576
|
+
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.
|
|
577
|
+
This works also with the HUE scene text field.
|
|
293
578
|
|
|
294
579
|
**General**
|
|
295
580
|
|Property|Description|
|
|
296
581
|
|--|--|
|
|
297
582
|
| KNX GW | Select the KNX gateway to be used |
|
|
298
583
|
| HUE Bridge | Select the HUE Bridge to be used |
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
**Single mode tab**
|
|
587
|
+
|Property|Description|
|
|
588
|
+
|--|--|
|
|
299
589
|
| Hue Scene | HUE scene to control. The avaiable scenes start showing up while you're typing.|
|
|
590
|
+
| Recall as | This sets the calling mode. |
|
|
300
591
|
|
|
301
592
|
**KNX**
|
|
302
|
-
|
|
303
593
|
|Property|Description|
|
|
304
594
|
|--|--|
|
|
305
595
|
| 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
596
|
| # | Select the KNX scene number. Visible only with datapoint 18.001. |
|
|
597
|
+
| Status | It's the scene status. *True* when the scene is active, *false* when the scene is not active. |
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
**Multi mode tab**
|
|
601
|
+
|Property|Description|
|
|
602
|
+
|--|--|
|
|
603
|
+
| Recall | Choose your group address and datapoint to be used for recalling the HUE scene. |
|
|
604
|
+
|
|
605
|
+
**Scene selector**
|
|
606
|
+
|
|
607
|
+
The scene selector list, contains all scenes, ativated for each KNX Scene number.
|
|
608
|
+
Click the <code>+add</code> button at the bottom of the list, to add a row.
|
|
609
|
+
Click the <code>X</code> on the far right of each row, to delete the row.
|
|
610
|
+
Drag the <code>toast</code> (the icon with three lines) at the far left of a row, to reorder the item in the list.
|
|
307
611
|
|
|
308
|
-
**BEHAVIOUR**
|
|
309
612
|
|
|
613
|
+
**Behaviour tab**
|
|
310
614
|
|Property|Description|
|
|
311
615
|
|--|--|
|
|
312
616
|
| 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,45 +51,102 @@ 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
|
|
|
79
115
|
node.handleSendHUE = (_event) => {
|
|
80
116
|
try {
|
|
81
|
-
if (_event.id === config.hueDevice) {
|
|
82
|
-
//
|
|
83
|
-
|
|
117
|
+
if (Number(config.selectedModeTabNumber) === 0 && config.hueDevice !== undefined && _event.id === config.hueDevice) {
|
|
118
|
+
// Single mode
|
|
119
|
+
const knxMsgPayload = {};
|
|
120
|
+
knxMsgPayload.topic = config.GAsceneStatus;
|
|
121
|
+
knxMsgPayload.dpt = config.dptsceneStatus;
|
|
122
|
+
if (_event.hasOwnProperty("status") && _event.status.hasOwnProperty("active")) {
|
|
123
|
+
knxMsgPayload.payload = _event.status.active !== "inactive";
|
|
124
|
+
// Send to KNX bus
|
|
125
|
+
if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined && node.server !== undefined) {
|
|
126
|
+
node.server.writeQueueAdd({
|
|
127
|
+
grpaddr: knxMsgPayload.topic,
|
|
128
|
+
payload: knxMsgPayload.payload,
|
|
129
|
+
dpt: knxMsgPayload.dpt,
|
|
130
|
+
outputtype: "write",
|
|
131
|
+
nodecallerid: node.id,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
node.status({
|
|
135
|
+
fill: "blue",
|
|
136
|
+
shape: "dot",
|
|
137
|
+
text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
|
|
138
|
+
});
|
|
139
|
+
// Output the msg to the flow
|
|
140
|
+
node.send(_event);
|
|
141
|
+
}
|
|
142
|
+
} else if (Number(config.selectedModeTabNumber) === 1 && config.rules !== undefined) {
|
|
143
|
+
// Multi mode
|
|
144
|
+
config.rules.forEach(row => {
|
|
145
|
+
if (row.rowRuleHUESceneID !== undefined && _event.id === row.rowRuleHUESceneID) {
|
|
146
|
+
// Output the msg to the flow
|
|
147
|
+
node.send(_event);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
84
150
|
}
|
|
85
151
|
} catch (error) {
|
|
86
152
|
node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
|
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.31",
|
|
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",
|