node-red-contrib-knx-ultimate 2.4.10 → 2.4.12

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,9 +6,10 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
- **Version 2.4.10** - March 2024<br/>
9
+ **Version 2.4.12** - April 2024<br/>
10
10
  - Warning: this version uses the Node-Red plugin system; the Node-Red version must be **equals or major than 3.1.1**<br/>
11
- - NEW: Home Assistant translator node: translates the HA input msg, to a KNX value. Comes with a built.in translation table, that's user editable.<br/>
11
+ - NEW: Home Assistant translator node: translates the HA input msg, to a KNX value. Comes with a built-in translation table, that's user editable.<br/>
12
+ - NEW: HUE Contact Sensor node.<br/>
12
13
  - Updated KNX-Ultimate device node help.<br/>
13
14
  - Minor KNX-Ultimate device node UI changes.<br/>
14
15
 
package/README.md CHANGED
@@ -38,7 +38,7 @@ msg.payload = {red:255, green:200, blue:30} // Put some colors in our life
38
38
  * **LOAD CONTROL node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/LoadControl-Configuration). Control your loads (Oven, Washing machine, etc..) and avoit shutting down the main voltage due to too high power consumption.
39
39
  * **VIEWER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer). View all Group Addresses and values of your KNX BUS, in the Node-Red Dashboard.
40
40
  * **PHILIPS HUE nodeset** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/en-hue-configuration). Link HUE devices to KNX in a simple way.
41
- * **Homeassistant translator node** translates the HA input msg, to a KNX value. Comes with a built.in translation table, that's user editable.
41
+ * **HOME ASSISTANT translator node** translates the HA input msg, to a KNX value. Comes with a built-in translation table, that's user editable.
42
42
 
43
43
 
44
44
  <br>
@@ -317,6 +317,14 @@ module.exports = (RED) => {
317
317
  //const zigbee = node.hueAllResources.filter((a) => a.services !== undefined).find((a) => a.services.rtype === "zigbee_connectivity");
318
318
  //const devs = zigbee.filter((a) => a.rtype === "zigbee_connectivity");
319
319
  }
320
+ if (_rtype === 'contact') {
321
+ const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid))
322
+ const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || ''
323
+ retArray.push({
324
+ name: `Contact: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`,
325
+ id: resource.id,
326
+ });
327
+ }
320
328
  } catch (error) {
321
329
  retArray.push({
322
330
  name: `${_rtype}: ERROR ${error.message}`,
@@ -0,0 +1,257 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
2
+
3
+ <script type="text/javascript">
4
+ RED.nodes.registerType('knxUltimateHueContactSensor', {
5
+ category: 'KNX Ultimate',
6
+ color: '#C0C7E9',
7
+ defaults: {
8
+ //buttonState: {value: true},
9
+ server: { type: 'knxUltimate-config', required: false },
10
+ serverHue: { type: 'hue-config', required: true },
11
+ name: { value: '' },
12
+
13
+ namecontact: { value: '' },
14
+ GAcontact: { value: '' },
15
+ dptcontact: { value: '' },
16
+
17
+ hueDevice: { value: '' },
18
+ },
19
+ inputs: 0,
20
+ outputs: 1,
21
+ icon: 'node-hue-icon.svg',
22
+ label: function () {
23
+ return (this.name || 'Hue Contact Sensor')
24
+ },
25
+ paletteLabel: 'Hue Contact Sensor',
26
+ // button: {
27
+ // enabled: function() {
28
+ // // return whether or not the button is enabled, based on the current
29
+ // // configuration of the node
30
+ // return !this.changed
31
+ // },
32
+ // visible: function() {
33
+ // // return whether or not the button is visible, based on the current
34
+ // // configuration of the node
35
+ // return this.hasButton
36
+ // },
37
+ // //toggle: "buttonState",
38
+ // onclick: function() {}
39
+ // },
40
+ oneditprepare: function () {
41
+ var node = this
42
+ var oNodeServer = RED.nodes.node($('#node-input-server').val()) // Store the config-node
43
+ var oNodeServerHue = RED.nodes.node($('#node-input-serverHue').val()) // Store the config-node
44
+
45
+ // 19/02/2020 Used to get the server sooner als deploy.
46
+ $('#node-input-server').change(function () {
47
+ try {
48
+ oNodeServer = RED.nodes.node($(this).val())
49
+ } catch (error) {
50
+ }
51
+ })
52
+ // 19/02/2020 Used to get the server sooner als deploy.
53
+ $('#node-input-serverHue').change(function () {
54
+ try {
55
+ oNodeServerHue = RED.nodes.node($(this).val())
56
+ } catch (error) {
57
+ }
58
+ })
59
+
60
+ // 31/03/2020 Search Helper
61
+ function fullSearch(sourceText, searchString) {
62
+ // This searches for all words in a string
63
+ var aSearchWords = searchString.toLowerCase().split(' ')
64
+ var i = 0
65
+ for (let index = 0; index < aSearchWords.length; index++) {
66
+ if (sourceText.toLowerCase().indexOf(aSearchWords[index]) > -1) i += 1
67
+ }
68
+ return i == aSearchWords.length
69
+ }
70
+
71
+ // DPT
72
+ // ########################
73
+ $.getJSON('knxUltimateDpts?serverId=' + $('#node-input-server').val(), (data) => {
74
+ data.forEach(dpt => {
75
+ if (dpt.value.startsWith('1.')) {
76
+ $('#node-input-dptcontact').append($('<option></option>')
77
+ .attr('value', dpt.value)
78
+ .text(dpt.text))
79
+ }
80
+ })
81
+ $('#node-input-dptcontact').val(this.dptcontact)
82
+ })
83
+
84
+ // Autocomplete suggestion with ETS csv File
85
+ $('#node-input-GAcontact').autocomplete({
86
+ minLength: 1,
87
+ source: function (request, response) {
88
+ //$.getJSON("csv", request, function( data, status, xhr ) {
89
+ $.getJSON('knxUltimatecsv?nodeID=' + oNodeServer.id, (data) => {
90
+ response($.map(data, function (value, key) {
91
+ var sSearch = (value.ga + ' (' + value.devicename + ') DPT' + value.dpt)
92
+ if (fullSearch(sSearch, request.term + ' 1.')) {
93
+ return {
94
+ label: value.ga + ' # ' + value.devicename + ' # ' + value.dpt, // Label for Display
95
+ value: value.ga, // Value
96
+ }
97
+ } else {
98
+ return null
99
+ }
100
+ }))
101
+ })
102
+ }, select: function (event, ui) {
103
+ // Sets Datapoint and device name automatically
104
+ var sDevName = ui.item.label.split('#')[1].trim()
105
+ try {
106
+ sDevName = sDevName.substr(sDevName.indexOf(')') + 1).trim()
107
+ } catch (error) {
108
+ }
109
+ $('#node-input-namecontact').val(sDevName)
110
+ var optVal = $('#node-input-dptcontact option:contains(\'' + ui.item.label.split('#')[2].trim() + '\')').attr('value')
111
+ // Select the option value
112
+ $('#node-input-dptcontact').val(optVal)
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('KNXUltimateGetResourcesHUE?rtype=contact&serverId=' + 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/html" data-template-name="knxUltimateHueContactSensor">
159
+ <div class="form-row">
160
+ <b>HUE Motion node&nbsp&nbsp</b>
161
+ <span style="color:red">&nbsp<i class="fa fa-youtube"></i>&nbsp</span>
162
+ <a target="_blank" href="https://youtu.be/jjEUI1J8bkA">
163
+ <u>Youtube sample</u>
164
+ </a>
165
+ <br />
166
+ <br />
167
+ <p align="center">
168
+ <i class="fa-regular fa-door-open fa-shake fa-4x"></i>
169
+ </p>
170
+ <br />
171
+ <label for="node-input-server">
172
+ <img
173
+ 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>
174
+ KNX GW
175
+ </label>
176
+ <input type="text" id="node-input-server" />
177
+ </div>
178
+
179
+ <div class="form-row">
180
+ <label for="node-input-serverHue">
181
+ <img
182
+ src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABFUlEQVQ4EZWSsWoCQRCG1yiENEFEi6QSkjqWWoqFoBYJ+Br6JHkMn8Iibd4ihQpaJIhWNkry/ZtdGZY78Qa+m39nZ+dm9s4550awglNBluS/gVtAX6KgDclf68w2OThgfR9iT/jnoEv4TtByDThWTCDKW4SSZTf/zj9/eZbN+izTDuKGimu0vPF8B/YN8aC8LmcOj/AAn9CFTEs70Js/oGqy79C69bqJ5XbQI2kGO5N8QL9D08S8zBtBF5ZaVsznpCMoqJnVdjTpb1Db0fwIWmQV6BLXzFOYgA6/gDVfQN9bBWp2J2hdWDPoBV5FrKnAJutHikk/CHHR8i7x4iG7qQ720IYvu3GFbpHjx3pFrOFYkA354z/5bkK826phyAAAAABJRU5ErkJggg==" />
183
+ HUE Bridge
184
+ </label>
185
+ <input type="text" id="node-input-serverHue" />
186
+ </div>
187
+
188
+ <br />
189
+ <p>
190
+ <b>Philips HUE</b>
191
+ </p>
192
+
193
+ <div class="form-row">
194
+ <label for="node-input-hueDevice">
195
+ <i class="fa fa-play-circle"></i>&nbspHue Sensor</label>
196
+ <input type="text" id="node-input-name" placeholder="Enter your hue device name" />
197
+ <input type="hidden" id="node-input-hueDevice" />
198
+ </div>
199
+
200
+ <br />
201
+
202
+ <p>
203
+ <b>KNX</b>
204
+ </p>
205
+
206
+ <div class="form-row">
207
+ <label for="node-input-namecontact" style="width:100px;"><i class="fa fa-play-circle-o"></i> Contact</span></label>
208
+
209
+ <label for="node-input-GAcontact" style="width:20px;">GA</label>
210
+ <input type="text" id="node-input-GAcontact" placeholder="Ex: 1/1/1"
211
+ style="width:70px;margin-left: 5px; text-align: left;">
212
+
213
+ <label for="node-input-dptcontact" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
214
+ <select id="node-input-dptcontact" style="width:140px;"></select>
215
+
216
+ <label for="node-input-namecontact" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
217
+ <input type="text" id="node-input-namecontact" style="width:200px;margin-left: 5px; text-align: left;">
218
+ </div>
219
+
220
+ <br />
221
+ <br />
222
+ <br />
223
+ </script>
224
+
225
+ <script type="text/markdown" data-help-name="knxUltimateHueContactSensor">
226
+ This node lets you get the events from your HUE contact sensor.
227
+
228
+ 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.
229
+
230
+ **General**
231
+ |Property|Description|
232
+ |--|--|
233
+ | KNX GW | Select the KNX gateway to be used |
234
+ | HUE Bridge | Select the HUE Bridge to be used |
235
+ | Hue Sensor | HUE contact sensor to be used. The avaiable contact sensors start showing up while you're typing.|
236
+
237
+ |Property|Description|
238
+ |--|--|
239
+ | Contact | As soon as a contact sensor is being opene/closed, a *true* KNX value is sent to this group address, otherwise *false* is sent. |
240
+
241
+ ### Outputs
242
+
243
+ 1. Standard output
244
+ : payload (boolean) : the standard output of the command.
245
+
246
+ ### Details
247
+
248
+ `msg.payload` is used as the payload of the published message.
249
+ It contains the detailed event sent by your Hue device so you can use it for whatever you want.
250
+
251
+ <br/>
252
+
253
+ [Find it useful?](https://www.paypal.me/techtoday)
254
+
255
+ <br/>
256
+
257
+ </script>
@@ -0,0 +1,129 @@
1
+ module.exports = function (RED) {
2
+ function knxUltimateHueContactSensor(config) {
3
+ RED.nodes.createNode(this, config)
4
+ const node = this
5
+ node.server = RED.nodes.getNode(config.server)
6
+ node.serverHue = RED.nodes.getNode(config.serverHue)
7
+ node.topic = node.name
8
+ node.name = config.name === undefined ? 'Hue' : config.name
9
+ node.dpt = ''
10
+ node.notifyreadrequest = false
11
+ node.notifyreadrequestalsorespondtobus = 'false'
12
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
13
+ node.notifyresponse = false
14
+ node.notifywrite = true
15
+ node.initialread = true
16
+ node.listenallga = true // Don't remove
17
+ node.outputtype = 'write'
18
+ node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
19
+ node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
20
+ node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
21
+ node.passthrough = 'no'
22
+ node.formatmultiplyvalue = 1
23
+ node.formatnegativevalue = 'leave'
24
+ node.formatdecimalsvalue = 2
25
+ node.hueDevice = config.hueDevice
26
+ node.initializingAtStart = false
27
+
28
+ // Used to call the status update from the config node.
29
+ node.setNodeStatus = ({ fill, shape, text, payload }) => {
30
+ }
31
+ // Used to call the status update from the HUE config node.
32
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
33
+ if (payload === undefined) payload = ''
34
+ const dDate = new Date()
35
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
36
+ node.status({
37
+ fill,
38
+ shape,
39
+ text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`,
40
+ })
41
+ }
42
+
43
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
44
+ node.handleSend = (msg) => {
45
+ }
46
+
47
+ node.handleSendHUE = (_event) => {
48
+ try {
49
+ if (_event.id === config.hueDevice) {
50
+
51
+ if (!_event.hasOwnProperty('contact_report')) {
52
+ return
53
+ }
54
+
55
+ const knxMsgPayload = {}
56
+ knxMsgPayload.topic = config.GAcontact
57
+ knxMsgPayload.dpt = config.dptcontact
58
+
59
+ if (_event.hasOwnProperty('contact_report')) {
60
+ knxMsgPayload.payload = _event.contact_report.state === 'contact'
61
+
62
+ // Send to KNX bus
63
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
64
+ node.server.writeQueueAdd({
65
+ grpaddr: knxMsgPayload.topic,
66
+ payload: knxMsgPayload.payload,
67
+ dpt: knxMsgPayload.dpt,
68
+ outputtype: 'write',
69
+ nodecallerid: node.id,
70
+ })
71
+ }
72
+
73
+ node.status({
74
+ fill: 'green',
75
+ shape: 'dot',
76
+ text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
77
+ })
78
+
79
+ // Set up the output msg
80
+ knxMsgPayload.name = node.name
81
+ knxMsgPayload.event = 'contact'
82
+
83
+ // Send payload
84
+ knxMsgPayload.rawEvent = _event
85
+ node.send(knxMsgPayload)
86
+ node.setNodeStatusHue({
87
+ fill: 'blue',
88
+ shape: 'ring',
89
+ text: 'HUE->KNX',
90
+ payload: knxMsgPayload.payload,
91
+ })
92
+ }
93
+ }
94
+ } catch (error) {
95
+ node.status({
96
+ fill: 'red',
97
+ shape: 'dot',
98
+ text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`,
99
+ })
100
+ }
101
+ }
102
+
103
+ // On each deploy, unsubscribe+resubscribe
104
+ if (node.server) {
105
+ node.server.removeClient(node)
106
+ node.server.addClient(node)
107
+ }
108
+
109
+ if (node.serverHue) {
110
+ node.serverHue.removeClient(node)
111
+ node.serverHue.addClient(node)
112
+ }
113
+
114
+ node.on('input', (msg) => {
115
+ })
116
+
117
+ node.on('close', (done) => {
118
+ if (node.server) {
119
+ node.server.removeClient(node)
120
+ }
121
+ if (node.serverHue) {
122
+ node.serverHue.removeClient(node)
123
+ }
124
+ done()
125
+ })
126
+ }
127
+
128
+ RED.nodes.registerType('knxUltimateHueContactSensor', knxUltimateHueContactSensor)
129
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.4.10",
6
+ "version": "2.4.12",
7
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",
@@ -45,6 +45,7 @@
45
45
  "knxUltimateHueScene": "/nodes/knxUltimateHueScene.js",
46
46
  "knxUltimateHueBattery": "/nodes/knxUltimateHueBattery.js",
47
47
  "knxUltimateHueZigbeeConnectivity": "/nodes/knxUltimateHueZigbeeConnectivity.js",
48
+ "knxUltimateContactSensor": "/nodes/knxUltimateHueContactSensor.js",
48
49
  "knxUltimateHATranslator": "/nodes/knxUltimateHATranslator.js"
49
50
  }
50
51
  },