node-red-contrib-knx-ultimate 2.0.15 → 2.0.16

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,11 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ <p>
10
+ <b>Version 2.0.16</b> - June 2023<br/>
11
+ - HUE: CAUTION POSSIBLE BREAKING CHANGES TO THE HUE NODES. PLEASE BE AWARE THAT HUE NODES ARE STILL IN BETA<br/>
12
+ - Fixed issues with dimming in the hue button and hue tap dial nodes.<br/>
13
+ </p>
9
14
  <p>
10
15
  <b>Version 2.0.15</b> - June 2023<br/>
11
16
  - HUE: CAUTION POSSIBLE BREAKING CHANGES TO THE HUE NODES. PLEASE BE AWARE THAT HUE NODES ARE STILL IN BETA<br/>
@@ -8,7 +8,7 @@
8
8
  serverHue: { type: "hue-config", required: true },
9
9
  name: { value: "" },
10
10
 
11
- namerepeat: { value: "" },
11
+ nameDim: { value: "" },
12
12
  GArepeat: { value: "" },
13
13
  dptrepeat: { value: "3.007" },
14
14
 
@@ -73,7 +73,7 @@
73
73
  return i == aSearchWords.length;
74
74
  }
75
75
 
76
- // DPT repeat
76
+ // DPT Dim
77
77
  // ########################
78
78
  $.getJSON('knxUltimateDpts', (data) => {
79
79
  data.forEach(dpt => {
@@ -112,7 +112,7 @@
112
112
  sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
113
113
  } catch (error) {
114
114
  }
115
- $('#node-input-namerepeat').val(sDevName);
115
+ $('#node-input-nameDim').val(sDevName);
116
116
  var optVal = $("#node-input-dptrepeat option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
117
117
  // Select the option value
118
118
  $("#node-input-dptrepeat").val(optVal);
@@ -382,8 +382,8 @@
382
382
  <label for="node-input-dptrepeat" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
383
383
  <select id="node-input-dptrepeat" style="width:140px;"></select>
384
384
 
385
- <label for="node-input-namerepeat" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
386
- <input type="text" id="node-input-namerepeat" style="width:200px;margin-left: 5px; text-align: left;">
385
+ <label for="node-input-nameDim" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
386
+ <input type="text" id="node-input-nameDim" style="width:200px;margin-left: 5px; text-align: left;">
387
387
  </div>
388
388
  <br/>
389
389
  <br/>
@@ -433,7 +433,7 @@ This allows the internal logic to be aware of external devices, like wall switch
433
433
  |--|--|
434
434
  | Switch | As soon as you quickly press and release your HUE button, this event fires |
435
435
  | Switch Status | To allow internal logic to take care of the external KNX devices, for example an external wall switch, you should set this group address |
436
- | Repeat | This event is used either to send DIM (increase/decrease) or true/false commands to the KNX group address |
436
+ | Dim | This event is used either to send DIM (increase/decrease) or true/false commands to the KNX group address |
437
437
  | 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 |
438
438
 
439
439
  ### Outputs
@@ -24,8 +24,8 @@ module.exports = function (RED) {
24
24
  node.formatmultiplyvalue = 1
25
25
  node.formatnegativevalue = 'leave'
26
26
  node.formatdecimalsvalue = 2
27
- node.toggleGArepeat = false // up or down if repeat field is set to DIM
28
- node.toggleGAshort_release = false
27
+ node.short_releaseValue = false
28
+ node.isTimerDimStopRunning = false
29
29
 
30
30
  // Used to call the status update from the config node.
31
31
  node.setNodeStatus = ({ fill, shape, text, payload }) => {
@@ -39,7 +39,7 @@ module.exports = function (RED) {
39
39
  switch (msg.knx.destination) {
40
40
  case config.GAshort_releaseStatus:
41
41
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptshort_release))
42
- node.toggleGAshort_release = msg.payload
42
+ node.short_releaseValue = msg.payload
43
43
  setTimeout(() => {
44
44
  node.status({ fill: 'blue', shape: 'dot', text: 'Updated Switch ' + msg.knx.destination + ' ' + JSON.stringify(msg.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
45
45
  }, 500)
@@ -63,61 +63,84 @@ module.exports = function (RED) {
63
63
  try {
64
64
  if (_event.id === config.hueDevice) {
65
65
  const knxMsgPayload = {}
66
+ let flowMsgPayload = true
66
67
  // Handling events with toggles
68
+ // KNX Dimming reminder tips
69
+ // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
70
+ // { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
67
71
  switch (_event.button.last_event) {
68
72
  case 'initial_press':
69
73
  if (node.initial_pressValue === undefined) node.initial_pressValue = false
70
- config.toggleValues ? node.initial_pressValue = !node.initial_pressValue : node.initial_pressValue = true
71
- knxMsgPayload.payload = node.initial_pressValue
74
+ node.initial_pressValue = config.toggleValues ? !node.initial_pressValue : true
75
+ flowMsgPayload = node.initial_pressValue
72
76
  break
73
77
  case 'long_release':
74
- if (node.long_releaseValue === undefined) node.long_releaseValue = false
75
- config.toggleValues ? node.long_releaseValue = !node.long_releaseValue : node.long_releaseValue = true
76
- knxMsgPayload.payload = node.long_releaseValue
78
+ flowMsgPayload = node.long_pressValue
77
79
  break
78
80
  case 'double_short_release':
79
81
  if (node.double_short_releaseValue === undefined) node.double_short_releaseValue = false
80
- config.toggleValues ? node.double_short_releaseValue = !node.double_short_releaseValue : node.double_short_releaseValue = true
81
- knxMsgPayload.payload = node.double_short_releaseValue
82
+ node.double_short_releaseValue = config.toggleValues ? !node.double_short_releaseValue : true
83
+ flowMsgPayload = node.double_short_releaseValue
82
84
  break
83
85
  case 'long_press':
84
86
  if (node.long_pressValue === undefined) node.long_pressValue = false
85
- config.toggleValues ? node.long_pressValue = !node.long_pressValue : node.long_pressValue = true
86
- knxMsgPayload.payload = node.long_pressValue
87
+ node.long_pressValue = config.toggleValues ? !node.long_pressValue : true
88
+ flowMsgPayload = node.long_pressValue
87
89
  break
88
90
  case 'short_release':
91
+ node.short_releaseValue = config.toggleValues ? !node.short_releaseValue : true
92
+ flowMsgPayload = node.short_releaseValue
89
93
  knxMsgPayload.topic = config.GAshort_release
90
94
  knxMsgPayload.dpt = config.dptshort_release
91
- config.toggleValues ? (node.toggleGAshort_release = !node.toggleGAshort_release) : node.toggleGAshort_release = true
92
- knxMsgPayload.payload = node.toggleGAshort_release
93
- node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + _event.button.last_event + ' ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
95
+ knxMsgPayload.payload = node.short_releaseValue
94
96
  // Send to KNX bus
95
97
  if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
98
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + _event.button.last_event + ' ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
96
99
  break
97
100
  case 'repeat':
98
- knxMsgPayload.topic = config.GArepeat
99
- knxMsgPayload.dpt = config.dptrepeat
100
- if (!config.toggleValues) node.toggleGArepeat = true
101
- knxMsgPayload.payload = node.toggleGArepeat ? { decr_incr: 1, data: 3 } : { decr_incr: 0, data: 3 }
102
- node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + _event.button.last_event + ' ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
103
- // Send to KNX bus
104
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
101
+ flowMsgPayload = true
102
+ if (node.isTimerDimStopRunning === false) {
103
+ // Set KNX Dim up/down start
104
+ knxMsgPayload.topic = config.GArepeat
105
+ knxMsgPayload.dpt = config.dptrepeat
106
+ knxMsgPayload.payload = node.long_pressValue ? { decr_incr: 1, data: 3 } : { decr_incr: 0, data: 3 }
107
+ // Send to KNX bus
108
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
109
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX start Dim' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
110
+ }
111
+ node.startDimStopper(knxMsgPayload)
105
112
  break
106
113
  default:
107
114
  break
108
115
  }
109
116
 
110
117
  // Setup the output msg
111
- knxMsgPayload.name = node.name
112
- knxMsgPayload.event = _event.button.last_event
113
- knxMsgPayload.rawEvent = _event
114
- node.send(knxMsgPayload)
118
+ const flowMsg = {}
119
+ flowMsg.name = node.name
120
+ flowMsg.event = _event.button.last_event
121
+ flowMsg.rawEvent = _event
122
+ flowMsg.payload = flowMsgPayload
123
+ node.send(flowMsg)
115
124
  }
116
125
  } catch (error) {
117
126
  node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
118
127
  }
119
128
  }
120
129
 
130
+ // Timer to stop the dimming sequence
131
+ node.startDimStopper = function (knxMsgPayload) {
132
+ if (node.timerDimStop !== undefined) clearInterval(node.timerDimStop)
133
+ node.isTimerDimStopRunning = true
134
+ node.timerDimStop = setTimeout(() => {
135
+ // KNX Stop DIM
136
+ knxMsgPayload.payload = { decr_incr: 0, data: 0 } // Payload for the output msg
137
+ // Send to KNX bus
138
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
139
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Stop DIM' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
140
+ node.isTimerDimStopRunning = false
141
+ }, 700);
142
+ }
143
+
121
144
  // On each deploy, unsubscribe+resubscribe
122
145
  if (node.server) {
123
146
  node.server.removeClient(node)
@@ -69,9 +69,16 @@ module.exports = function (RED) {
69
69
  node.serverHue.hueManager.setLightState(config.hueDevice, state)
70
70
  break
71
71
  case config.GALightDIM:
72
+ // { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
73
+ // { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
72
74
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightDIM))
73
- state = msg.payload.decr_incr === 1 ? { dimming_delta: { action: 'up', brightness_delta: 20 } } : { dimming_delta: { action: 'down', brightness_delta: 20 } }
74
- node.serverHue.hueManager.setLightState(config.hueDevice, state)
75
+ if (msg.payload.data > 0) {
76
+ let dimDirection = 'down'
77
+ dimDirection = msg.payload.decr_incr === 1 ? 'up' : 'down'
78
+ node.startDim(dimDirection)
79
+ } else {
80
+ node.startDim('stop')
81
+ }
75
82
  break
76
83
  case config.GALightBrightness:
77
84
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness))
@@ -94,6 +101,31 @@ module.exports = function (RED) {
94
101
  node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
95
102
  }
96
103
  }
104
+ // Start dimming
105
+ node.timerDim = undefined
106
+ node.dimDirection = {}
107
+ node.timeoutDim = 0
108
+ node.startDim = function (_direction) {
109
+ clearInterval(node.timerDim)
110
+ if (_direction === "stop") return
111
+ switch (_direction) {
112
+ case 'up':
113
+ node.dimDirection = { dimming_delta: { action: 'up', brightness_delta: 5 } }
114
+ break
115
+ case 'down':
116
+ node.dimDirection = { dimming_delta: { action: 'down', brightness_delta: 5 } }
117
+ break
118
+ default:
119
+ break
120
+ }
121
+ node.timerDim = setInterval(() => {
122
+ node.timeoutDim += 1
123
+ if (node.timeoutDim > 100) { node.timeoutDim = 0; clearInterval(node.timerDim) }
124
+ node.serverHue.hueManager.setLightState(config.hueDevice, node.dimDirection)
125
+ }, 50);
126
+ }
127
+
128
+
97
129
 
98
130
  node.handleSendHUE = _event => {
99
131
  try {
@@ -39,6 +39,7 @@ module.exports = function (RED) {
39
39
  node.formatnegativevalue = 'leave'
40
40
  node.formatdecimalsvalue = 2
41
41
  node.brightnessState = 0
42
+ node.isTimerDimStopRunning = false
42
43
 
43
44
  // Read the state of the light and store it in the holding object
44
45
  try {
@@ -63,9 +64,16 @@ module.exports = function (RED) {
63
64
  const knxMsgPayload = {}
64
65
  knxMsgPayload.topic = config.GArepeat
65
66
  knxMsgPayload.dpt = config.dptrepeat
67
+
66
68
  if (_event.relative_rotary.last_event.rotation.direction === 'clock_wise') {
67
69
  if (knxMsgPayload.dpt.startsWith('3.007')) {
68
- knxMsgPayload.payload = { decr_incr: 1, data: 3 }
70
+ if (node.isTimerDimStopRunning === false) {
71
+ // Set KNX Dim up/down start
72
+ knxMsgPayload.payload = { decr_incr: 1, data: 3 } // Send to KNX bus
73
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
74
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX start Dim' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
75
+ }
76
+ node.startDimStopper(knxMsgPayload)
69
77
  } else if (knxMsgPayload.dpt.startsWith('5.001')) {
70
78
  //0 – maximum: 32767
71
79
  node.brightnessState < 100 ? node.brightnessState += 20 : node.brightnessState = 100
@@ -73,17 +81,19 @@ module.exports = function (RED) {
73
81
  }
74
82
  } else if (_event.relative_rotary.last_event.rotation.direction === 'counter_clock_wise') {
75
83
  if (knxMsgPayload.dpt.startsWith('3.007')) {
76
- knxMsgPayload.payload = { decr_incr: 0, data: 3 }
84
+ if (node.isTimerDimStopRunning === false) {
85
+ // Set KNX Dim up/down start
86
+ knxMsgPayload.payload = { decr_incr: 0, data: 3 } // Send to KNX bus
87
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
88
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX start Dim' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
89
+ }
90
+ node.startDimStopper(knxMsgPayload)
77
91
  } else if (knxMsgPayload.dpt.startsWith('5.001')) {
78
92
  node.brightnessState > 0 ? node.brightnessState -= 20 : node.brightnessState = 0
79
93
  knxMsgPayload.payload = node.brightnessState
80
94
  }
81
95
  }
82
96
 
83
- // Send to KNX bus
84
- if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, 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
97
  // Setup the output msg
88
98
  knxMsgPayload.name = node.name
89
99
  knxMsgPayload.event = 'rotation ' + _event.relative_rotary.last_event.rotation.direction
@@ -95,6 +105,20 @@ module.exports = function (RED) {
95
105
  }
96
106
  }
97
107
 
108
+ // Timer to stop the dimming sequence
109
+ node.startDimStopper = function (knxMsgPayload) {
110
+ if (node.timerDimStop !== undefined) clearInterval(node.timerDimStop)
111
+ node.isTimerDimStopRunning = true
112
+ node.timerDimStop = setTimeout(() => {
113
+ // KNX Stop DIM
114
+ knxMsgPayload.payload = { decr_incr: 0, data: 0 } // Payload for the output msg
115
+ // Send to KNX bus
116
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
117
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX Stop DIM' + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
118
+ node.isTimerDimStopRunning = false
119
+ }, 500);
120
+ }
121
+
98
122
  // On each deploy, unsubscribe+resubscribe
99
123
  if (node.server) {
100
124
  node.server.removeClient(node)
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=14.0.0"
5
5
  },
6
- "version": "2.0.15",
6
+ "version": "2.0.16",
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",