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 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
  ![Sample Node](img/readmemain.png)
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://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/c/knxuserforum.png)](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 (加群需要备注 “来自github](tencent://groupwpa/?subcmd=all&param=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
336
+ * [QQ group: 837579219 (加群需要备注 "来自github"](tencent://groupwpa/?subcmd=all&param=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
335
337
 
336
338
 
337
339
 
@@ -167,7 +167,7 @@
167
167
  <br />
168
168
  <br />
169
169
  <p align="center">
170
- <i class="fa-solid fa-battery-half fa-beat-fade fa-8x"></i>
170
+ <i class="fa-solid fa-battery-half fa-beat-fade fa-4x"></i>
171
171
  </p>
172
172
  <br />
173
173
  <label for="node-input-server" >
@@ -312,7 +312,7 @@
312
312
  <br />
313
313
  <br />
314
314
  <p align="center">
315
- <i class="fa-regular fa-circle-dot fa-beat-fade fa-8x"></i>
315
+ <i class="fa-regular fa-circle-dot fa-beat-fade fa-4x"></i>
316
316
  </p>
317
317
  <br />
318
318
  <label for="node-input-server" >
@@ -651,7 +651,7 @@
651
651
  label: function () {
652
652
  return this.name;
653
653
  },
654
- paletteLabel: "Hue Light (beta)",
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-8x"></i>
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-square-o"></i> Node Input/Output PINs
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>
@@ -165,7 +165,7 @@
165
165
  <br />
166
166
  <br />
167
167
  <p align="center">
168
- <i class="fa-solid fa-circle-half-stroke fa-8x"></i>
168
+ <i class="fa-solid fa-circle-half-stroke fa-4x"></i>
169
169
  </p>
170
170
  <br />
171
171
  <label for="node-input-server" >
@@ -164,7 +164,7 @@
164
164
  <br />
165
165
  <br />
166
166
  <p align="center">
167
- <i class="fa-solid fa-person-walking fa-shake fa-8x"></i>
167
+ <i class="fa-solid fa-person-walking fa-shake fa-4x"></i>
168
168
  </p>
169
169
  <br />
170
170
  <label for="node-input-server" >
@@ -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
- return (this.name);
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>&nbsp&nbsp<span style="color:red"
197
394
  &nbsp<i class="fa fa-youtube"></i></span>&nbsp<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-8x"></i>
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
- <p>
221
- <b>Philips HUE</b>
222
- </p>
417
+ <div id="tabs">
223
418
 
224
- <div class="form-row">
225
- <label for="node-input-hueDevice" >
226
- <i class="fa fa-play-circle"></i>&nbspHue Scene</label>
227
- <input type="text" id="node-input-name" placeholder="Enter your hue device name" />
228
- <input type="hidden" id="node-input-hueDevice" />
229
- </div>
230
- <div class="form-row">
231
- <label for="node-input-hueSceneRecallType">
232
- <i class="fa fa-minus-circle"></i> Recall as
233
- </label>
234
- <select id="node-input-hueSceneRecallType">
235
- <option value="active">Active</option>
236
- <option value="dynamic_palette">Dynamic</option>
237
- <option value="static">Static</option>
238
- </select>
239
- </div>
240
-
241
- <br/>
242
-
243
- <p>
244
- <b>KNX</b>
245
- </p>
246
-
247
- <div class="form-row">
248
- <label for="node-input-namescene" style="width:100px;"><i class="fa fa-play-circle-o"></i> Recall</label>
249
-
250
- <label for="node-input-GAscene" style="width:20px;">GA</label>
251
- <input type="text" id="node-input-GAscene" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
252
-
253
- <label for="node-input-dptscene" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
254
- <select id="node-input-dptscene" style="width:140px;"></select>
255
-
256
- <label for="node-input-namescene" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
257
- <input type="text" id="node-input-namescene" style="width:200px;margin-left: 5px; text-align: left;">
258
- </div>
259
-
260
- <div class="form-row" id="divValScene" hidden>
261
- <label for="node-input-valscene" style="width:100px;"></label>
262
- <label for="node-input-valscene" style="width:20px;">#</label>
263
- <select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
264
- </div>
265
-
266
- <br/>
267
- <br/>
268
-
269
- <p>
270
- <b>BEHAVIOUR</b>
271
- </p>
272
-
273
- <div class="form-row">
274
- <label for="node-input-enableNodePINS" style="width:240px;">
275
- <i class="fa fa-square-o"></i> Node Input/Output PINs
276
- </label>
277
- <select id="node-input-enableNodePINS">
278
- <option value="no">Hide</option>
279
- <option value="yes">Show node input/output PINs</option>
280
- </select>
281
-
282
- <br/>
283
- <br/>
284
- <br/>
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>&nbspHue 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
- 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.
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
- try {
46
- switch (msg.knx.destination) {
47
- case config.GAscene:
48
- msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptscene));
49
- if (config.dptscene.startsWith("1.")) {
50
- if (msg.payload === true) {
51
- state = { recall: { action: config.hueSceneRecallType } };
52
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene');
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
- // Turn off all light belonging to the scene
55
- (async () => {
56
- try {
57
- node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'stopScene');
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
- node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload });
70
- break;
71
- default:
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
 
@@ -166,7 +166,7 @@
166
166
  <br />
167
167
  <br />
168
168
  <p align="center">
169
- <i class="fa-solid fa-circle-notch fa-spin fa-8x"></i>
169
+ <i class="fa-solid fa-circle-notch fa-spin fa-4x"></i>
170
170
  </p>
171
171
  <br />
172
172
  <label for="node-input-server" >
@@ -165,7 +165,7 @@
165
165
  <br />
166
166
  <br />
167
167
  <p align="center">
168
- <i class="fa-solid fa-temperature-full fa-beat fa-8x"></i>
168
+ <i class="fa-solid fa-temperature-full fa-beat fa-4x"></i>
169
169
  </p>
170
170
  <br />
171
171
  <label for="node-input-server" >
package/package.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.29",
7
- "description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices control.",
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",