node-red-contrib-knx-ultimate 6.2.2 → 6.2.3-beta.1

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.
Files changed (141) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/nodes/knxUltimateAI.js +7 -1
  3. package/nodes/knxUltimateHueAreaMotion.html +16 -4
  4. package/nodes/knxUltimateHueBattery.html +16 -4
  5. package/nodes/knxUltimateHueButton.html +16 -4
  6. package/nodes/knxUltimateHueCameraMotion.html +16 -4
  7. package/nodes/knxUltimateHueContactSensor.html +16 -4
  8. package/nodes/knxUltimateHueController.html +1259 -0
  9. package/nodes/knxUltimateHueController.js +28 -0
  10. package/nodes/knxUltimateHueHumiditySensor.html +16 -4
  11. package/nodes/knxUltimateHueLight.html +16 -4
  12. package/nodes/knxUltimateHueLightSensor.html +16 -4
  13. package/nodes/knxUltimateHueMotion.html +16 -4
  14. package/nodes/knxUltimateHuePlug.html +16 -4
  15. package/nodes/knxUltimateHueScene.html +19 -7
  16. package/nodes/knxUltimateHueTapDial.html +16 -4
  17. package/nodes/knxUltimateHueTemperatureSensor.html +16 -4
  18. package/nodes/knxUltimateHueZigbeeConnectivity.html +16 -4
  19. package/nodes/knxUltimateHuedevice_software_update.html +16 -4
  20. package/nodes/locales/de/knxUltimateHueAreaMotion.html +1 -1
  21. package/nodes/locales/de/knxUltimateHueBattery.html +1 -0
  22. package/nodes/locales/de/knxUltimateHueButton.html +1 -0
  23. package/nodes/locales/de/knxUltimateHueCameraMotion.html +1 -0
  24. package/nodes/locales/de/knxUltimateHueContactSensor.html +1 -0
  25. package/nodes/locales/de/knxUltimateHueController.html +668 -0
  26. package/nodes/locales/de/knxUltimateHueController.json +47 -0
  27. package/nodes/locales/de/knxUltimateHueHumiditySensor.html +1 -0
  28. package/nodes/locales/de/knxUltimateHueLight.html +1 -0
  29. package/nodes/locales/de/knxUltimateHueLightSensor.html +1 -0
  30. package/nodes/locales/de/knxUltimateHueMotion.html +1 -0
  31. package/nodes/locales/de/knxUltimateHuePlug.html +1 -0
  32. package/nodes/locales/de/knxUltimateHueScene.html +1 -0
  33. package/nodes/locales/de/knxUltimateHueTapDial.html +1 -0
  34. package/nodes/locales/de/knxUltimateHueTemperatureSensor.html +1 -0
  35. package/nodes/locales/de/knxUltimateHueZigbeeConnectivity.html +1 -0
  36. package/nodes/locales/de/knxUltimateHuedevice_software_update.html +1 -0
  37. package/nodes/locales/en/knxUltimateHueAreaMotion.html +1 -1
  38. package/nodes/locales/en/knxUltimateHueBattery.html +1 -0
  39. package/nodes/locales/en/knxUltimateHueButton.html +1 -0
  40. package/nodes/locales/en/knxUltimateHueCameraMotion.html +1 -0
  41. package/nodes/locales/en/knxUltimateHueContactSensor.html +1 -0
  42. package/nodes/locales/en/knxUltimateHueController.html +671 -0
  43. package/nodes/locales/en/knxUltimateHueController.json +47 -0
  44. package/nodes/locales/en/knxUltimateHueHumiditySensor.html +1 -0
  45. package/nodes/locales/en/knxUltimateHueLight.html +1 -0
  46. package/nodes/locales/en/knxUltimateHueLightSensor.html +1 -0
  47. package/nodes/locales/en/knxUltimateHueMotion.html +1 -0
  48. package/nodes/locales/en/knxUltimateHuePlug.html +1 -0
  49. package/nodes/locales/en/knxUltimateHueScene.html +1 -0
  50. package/nodes/locales/en/knxUltimateHueTapDial.html +1 -0
  51. package/nodes/locales/en/knxUltimateHueTemperatureSensor.html +1 -0
  52. package/nodes/locales/en/knxUltimateHueZigbeeConnectivity.html +1 -0
  53. package/nodes/locales/en/knxUltimateHuedevice_software_update.html +1 -0
  54. package/nodes/locales/es/knxUltimateHueAreaMotion.html +1 -1
  55. package/nodes/locales/es/knxUltimateHueBattery.html +1 -0
  56. package/nodes/locales/es/knxUltimateHueButton.html +1 -0
  57. package/nodes/locales/es/knxUltimateHueCameraMotion.html +1 -0
  58. package/nodes/locales/es/knxUltimateHueContactSensor.html +1 -0
  59. package/nodes/locales/es/knxUltimateHueController.html +671 -0
  60. package/nodes/locales/es/knxUltimateHueController.json +47 -0
  61. package/nodes/locales/es/knxUltimateHueHumiditySensor.html +1 -0
  62. package/nodes/locales/es/knxUltimateHueLight.html +1 -0
  63. package/nodes/locales/es/knxUltimateHueLightSensor.html +1 -0
  64. package/nodes/locales/es/knxUltimateHueMotion.html +1 -0
  65. package/nodes/locales/es/knxUltimateHuePlug.html +1 -0
  66. package/nodes/locales/es/knxUltimateHueScene.html +1 -0
  67. package/nodes/locales/es/knxUltimateHueTapDial.html +1 -0
  68. package/nodes/locales/es/knxUltimateHueTemperatureSensor.html +1 -0
  69. package/nodes/locales/es/knxUltimateHueZigbeeConnectivity.html +1 -0
  70. package/nodes/locales/es/knxUltimateHuedevice_software_update.html +1 -0
  71. package/nodes/locales/fr/knxUltimateHueAreaMotion.html +1 -1
  72. package/nodes/locales/fr/knxUltimateHueBattery.html +1 -0
  73. package/nodes/locales/fr/knxUltimateHueButton.html +1 -0
  74. package/nodes/locales/fr/knxUltimateHueCameraMotion.html +1 -0
  75. package/nodes/locales/fr/knxUltimateHueContactSensor.html +1 -0
  76. package/nodes/locales/fr/knxUltimateHueController.html +671 -0
  77. package/nodes/locales/fr/knxUltimateHueController.json +47 -0
  78. package/nodes/locales/fr/knxUltimateHueHumiditySensor.html +1 -0
  79. package/nodes/locales/fr/knxUltimateHueLight.html +1 -0
  80. package/nodes/locales/fr/knxUltimateHueLightSensor.html +1 -0
  81. package/nodes/locales/fr/knxUltimateHueMotion.html +1 -0
  82. package/nodes/locales/fr/knxUltimateHuePlug.html +1 -0
  83. package/nodes/locales/fr/knxUltimateHueScene.html +1 -0
  84. package/nodes/locales/fr/knxUltimateHueTapDial.html +1 -0
  85. package/nodes/locales/fr/knxUltimateHueTemperatureSensor.html +1 -0
  86. package/nodes/locales/fr/knxUltimateHueZigbeeConnectivity.html +1 -0
  87. package/nodes/locales/fr/knxUltimateHuedevice_software_update.html +1 -0
  88. package/nodes/locales/it/knxUltimateHueAreaMotion.html +1 -1
  89. package/nodes/locales/it/knxUltimateHueBattery.html +1 -0
  90. package/nodes/locales/it/knxUltimateHueButton.html +1 -0
  91. package/nodes/locales/it/knxUltimateHueCameraMotion.html +1 -0
  92. package/nodes/locales/it/knxUltimateHueContactSensor.html +1 -0
  93. package/nodes/locales/it/knxUltimateHueController.html +671 -0
  94. package/nodes/locales/it/knxUltimateHueController.json +47 -0
  95. package/nodes/locales/it/knxUltimateHueHumiditySensor.html +1 -0
  96. package/nodes/locales/it/knxUltimateHueLight.html +1 -0
  97. package/nodes/locales/it/knxUltimateHueLightSensor.html +1 -0
  98. package/nodes/locales/it/knxUltimateHueMotion.html +1 -0
  99. package/nodes/locales/it/knxUltimateHuePlug.html +1 -0
  100. package/nodes/locales/it/knxUltimateHueScene.html +1 -0
  101. package/nodes/locales/it/knxUltimateHueTapDial.html +1 -0
  102. package/nodes/locales/it/knxUltimateHueTemperatureSensor.html +1 -0
  103. package/nodes/locales/it/knxUltimateHueZigbeeConnectivity.html +1 -0
  104. package/nodes/locales/it/knxUltimateHuedevice_software_update.html +1 -0
  105. package/nodes/locales/zh-CN/knxUltimateHueAreaMotion.html +1 -1
  106. package/nodes/locales/zh-CN/knxUltimateHueBattery.html +1 -0
  107. package/nodes/locales/zh-CN/knxUltimateHueButton.html +1 -0
  108. package/nodes/locales/zh-CN/knxUltimateHueCameraMotion.html +1 -0
  109. package/nodes/locales/zh-CN/knxUltimateHueContactSensor.html +1 -0
  110. package/nodes/locales/zh-CN/knxUltimateHueController.html +673 -0
  111. package/nodes/locales/zh-CN/knxUltimateHueController.json +47 -0
  112. package/nodes/locales/zh-CN/knxUltimateHueHumiditySensor.html +1 -0
  113. package/nodes/locales/zh-CN/knxUltimateHueLight.html +1 -0
  114. package/nodes/locales/zh-CN/knxUltimateHueLightSensor.html +1 -0
  115. package/nodes/locales/zh-CN/knxUltimateHueMotion.html +1 -0
  116. package/nodes/locales/zh-CN/knxUltimateHuePlug.html +1 -0
  117. package/nodes/locales/zh-CN/knxUltimateHueScene.html +1 -0
  118. package/nodes/locales/zh-CN/knxUltimateHueTapDial.html +1 -0
  119. package/nodes/locales/zh-CN/knxUltimateHueTemperatureSensor.html +1 -0
  120. package/nodes/locales/zh-CN/knxUltimateHueZigbeeConnectivity.html +1 -0
  121. package/nodes/locales/zh-CN/knxUltimateHuedevice_software_update.html +1 -0
  122. package/nodes/utils/hueControllerProfileAdapter.js +104 -0
  123. package/nodes/utils/hueControllerProfiles/runtime/area_motion.js +221 -0
  124. package/nodes/utils/hueControllerProfiles/runtime/button.js +295 -0
  125. package/nodes/utils/hueControllerProfiles/runtime/camera_motion.js +221 -0
  126. package/nodes/utils/hueControllerProfiles/runtime/contact.js +191 -0
  127. package/nodes/utils/hueControllerProfiles/runtime/device_power.js +185 -0
  128. package/nodes/utils/hueControllerProfiles/runtime/device_software_update.js +185 -0
  129. package/nodes/utils/hueControllerProfiles/runtime/humidity.js +204 -0
  130. package/nodes/utils/hueControllerProfiles/runtime/light.js +1583 -0
  131. package/nodes/utils/hueControllerProfiles/runtime/light_level.js +180 -0
  132. package/nodes/utils/hueControllerProfiles/runtime/motion.js +185 -0
  133. package/nodes/utils/hueControllerProfiles/runtime/plug.js +344 -0
  134. package/nodes/utils/hueControllerProfiles/runtime/relative_rotary.js +271 -0
  135. package/nodes/utils/hueControllerProfiles/runtime/scene.js +352 -0
  136. package/nodes/utils/hueControllerProfiles/runtime/temperature.js +187 -0
  137. package/nodes/utils/hueControllerProfiles/runtime/zigbee_connectivity.js +185 -0
  138. package/package.json +5 -2
  139. package/resources/hueControllerMigration.js +338 -0
  140. package/resources/hueControllerMigrationDialog.js +235 -0
  141. package/resources/hueControllerProfiles.js +9290 -0
@@ -0,0 +1,180 @@
1
+ // Canonical private runtime profile for HUE Controller: light_level.
2
+ // This module is shipped with the Controller but is intentionally absent from
3
+ // package.json, so Node-RED never registers it as a separate palette node.
4
+ module.exports = function (RED) {
5
+ function knxUltimateHueLightSensor (config) {
6
+ RED.nodes.createNode(this, config)
7
+ const node = this
8
+ node.serverKNX = RED.nodes.getNode(config.server) || undefined
9
+ node.serverHue = RED.nodes.getNode(config.serverHue) || undefined
10
+ node.topic = node.name
11
+ node.name = config.name === undefined ? 'Hue' : config.name
12
+ node.dpt = ''
13
+ node.notifyreadrequest = true
14
+ node.notifyreadrequestalsorespondtobus = 'false'
15
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
16
+ node.notifyresponse = false
17
+ node.notifywrite = true
18
+ node.initialread = true
19
+ node.listenallga = true // Don't remove
20
+ node.outputtype = 'write'
21
+ node.outputRBE = 'false' // Apply or not RBE to the output (Messages coming from flow)
22
+ node.inputRBE = 'false' // Apply or not RBE to the input (Messages coming from BUS)
23
+ node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
24
+ node.passthrough = 'no'
25
+ node.formatmultiplyvalue = 1
26
+ node.formatnegativevalue = 'leave'
27
+ node.formatdecimalsvalue = 2
28
+ node.hueDevice = config.hueDevice
29
+ node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === 'yes')
30
+ node.currentDeviceValue = 0
31
+
32
+ const pushStatus = (status) => {
33
+ if (!status) return
34
+ const provider = node.serverKNX
35
+ if (provider && typeof provider.applyStatusUpdate === 'function') {
36
+ provider.applyStatusUpdate(node, status)
37
+ } else {
38
+ node.status(status)
39
+ }
40
+ }
41
+
42
+ const updateStatus = (status) => {
43
+ if (!status) return
44
+ pushStatus(status)
45
+ }
46
+
47
+ const formatTs = (date) => {
48
+ const d = date instanceof Date ? date : new Date(date)
49
+ const provider = node.serverKNX
50
+ if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
51
+ return `${d.getDate()}, ${d.toLocaleTimeString()}`
52
+ }
53
+
54
+ const safeSendToKNX = (telegram, context = 'write') => {
55
+ try {
56
+ if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
57
+ const now = new Date()
58
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(now)})` })
59
+ return
60
+ }
61
+ node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
62
+ } catch (error) {
63
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
64
+ }
65
+ }
66
+
67
+ // Used to call the status update from the config node.
68
+ node.setNodeStatus = ({
69
+ fill, shape, text, payload
70
+ }) => {
71
+ try {
72
+ if (payload === undefined) payload = ''
73
+ const dDate = new Date()
74
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
75
+ node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
76
+ pushStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
77
+ } catch (error) { }
78
+ }
79
+ // Used to call the status update from the HUE config node.
80
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
81
+ try {
82
+ if (payload === undefined) payload = ''
83
+ const dDate = new Date()
84
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
85
+ node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${formatTs(dDate)})`
86
+ pushStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
87
+ } catch (error) { }
88
+ }
89
+
90
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
91
+ node.handleSend = msg => {
92
+ // Respond to KNX read telegram, by sending the current value as response telegram.
93
+ if (msg.knx.event === 'GroupValue_Read') {
94
+ switch (msg.knx.destination) {
95
+ case config.GAlightsensor:
96
+ // To the KNX bus wires
97
+ node.sendResponseToKNX(node.currentDeviceValue)
98
+ break
99
+ default:
100
+ break
101
+ }
102
+ }
103
+ }
104
+
105
+ node.handleSendHUE = _event => {
106
+ try {
107
+ if (_event.id === config.hueDevice) {
108
+ if (!_event.hasOwnProperty('light') || _event.light.light_level === undefined) return
109
+
110
+ const knxMsgPayload = {}
111
+ knxMsgPayload.topic = config.GAlightsensor
112
+ knxMsgPayload.dpt = config.dptlightsensor
113
+
114
+ if (_event.hasOwnProperty('light') && _event.light.hasOwnProperty('light_level')) {
115
+ // console.log(Math.round(10 ** ((_event.light.light_level - 1) / 10000)))
116
+ // console.log(_event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000)))
117
+ knxMsgPayload.payload = _event.light.light_level === 0 ? 0 : Math.round(Math.pow(10, (_event.light.light_level - 1) / 10000))
118
+ // Send to KNX bus
119
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
120
+ safeSendToKNX({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write' }, 'write')
121
+ }
122
+ node.currentDeviceValue = knxMsgPayload.payload
123
+
124
+ updateStatus({ fill: 'green', shape: 'dot', text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${formatTs(new Date())})` })
125
+
126
+ // Setup the output msg
127
+ knxMsgPayload.name = node.name
128
+ knxMsgPayload.event = 'light_level'
129
+
130
+ // Send payload
131
+ knxMsgPayload.rawEvent = _event
132
+ node.send(knxMsgPayload)
133
+ node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload })
134
+ }
135
+ }
136
+ } catch (error) {
137
+ updateStatus({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${formatTs(new Date())})` })
138
+ }
139
+ }
140
+
141
+ node.sendResponseToKNX = (_level) => {
142
+ const knxMsgPayload = {}
143
+ knxMsgPayload.topic = config.GAlightsensor
144
+ knxMsgPayload.dpt = config.dptlightsensor
145
+
146
+ knxMsgPayload.payload = _level
147
+ // Send to KNX bus
148
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
149
+ safeSendToKNX({
150
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response'
151
+ }, 'response')
152
+ }
153
+ }
154
+
155
+ // On each deploy, unsubscribe+resubscribe
156
+ if (node.serverKNX) {
157
+ node.serverKNX.removeClient(node)
158
+ node.serverKNX.addClient(node)
159
+ }
160
+ if (node.serverHue) {
161
+ node.serverHue.removeClient(node)
162
+ node.serverHue.addClient(node)
163
+ }
164
+
165
+ node.on('input', function (msg) {
166
+
167
+ })
168
+
169
+ node.on('close', function (done) {
170
+ if (node.serverKNX) {
171
+ node.serverKNX.removeClient(node)
172
+ }
173
+ if (node.serverHue) {
174
+ node.serverHue.removeClient(node)
175
+ }
176
+ done()
177
+ })
178
+ }
179
+ RED.nodes.registerType('knxUltimateHueLightSensor', knxUltimateHueLightSensor)
180
+ }
@@ -0,0 +1,185 @@
1
+ // Canonical private runtime profile for HUE Controller: motion.
2
+ // This module is shipped with the Controller but is intentionally absent from
3
+ // package.json, so Node-RED never registers it as a separate palette node.
4
+ module.exports = function (RED) {
5
+ function knxUltimateHueMotion (config) {
6
+ RED.nodes.createNode(this, config)
7
+ const node = this
8
+ node.serverKNX = RED.nodes.getNode(config.server) || undefined
9
+ node.serverHue = RED.nodes.getNode(config.serverHue) || undefined
10
+ node.topic = node.name
11
+ node.name = config.name === undefined ? 'Hue' : config.name
12
+ node.dpt = ''
13
+ node.notifyreadrequest = false
14
+ node.notifyreadrequestalsorespondtobus = 'false'
15
+ node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
16
+ node.notifyresponse = false
17
+ node.notifywrite = true
18
+ node.initialread = true
19
+ node.listenallga = true // Don't remove
20
+ node.outputtype = 'write'
21
+ node.outputRBE = 'false' // Apply or not RBE to the output (Messages coming from flow)
22
+ node.inputRBE = 'false' // Apply or not RBE to the input (Messages coming from BUS)
23
+ node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
24
+ node.passthrough = 'no'
25
+ node.formatmultiplyvalue = 1
26
+ node.formatnegativevalue = 'leave'
27
+ node.formatdecimalsvalue = 2
28
+ node.hueDevice = config.hueDevice
29
+ // Re-publish the authoritative motion state at startup and after every event-stream
30
+ // (re)connection, so events missed during an SSE reconnect gap don't leave KNX stuck
31
+ // on a wrong value (same root cause as the contact sensor, issue #514).
32
+ node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === 'yes')
33
+ // Timestamp (ms) of the last motion_report.changed we already applied to KNX. Used to
34
+ // ignore stale/out-of-order reports (e.g. replays after a reconnect).
35
+ node.lastMotionChangedTs = undefined
36
+ const pinsSetting = (config.enableNodePINS === undefined || config.enableNodePINS === 'yes' || config.enableNodePINS === true)
37
+ node.enableNodePINS = pinsSetting ? 'yes' : 'no'
38
+ node.outputs = pinsSetting ? 1 : 0
39
+ if (!node.serverKNX && node.outputs === 0) {
40
+ node.enableNodePINS = 'yes'
41
+ node.outputs = 1
42
+ }
43
+
44
+ const pushStatus = (status) => {
45
+ if (!status) return
46
+ const provider = node.serverKNX
47
+ if (provider && typeof provider.applyStatusUpdate === 'function') {
48
+ provider.applyStatusUpdate(node, status)
49
+ } else {
50
+ node.status(status)
51
+ }
52
+ }
53
+
54
+ const updateStatus = (status) => {
55
+ if (!status) return
56
+ pushStatus(status)
57
+ }
58
+
59
+ const formatTs = (date) => {
60
+ const d = date instanceof Date ? date : new Date(date)
61
+ const provider = node.serverKNX
62
+ if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
63
+ return `${d.getDate()}, ${d.toLocaleTimeString()}`
64
+ }
65
+
66
+ const safeSendToKNX = (telegram, context = 'write') => {
67
+ try {
68
+ if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
69
+ const now = new Date()
70
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(now)})` })
71
+ return
72
+ }
73
+ node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
74
+ } catch (error) {
75
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
76
+ }
77
+ }
78
+
79
+ // Used to call the status update from the config node.
80
+ node.setNodeStatus = ({
81
+ fill, shape, text, payload
82
+ }) => {
83
+ try {
84
+ if (payload === undefined) payload = ''
85
+ const dDate = new Date()
86
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
87
+ node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
88
+ pushStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
89
+ } catch (error) { }
90
+ }
91
+ // Used to call the status update from the HUE config node.
92
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
93
+ try {
94
+ if (payload === undefined) payload = ''
95
+ const dDate = new Date()
96
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
97
+ node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${formatTs(dDate)})`
98
+ pushStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
99
+ } catch (error) { }
100
+ }
101
+
102
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
103
+ node.handleSend = (msg) => { }
104
+
105
+ node.handleSendHUE = (_event) => {
106
+ try {
107
+ if (_event.id === config.hueDevice) {
108
+ if (!_event.hasOwnProperty('motion') || _event.motion.motion === undefined) return
109
+
110
+ // Ignore reports that are not newer than the last one applied, so stale/replayed
111
+ // events after an SSE reconnect can't push a wrong state to KNX (issue #514).
112
+ const motionChanged = _event.motion.motion_report !== undefined ? _event.motion.motion_report.changed : undefined
113
+ const changedTs = motionChanged !== undefined ? new Date(motionChanged).getTime() : NaN
114
+ if (!Number.isNaN(changedTs)) {
115
+ if (node.lastMotionChangedTs !== undefined && changedTs <= node.lastMotionChangedTs) {
116
+ return
117
+ }
118
+ node.lastMotionChangedTs = changedTs
119
+ }
120
+
121
+ const knxMsgPayload = {}
122
+ knxMsgPayload.topic = config.GAmotion
123
+ knxMsgPayload.dpt = config.dptmotion
124
+
125
+ if (_event.hasOwnProperty('motion') && _event.motion.hasOwnProperty('motion')) {
126
+ knxMsgPayload.payload = _event.motion.motion_report.motion
127
+ // Send to KNX bus
128
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
129
+ safeSendToKNX({
130
+ grpaddr: knxMsgPayload.topic,
131
+ payload: knxMsgPayload.payload,
132
+ dpt: knxMsgPayload.dpt,
133
+ outputtype: 'write'
134
+ }, 'write')
135
+ }
136
+ updateStatus({
137
+ fill: 'green',
138
+ shape: 'dot',
139
+ text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${formatTs(new Date())})`
140
+ })
141
+
142
+ // Setup the output msg
143
+ knxMsgPayload.name = node.name
144
+ knxMsgPayload.event = 'motion'
145
+
146
+ // Send payload
147
+ knxMsgPayload.rawEvent = _event
148
+ node.send(knxMsgPayload)
149
+ node.setNodeStatusHue({
150
+ fill: 'blue',
151
+ shape: 'ring',
152
+ text: 'HUE->KNX',
153
+ payload: knxMsgPayload.payload
154
+ })
155
+ }
156
+ }
157
+ } catch (error) {
158
+ updateStatus({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${formatTs(new Date())})` })
159
+ }
160
+ }
161
+
162
+ // On each deploy, unsubscribe+resubscribe
163
+ if (node.serverKNX) {
164
+ node.serverKNX.removeClient(node)
165
+ node.serverKNX.addClient(node)
166
+ }
167
+ if (node.serverHue) {
168
+ node.serverHue.removeClient(node)
169
+ node.serverHue.addClient(node)
170
+ }
171
+
172
+ node.on('input', (msg) => { })
173
+
174
+ node.on('close', (done) => {
175
+ if (node.serverKNX) {
176
+ node.serverKNX.removeClient(node)
177
+ }
178
+ if (node.serverHue) {
179
+ node.serverHue.removeClient(node)
180
+ }
181
+ done()
182
+ })
183
+ }
184
+ RED.nodes.registerType('knxUltimateHueMotion', knxUltimateHueMotion)
185
+ }