node-red-contrib-knx-ultimate 2.0.8 → 2.0.9

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
@@ -7,9 +7,9 @@
7
7
  # CHANGELOG
8
8
 
9
9
  <p>
10
- <b>Version 2.0.7</b> - June 2023<br/>
10
+ <b>Version 2.0.9</b> - June 2023<br/>
11
11
  - NEW: HUE Motion node<br/>
12
- </p>
12
+ - NEW: HUE Tap Dial node<br/></p>
13
13
  <p>
14
14
  <b>Version 2.0.7</b> - June 2023<br/>
15
15
  - HUE Button node: added an output PIN<br/>
Binary file
@@ -32,7 +32,6 @@
32
32
  GAlong_press: { value: "" },
33
33
  dptlong_press: { value: "" },
34
34
 
35
- outputSimpleMode: { value: true },
36
35
  toggleValues: { value: true },
37
36
  hueDevice: { value: "" }
38
37
  },
@@ -534,11 +533,7 @@
534
533
  Toggle values
535
534
  </label>
536
535
  </div>
537
- <div class="form-row">
538
- <input type="checkbox" id="node-input-outputSimpleMode" style="display:inline-block; width:auto; vertical-align:top;" />
539
- <label style="width:auto" for="node-input-outputSimpleMode"><i class="fa fa-filter">Simple output mode</i></label>
540
- </div>
541
-
536
+
542
537
  </div>
543
538
 
544
539
 
@@ -557,10 +552,12 @@
557
552
  <script type="text/markdown" data-help-name="knxUltimateHueButton">
558
553
  <p> This node lets you get the events from your HUE button.<br/>
559
554
 
560
- There are many event you can choose from. The relevants one are *short release* and *repeat*.<br/>
561
- In *short release* you can send true/false to your KNX group address to, for example, toggle a light.<br/>
562
- In *repeat* event, you can DIM a KNX light.<br/>
563
- The **toggle values** option is enabled by default. This option toggles the value of each KNX group address (*true/false, increase/decrease dim*)
555
+ There are many event you can choose from. The relevants one are *Initial press* and *repeat*.
556
+ In *Initial press* you can send true/false to your KNX group address to, for example, toggle a light.
557
+ In *repeat* event, you can DIM a KNX light.
558
+ The **toggle values** option is enabled by default. This option toggles the value of each KNX group address (*true/false, increase/decrease dim*)
559
+ If you want to simply switch on and off a KNX load or light, use the *Initial press* group address and be sure that the *Toggle values* is enabled.
560
+
564
561
 
565
562
  **General**
566
563
  |Property|Description|
@@ -578,12 +575,11 @@ Start typing in the GA field, the name or group address of your KNX device, the
578
575
  |Property|Description|
579
576
  |--|--|
580
577
  | Initial press | As soon as you press your HUE button, this event fires|
581
- | Repeat | This command is used either to send DIM (increase/decrease) or true/false commands to the KNX group address |
578
+ | Repeat | This event is used either to send DIM (increase/decrease) or true/false commands to the KNX group address |
582
579
  | Short release | Rapid push of the button, fired as soon as you release your finger from the button. Usually used to toggle a KNX light true/false |
583
580
  | Long release | Long press of the button, fired as soon as you release your finger from the button |
584
581
  | Long press | Long press of the button, fired as soon as you long press the button |
585
582
  | Toggle values | Enable or disable toggling values. If enabled, all values toggles, otherwise, all values are sent as *true* or *increase dim*, to the selected KNX group address |
586
- | Simple output mode | Enables an RBE output on the payload *true/false*, independently from the HUE Button events type. *Repeat* messages are always repeated. When disabled, the node outputs a msg for every different HUE Button events |
587
583
 
588
584
  ### Outputs
589
585
 
@@ -1,20 +1,5 @@
1
1
  module.exports = function (RED) {
2
2
 
3
- async function getLightState(node, _lightID) {
4
- return new Promise((resolve, reject) => {
5
- try {
6
- if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
7
- node.serverHue.hueManager.getLight(_lightID).then(ret => {
8
- node.currentHUEDevice = ret[0]
9
- resolve(ret)
10
- })
11
- }
12
- } catch (error) {
13
- reject(error)
14
- }
15
- })
16
- }
17
-
18
3
  function knxUltimateHueButton(config) {
19
4
  RED.nodes.createNode(this, config)
20
5
  const node = this
@@ -45,15 +30,7 @@ module.exports = function (RED) {
45
30
  node.toggle4 = false
46
31
  node.toggle5 = false
47
32
  node.toggle5 = false
48
- node.rbeOutputPayload = undefined
49
-
50
- // Read the state of the light and store it in the holding object
51
- try {
52
- if (config.hueLight !== undefined && config.hueLight !== '') getLightState(node, config.hueLight)
53
- } catch (error) {
54
- }
55
-
56
-
33
+ node.toggle6 = false
57
34
 
58
35
  // Used to call the status update from the config node.
59
36
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
@@ -114,22 +91,18 @@ module.exports = function (RED) {
114
91
  knxMsgPayload.payload = node.toggle6
115
92
  }
116
93
  // Send to KNX bus
117
- if (knxMsgPayload.ga !== undefined) {
118
- node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + _event.button.last_event + ' ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
119
- if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
94
+ if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) {
95
+ node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
120
96
  }
97
+ node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + _event.button.last_event + ' ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
98
+
121
99
  // Setup the output msg
122
100
  knxMsgPayload.topic = knxMsgPayload.ga
123
101
  delete knxMsgPayload.ga
124
102
  knxMsgPayload.name = node.name
125
103
  knxMsgPayload.event = _event.button.last_event
126
-
127
- // Applying rbe filter
128
- if (config.outputSimpleMode && knxMsgPayload.payload !== node.rbeOutputPayload) {
129
- node.rbeOutputPayload = knxMsgPayload.payload
130
- } else {
131
- node.send(knxMsgPayload)
132
- }
104
+ knxMsgPayload.rawEvent = _event
105
+ node.send(knxMsgPayload)
133
106
  }
134
107
  } catch (error) {
135
108
  node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
@@ -156,12 +129,6 @@ module.exports = function (RED) {
156
129
  }
157
130
  done()
158
131
  })
159
-
160
- // On each deploy, unsubscribe+resubscribe
161
- if (node.server) {
162
- node.server.removeClient(node)
163
- node.server.addClient(node)
164
- }
165
132
  }
166
133
  RED.nodes.registerType('knxUltimateHueButton', knxUltimateHueButton)
167
134
  }
@@ -116,10 +116,9 @@ module.exports = function (RED) {
116
116
  knxMsgPayload.payload = _event.dimming.brightness
117
117
  }
118
118
  // Send to KNX bus
119
- if (knxMsgPayload.ga !== undefined) {
120
- node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX State ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
121
- if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
122
- }
119
+ if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
120
+ node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX State ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
121
+
123
122
  }
124
123
  } catch (error) {
125
124
  node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
@@ -146,12 +145,6 @@ module.exports = function (RED) {
146
145
  }
147
146
  done()
148
147
  })
149
-
150
- // On each deploy, unsubscribe+resubscribe
151
- if (node.server) {
152
- node.server.removeClient(node)
153
- node.server.addClient(node)
154
- }
155
148
  }
156
149
  RED.nodes.registerType('knxUltimateHueLight', knxUltimateHueLight)
157
150
  }
@@ -44,18 +44,21 @@ module.exports = function (RED) {
44
44
  knxMsgPayload.dpt = config.dptmotion
45
45
  knxMsgPayload.payload = _event.motion.motion
46
46
 
47
- // Send to KNX bus
48
- node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
49
- if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
50
-
51
- // Setup the output msg
52
- knxMsgPayload.topic = knxMsgPayload.ga
53
- delete knxMsgPayload.ga
54
- knxMsgPayload.name = node.name
55
- knxMsgPayload.event = 'motion'
56
-
57
- // Applying rbe filter
58
- node.send(knxMsgPayload)
47
+ if (_event.hasOwnProperty('motion') && _event.motion.hasOwnProperty('motion')) {
48
+ // Send to KNX bus
49
+ if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
50
+ node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
51
+
52
+ // Setup the output msg
53
+ knxMsgPayload.topic = knxMsgPayload.ga
54
+ delete knxMsgPayload.ga
55
+ knxMsgPayload.name = node.name
56
+ knxMsgPayload.event = 'motion'
57
+
58
+ // Send payload
59
+ knxMsgPayload.rawEvent = _event
60
+ node.send(knxMsgPayload)
61
+ }
59
62
  }
60
63
  } catch (error) {
61
64
  node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
@@ -82,12 +85,6 @@ module.exports = function (RED) {
82
85
  }
83
86
  done()
84
87
  })
85
-
86
- // On each deploy, unsubscribe+resubscribe
87
- if (node.server) {
88
- node.server.removeClient(node)
89
- node.server.addClient(node)
90
- }
91
88
  }
92
89
  RED.nodes.registerType('knxUltimateHueMotion', knxUltimateHueMotion)
93
90
  }
@@ -0,0 +1,269 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('knxUltimateHueTapDial', {
3
+ category: "KNX Ultimate",
4
+ color: '#C7E9C0',
5
+ defaults: {
6
+ //buttonState: {value: true},
7
+ server: { type: "knxUltimate-config", required: false },
8
+ serverHue: { type: "hue-config", required: true },
9
+ name: { value: "" },
10
+
11
+ namerepeat: { value: "" },
12
+ GArepeat: { value: "" },
13
+ dptrepeat: { value: "" },
14
+
15
+ hueDevice: { value: "" }
16
+ },
17
+ inputs: 0,
18
+ outputs: 1,
19
+ icon: "node-hue-icon.svg",
20
+ label: function () {
21
+ return (this.name);
22
+ },
23
+ paletteLabel: "Hue Tap Dial (beta)",
24
+ // button: {
25
+ // enabled: function() {
26
+ // // return whether or not the button is enabled, based on the current
27
+ // // configuration of the node
28
+ // return !this.changed
29
+ // },
30
+ // visible: function() {
31
+ // // return whether or not the button is visible, based on the current
32
+ // // configuration of the node
33
+ // return this.hasButton
34
+ // },
35
+ // //toggle: "buttonState",
36
+ // onclick: function() {}
37
+ // },
38
+ oneditprepare: function () {
39
+ var node = this;
40
+ var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
41
+ var oNodeServerHue = RED.nodes.node($("#node-input-serverHue").val()); // Store the config-node
42
+
43
+ // 19/02/2020 Used to get the server sooner als deploy.
44
+ $("#node-input-server").change(function () {
45
+ try {
46
+ oNodeServer = RED.nodes.node($(this).val());
47
+ } catch (error) { }
48
+ });
49
+ // 19/02/2020 Used to get the server sooner als deploy.
50
+ $("#node-input-serverHue").change(function () {
51
+ try {
52
+ oNodeServerHue = RED.nodes.node($(this).val());
53
+ } catch (error) { }
54
+ });
55
+
56
+ // 31/03/2020 Search Helper
57
+ function fullSearch(sourceText, searchString) {
58
+ // This searches for all words in a string
59
+ var aSearchWords = searchString.toLowerCase().split(" ");
60
+ var i = 0;
61
+ for (let index = 0; index < aSearchWords.length; index++) {
62
+ if (sourceText.toLowerCase().indexOf(aSearchWords[index]) > -1) i += 1;
63
+ }
64
+ return i == aSearchWords.length;
65
+ }
66
+
67
+
68
+ // DPT repeat
69
+ // ########################
70
+ $.getJSON('knxUltimateDpts', (data) => {
71
+ data.forEach(dpt => {
72
+ if (dpt.value.startsWith("3.007") || dpt.value.startsWith("5.00")) {
73
+ $("#node-input-dptrepeat").append($("<option></option>")
74
+ .attr("value", dpt.value)
75
+ .text(dpt.text))
76
+ }
77
+ });
78
+
79
+ $("#node-input-dptrepeat").val(this.dptrepeat)
80
+ })
81
+
82
+ // Autocomplete suggestion with ETS csv File
83
+ $("#node-input-GArepeat").autocomplete({
84
+ minLength: 1,
85
+ source: function (request, response) {
86
+ $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
87
+ response($.map(data, function (value, key) {
88
+ var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
89
+ if (fullSearch(sSearch, request.term)) {
90
+ if (value.dpt.startsWith('5.001') || value.dpt.startsWith('3.007')) {
91
+ return {
92
+ label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
93
+ value: value.ga // Value
94
+ }
95
+ } else { return null; }
96
+ } else {
97
+ return null;
98
+ }
99
+ }));
100
+ });
101
+ }, select: function (event, ui) {
102
+ // Sets Datapoint and device name automatically
103
+ var sDevName = ui.item.label.split("#")[1].trim();
104
+ try {
105
+ sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
106
+ } catch (error) {
107
+ }
108
+ $('#node-input-namerepeat').val(sDevName);
109
+ var optVal = $("#node-input-dptrepeat option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
110
+ // Select the option value
111
+ $("#node-input-dptrepeat").val(optVal);
112
+ }
113
+ });
114
+
115
+
116
+
117
+
118
+ // Autocomplete suggestion with HUE
119
+ $("#node-input-name").autocomplete({
120
+ minLength: 1,
121
+ source: function (request, response) {
122
+ $.getJSON("KNXUltimateGetDevicesHUE?rtype=relative_rotary&nodeID=" + oNodeServerHue.id, (data) => {
123
+ response($.map(data.devices, function (value, key) {
124
+ //alert(JSON.stringify(value) + " "+ key)
125
+ var sSearch = (value.name);
126
+ if (fullSearch(sSearch, request.term)) {
127
+ return {
128
+ hueDevice: value.id, // Label for Display
129
+ value: value.name // Value
130
+ }
131
+ } else {
132
+ return null;
133
+ }
134
+ }));
135
+ });
136
+ }, select: function (event, ui) {
137
+ // Sets the fields
138
+ $('#node-input-hueDevice').val(ui.item.hueDevice);
139
+ }
140
+ });
141
+
142
+
143
+ // ########################
144
+
145
+
146
+ },
147
+ oneditsave: function () {
148
+
149
+
150
+ },
151
+ oneditcancel: function () {
152
+
153
+ }
154
+ })
155
+
156
+ </script>
157
+
158
+ <script type="text/x-red" data-template-name="knxUltimateHueTapDial">
159
+
160
+
161
+ <div class="form-row">
162
+ <b>HUE Light</b>&nbsp&nbsp<span style="color:red"
163
+ &nbsp<i class="fa fa-question-circle"></i></span>
164
+ &nbsp<a target="_blank" href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/en-hue-configuration"><u>Configuration</u></a>
165
+ <br />
166
+ <p align="center"><img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/hueTapDial.png'></p>
167
+ <br />
168
+ <label for="node-input-server" >
169
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img>
170
+ KNX GW
171
+ </label>
172
+ <input type="text" id="node-input-server" />
173
+ </div>
174
+
175
+ <div class="form-row">
176
+ <label for="node-input-serverHue">
177
+ <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABFUlEQVQ4EZWSsWoCQRCG1yiENEFEi6QSkjqWWoqFoBYJ+Br6JHkMn8Iibd4ihQpaJIhWNkry/ZtdGZY78Qa+m39nZ+dm9s4550awglNBluS/gVtAX6KgDclf68w2OThgfR9iT/jnoEv4TtByDThWTCDKW4SSZTf/zj9/eZbN+izTDuKGimu0vPF8B/YN8aC8LmcOj/AAn9CFTEs70Js/oGqy79C69bqJ5XbQI2kGO5N8QL9D08S8zBtBF5ZaVsznpCMoqJnVdjTpb1Db0fwIWmQV6BLXzFOYgA6/gDVfQN9bBWp2J2hdWDPoBV5FrKnAJutHikk/CHHR8i7x4iG7qQ720IYvu3GFbpHjx3pFrOFYkA354z/5bkK826phyAAAAABJRU5ErkJggg=="/>
178
+ HUE Bridge
179
+ </label>
180
+ <input type="text" id="node-input-serverHue" />
181
+ </div>
182
+
183
+ <br/>
184
+ <p>
185
+ <b>Philips HUE</b>
186
+ </p>
187
+
188
+ <div class="form-row">
189
+ <label for="node-input-hueDevice" >
190
+ <i class="fa fa-play-circle"></i>&nbspHue Device</label>
191
+ <input type="text" id="node-input-name" placeholder="Enter your hue device name" />
192
+ <input type="hidden" id="node-input-hueDevice" />
193
+ </div>
194
+
195
+ <br/>
196
+
197
+ <p>
198
+ <b>PHILIPS HUE BUTTON EVENTS -> TO KNX</b>
199
+ </p>
200
+
201
+ <div class="form-row">
202
+ <label for="node-input-namerepeat" style="width:100px;"><i class="fa fa-play-circle-o"></i> Rotate</span></label>
203
+
204
+ <label for="node-input-GArepeat" style="width:20px;">GA</label>
205
+ <input type="text" id="node-input-GArepeat" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
206
+
207
+ <label for="node-input-dptrepeat" style="width:40px; margin-left: 0px; text-align: right;">dpt</label>
208
+ <select id="node-input-dptrepeat" style="width:140px;"></select>
209
+
210
+ <label for="node-input-namerepeat" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
211
+ <input type="text" id="node-input-namerepeat" style="width:200px;margin-left: 5px; text-align: left;">
212
+ </div>
213
+
214
+
215
+ </div>
216
+
217
+
218
+ <br/>
219
+ <br/>
220
+ <br/>
221
+ <br/>
222
+ <br/>
223
+ <br/>
224
+ <br/>
225
+ <br/>
226
+
227
+
228
+ </script>
229
+
230
+ <script type="text/markdown" data-help-name="knxUltimateHueTapDial">
231
+ <p> This node lets you get the events from your HUE rotary, for example Tap Dial.<br/>
232
+
233
+ The Tap Dial button has 4 buttons and 1 rotary knob.
234
+ You can find the 4 buttons in the **Button node**, not here!
235
+ The rotary service of the Tap Dial button, can send a DIM (Datapoint 3.007) or absolute brightness (Datapoint 5.001) to the KNX bus,
236
+ to the selected group address.
237
+
238
+ **General**
239
+ |Property|Description|
240
+ |--|--|
241
+ | KNX GW | Select the KNX gateway to be used |
242
+ | HUE Bridge | Select the HUE Bridge to be used |
243
+ | Hue Button | HUE button to be used. The avaiable buttons start showing up while you're typing.|
244
+
245
+ <br/>
246
+
247
+ **PHILIPS HUE BUTTON EVENTS -> TO KNX**
248
+ Here you can get the HUE rotary service events.<br/>
249
+ 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.
250
+
251
+ |Property|Description|
252
+ |--|--|
253
+ | Rotate | This command is used either to send DIM (increase/decrease) or true/false commands to the KNX group address. Only as info, in the HUE event chain, this is called *repeat* |
254
+
255
+ ### Outputs
256
+
257
+ 1. Standard output
258
+ : payload (string|object) : the standard output of the command.
259
+
260
+ ### Details
261
+
262
+ `msg.payload` is used as the payload of the published message.
263
+ If it contains an Object it will be converted to a JSON string before being sent.
264
+
265
+ [Find it useful?](https://www.paypal.me/techtoday)
266
+
267
+ <br/>
268
+
269
+ </script>
@@ -0,0 +1,122 @@
1
+ module.exports = function (RED) {
2
+
3
+ async function getLightState(node, _lightID) {
4
+ return new Promise((resolve, reject) => {
5
+ try {
6
+ if (node !== null && node.serverHue !== null && node.serverHue.hueManager !== null) {
7
+ node.serverHue.hueManager.getLight(_lightID).then(ret => {
8
+ node.currentHUEDevice = ret[0]
9
+ resolve(ret)
10
+ })
11
+ }
12
+ } catch (error) {
13
+ reject(error)
14
+ }
15
+ })
16
+ }
17
+
18
+ function knxUltimateHueTapDial(config) {
19
+ RED.nodes.createNode(this, config)
20
+ const node = this
21
+ node.server = RED.nodes.getNode(config.server)
22
+ node.serverHue = RED.nodes.getNode(config.serverHue)
23
+ node.topic = node.name
24
+ node.name = config.name === undefined ? 'Hue' : config.name
25
+ node.dpt = ''
26
+ node.notifyreadrequest = false
27
+ node.notifyreadrequestalsorespondtobus = 'false'
28
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
29
+ node.notifyresponse = false
30
+ node.notifywrite = true
31
+ node.initialread = true
32
+ node.listenallga = true // Don't remove
33
+ node.outputtype = 'write'
34
+ node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
35
+ node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
36
+ node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
37
+ node.passthrough = 'no'
38
+ node.formatmultiplyvalue = 1
39
+ node.formatnegativevalue = 'leave'
40
+ node.formatdecimalsvalue = 2
41
+ node.brightnessState = 0
42
+
43
+ // Read the state of the light and store it in the holding object
44
+ try {
45
+ if (config.hueLight !== undefined && config.hueLight !== '') getLightState(node, config.hueLight)
46
+ } catch (error) {
47
+ }
48
+
49
+
50
+
51
+ // Used to call the status update from the config node.
52
+ node.setNodeStatus = ({ fill, shape, text, payload }) => {
53
+
54
+ }
55
+
56
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
57
+ node.handleSend = msg => {
58
+ }
59
+
60
+ node.handleSendHUE = _event => {
61
+ try {
62
+ if (_event.id === config.hueDevice) {
63
+ const knxMsgPayload = {}
64
+ knxMsgPayload.ga = config.GArepeat
65
+ knxMsgPayload.dpt = config.dptrepeat
66
+ if (_event.relative_rotary.last_event.rotation.direction === 'clock_wise') {
67
+ if (knxMsgPayload.dpt.startsWith('3.007')) {
68
+ knxMsgPayload.payload = { decr_incr: 1, data: 5 }
69
+ } else if (knxMsgPayload.dpt.startsWith('5.001')) {
70
+ //0 – maximum: 32767
71
+ node.brightnessState < 100 ? node.brightnessState += 20 : node.brightnessState = 100
72
+ knxMsgPayload.payload = node.brightnessState
73
+ }
74
+ } else if (_event.relative_rotary.last_event.rotation.direction === 'counter_clock_wise') {
75
+ if (knxMsgPayload.dpt.startsWith('3.007')) {
76
+ knxMsgPayload.payload = { decr_incr: 0, data: 5 }
77
+ } else if (knxMsgPayload.dpt.startsWith('5.001')) {
78
+ node.brightnessState > 0 ? node.brightnessState -= 20 : node.brightnessState = 0
79
+ knxMsgPayload.payload = node.brightnessState
80
+ }
81
+ }
82
+
83
+ // Send to KNX bus
84
+ if (knxMsgPayload.ga !== '' && knxMsgPayload.ga !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.ga, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
85
+ node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
86
+
87
+ // Setup the output msg
88
+ knxMsgPayload.topic = knxMsgPayload.ga
89
+ delete knxMsgPayload.ga
90
+ knxMsgPayload.name = node.name
91
+ knxMsgPayload.event = 'rotation ' + _event.relative_rotary.last_event.rotation.direction
92
+ knxMsgPayload.payload = _event
93
+ node.send(knxMsgPayload)
94
+ }
95
+ } catch (error) {
96
+ node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
97
+ }
98
+ }
99
+
100
+ // On each deploy, unsubscribe+resubscribe
101
+ if (node.server) {
102
+ node.server.removeClient(node)
103
+ node.server.addClient(node)
104
+ }
105
+ if (node.serverHue) {
106
+ node.serverHue.removeClient(node)
107
+ node.serverHue.addClient(node)
108
+ }
109
+
110
+ node.on('input', function (msg) {
111
+
112
+ })
113
+
114
+ node.on('close', function (done) {
115
+ if (node.server) {
116
+ node.server.removeClient(node)
117
+ }
118
+ done()
119
+ })
120
+ }
121
+ RED.nodes.registerType('knxUltimateHueTapDial', knxUltimateHueTapDial)
122
+ }
@@ -42,6 +42,9 @@ class classHUE extends EventEmitter {
42
42
  if (_rtype === 'motion') {
43
43
  retArray.push({ name: linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: device.id })
44
44
  }
45
+ if (_rtype === 'relative_rotary') {
46
+ retArray.push({ name: linkedDevName + (Room !== undefined ? ', room ' + Room.metadata.name : ''), id: device.id })
47
+ }
45
48
  })
46
49
  return { devices: retArray }
47
50
  } catch (error) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=14.0.0"
5
5
  },
6
- "version": "2.0.8",
6
+ "version": "2.0.9",
7
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 handling.",
8
8
  "dependencies": {
9
9
  "mkdirp": "1.0.4",
@@ -35,7 +35,8 @@
35
35
  "hueConfig": "/nodes/hue-config.js",
36
36
  "knxUltimateHueLight": "/nodes/knxUltimateHueLight.js",
37
37
  "knxUltimateHueButton": "/nodes/knxUltimateHueButton.js",
38
- "knxUltimateHueMotion": "/nodes/knxUltimateHueMotion.js"
38
+ "knxUltimateHueMotion": "/nodes/knxUltimateHueMotion.js",
39
+ "knxUltimateHueTapDial": "/nodes/knxUltimateHueTapDial.js"
39
40
  }
40
41
  },
41
42
  "repository": {