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

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 +15 -4
  4. package/nodes/knxUltimateHueBattery.html +15 -4
  5. package/nodes/knxUltimateHueButton.html +15 -4
  6. package/nodes/knxUltimateHueCameraMotion.html +15 -4
  7. package/nodes/knxUltimateHueContactSensor.html +15 -4
  8. package/nodes/knxUltimateHueController.html +1386 -0
  9. package/nodes/knxUltimateHueController.js +28 -0
  10. package/nodes/knxUltimateHueHumiditySensor.html +15 -4
  11. package/nodes/knxUltimateHueLight.html +15 -4
  12. package/nodes/knxUltimateHueLightSensor.html +15 -4
  13. package/nodes/knxUltimateHueMotion.html +15 -4
  14. package/nodes/knxUltimateHuePlug.html +15 -4
  15. package/nodes/knxUltimateHueScene.html +18 -7
  16. package/nodes/knxUltimateHueTapDial.html +15 -4
  17. package/nodes/knxUltimateHueTemperatureSensor.html +15 -4
  18. package/nodes/knxUltimateHueZigbeeConnectivity.html +15 -4
  19. package/nodes/knxUltimateHuedevice_software_update.html +15 -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 +669 -0
  26. package/nodes/locales/de/knxUltimateHueController.json +48 -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 +672 -0
  43. package/nodes/locales/en/knxUltimateHueController.json +48 -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 +672 -0
  60. package/nodes/locales/es/knxUltimateHueController.json +48 -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 +672 -0
  77. package/nodes/locales/fr/knxUltimateHueController.json +48 -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 +672 -0
  94. package/nodes/locales/it/knxUltimateHueController.json +48 -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 +674 -0
  111. package/nodes/locales/zh-CN/knxUltimateHueController.json +48 -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 +199 -0
  140. package/resources/hueControllerMigrationDialog.js +189 -0
  141. package/resources/hueControllerProfiles.js +9290 -0
@@ -0,0 +1,191 @@
1
+ // Canonical private runtime profile for HUE Controller: contact.
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 knxUltimateHueContactSensor (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 contact state at startup and after every event-stream
30
+ // (re)connection. Without this, any contact_report event missed during an SSE reconnect
31
+ // gap leaves KNX stuck on the last value seen (see issue #514).
32
+ node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === 'yes')
33
+ // Timestamp (ms) of the last contact_report.changed we already applied to KNX. Used to
34
+ // ignore stale/out-of-order reports (e.g. replays after a reconnect).
35
+ node.lastContactChangedTs = undefined
36
+
37
+ const pushStatus = (status) => {
38
+ if (!status) return
39
+ const provider = node.serverKNX
40
+ if (provider && typeof provider.applyStatusUpdate === 'function') {
41
+ provider.applyStatusUpdate(node, status)
42
+ } else {
43
+ node.status(status)
44
+ }
45
+ }
46
+
47
+ const updateStatus = (status) => {
48
+ if (!status) return
49
+ pushStatus(status)
50
+ }
51
+
52
+ const formatTs = (date) => {
53
+ const d = date instanceof Date ? date : new Date(date)
54
+ const provider = node.serverKNX
55
+ if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
56
+ return `${d.getDate()}, ${d.toLocaleTimeString()}`
57
+ }
58
+
59
+ const safeSendToKNX = (telegram, context = 'write') => {
60
+ try {
61
+ if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
62
+ const now = new Date()
63
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(now)})` })
64
+ return
65
+ }
66
+ node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
67
+ } catch (error) {
68
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
69
+ }
70
+ }
71
+
72
+ // Used to call the status update from the config node.
73
+ node.setNodeStatus = ({
74
+ fill, shape, text, payload
75
+ }) => {
76
+ try {
77
+ if (payload === undefined) payload = ''
78
+ const dDate = new Date()
79
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
80
+ node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
81
+ pushStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
82
+ } catch (error) { }
83
+ }
84
+ // Used to call the status update from the HUE config node.
85
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
86
+ try {
87
+ if (payload === undefined) payload = ''
88
+ const dDate = new Date()
89
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
90
+ node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${formatTs(dDate)})`
91
+ pushStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
92
+ } catch (error) { }
93
+ }
94
+
95
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
96
+ node.handleSend = (msg) => {
97
+ }
98
+
99
+ node.handleSendHUE = (_event) => {
100
+ try {
101
+ if (_event.id === config.hueDevice) {
102
+ if (!_event.hasOwnProperty('contact_report')) {
103
+ return
104
+ }
105
+
106
+ // contact_report.state is always the real current state (per Hue API v2), and
107
+ // contact_report.changed is when it last changed. Ignore reports that are not newer
108
+ // than the last one applied, so stale/replayed events after an SSE reconnect can't
109
+ // flip KNX to a wrong state (issue #514).
110
+ const changedTs = _event.contact_report.changed !== undefined ? new Date(_event.contact_report.changed).getTime() : NaN
111
+ if (!Number.isNaN(changedTs)) {
112
+ if (node.lastContactChangedTs !== undefined && changedTs <= node.lastContactChangedTs) {
113
+ return
114
+ }
115
+ node.lastContactChangedTs = changedTs
116
+ }
117
+
118
+ const knxMsgPayload = {}
119
+ knxMsgPayload.topic = config.GAcontact
120
+ knxMsgPayload.dpt = config.dptcontact
121
+
122
+ if (_event.hasOwnProperty('contact_report')) {
123
+ knxMsgPayload.payload = _event.contact_report.state === 'contact'
124
+
125
+ // Send to KNX bus
126
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
127
+ safeSendToKNX({
128
+ grpaddr: knxMsgPayload.topic,
129
+ payload: knxMsgPayload.payload,
130
+ dpt: knxMsgPayload.dpt,
131
+ outputtype: 'write'
132
+ }, 'write')
133
+ }
134
+
135
+ updateStatus({
136
+ fill: 'green',
137
+ shape: 'dot',
138
+ text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${formatTs(new Date())})`
139
+ })
140
+
141
+ // Set up the output msg
142
+ knxMsgPayload.name = node.name
143
+ knxMsgPayload.event = 'contact'
144
+
145
+ // Send payload
146
+ knxMsgPayload.rawEvent = _event
147
+ node.send(knxMsgPayload)
148
+ node.setNodeStatusHue({
149
+ fill: 'blue',
150
+ shape: 'ring',
151
+ text: 'HUE->KNX',
152
+ payload: knxMsgPayload.payload
153
+ })
154
+ }
155
+ }
156
+ } catch (error) {
157
+ updateStatus({
158
+ fill: 'red',
159
+ shape: 'dot',
160
+ text: `HUE->KNX error ${error.message} (${formatTs(new Date())})`
161
+ })
162
+ }
163
+ }
164
+
165
+ // On each deploy, unsubscribe+resubscribe
166
+ if (node.serverKNX) {
167
+ node.serverKNX.removeClient(node)
168
+ node.serverKNX.addClient(node)
169
+ }
170
+
171
+ if (node.serverHue) {
172
+ node.serverHue.removeClient(node)
173
+ node.serverHue.addClient(node)
174
+ }
175
+
176
+ node.on('input', (msg) => {
177
+ })
178
+
179
+ node.on('close', (done) => {
180
+ if (node.serverKNX) {
181
+ node.serverKNX.removeClient(node)
182
+ }
183
+ if (node.serverHue) {
184
+ node.serverHue.removeClient(node)
185
+ }
186
+ done()
187
+ })
188
+ }
189
+
190
+ RED.nodes.registerType('knxUltimateHueContactSensor', knxUltimateHueContactSensor)
191
+ }
@@ -0,0 +1,185 @@
1
+ // Canonical private runtime profile for HUE Controller: device_power.
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 knxUltimateHueBattery (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
+ const pinsSetting = (config.enableNodePINS === undefined || config.enableNodePINS === 'yes' || config.enableNodePINS === true)
32
+ node.enableNodePINS = pinsSetting ? 'yes' : 'no'
33
+ node.outputs = pinsSetting ? 1 : 0
34
+ if (!node.serverKNX && node.outputs === 0) {
35
+ node.enableNodePINS = 'yes'
36
+ node.outputs = 1
37
+ }
38
+
39
+ const pushStatus = (status) => {
40
+ if (!status) return
41
+ const provider = node.serverKNX
42
+ if (provider && typeof provider.applyStatusUpdate === 'function') {
43
+ provider.applyStatusUpdate(node, status)
44
+ } else {
45
+ node.status(status)
46
+ }
47
+ }
48
+
49
+ const updateStatus = (status) => {
50
+ if (!status) return
51
+ pushStatus(status)
52
+ }
53
+
54
+ const formatTs = (date) => {
55
+ const d = date instanceof Date ? date : new Date(date)
56
+ const provider = node.serverKNX
57
+ if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
58
+ return `${d.getDate()}, ${d.toLocaleTimeString()}`
59
+ }
60
+
61
+ const safeSendToKNX = (telegram, context = 'write') => {
62
+ try {
63
+ if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
64
+ const now = new Date()
65
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(now)})` })
66
+ return
67
+ }
68
+ node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
69
+ } catch (error) {
70
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
71
+ }
72
+ }
73
+
74
+ // Used to call the status update from the config node.
75
+ node.setNodeStatus = ({
76
+ fill, shape, text, payload
77
+ }) => {
78
+ try {
79
+ if (payload === undefined) payload = ''
80
+ const dDate = new Date()
81
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
82
+ node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
83
+ pushStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
84
+ } catch (error) { }
85
+ }
86
+ // Used to call the status update from the HUE config node.
87
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
88
+ try {
89
+ if (payload === undefined) payload = ''
90
+ const dDate = new Date()
91
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
92
+ node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${formatTs(dDate)})`
93
+ pushStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
94
+ } catch (error) { }
95
+ }
96
+
97
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
98
+ node.handleSend = (msg) => {
99
+ // Respond to KNX read telegram, by sending the current value as response telegram.
100
+ if (msg.knx.event === 'GroupValue_Read') {
101
+ switch (msg.knx.destination) {
102
+ case config.GAbatterysensor:
103
+ // To the KNX bus wires
104
+ node.sendResponseToKNX(node.currentDeviceValue)
105
+ break
106
+ default:
107
+ break
108
+ }
109
+ }
110
+ }
111
+
112
+ node.handleSendHUE = (_event) => {
113
+ try {
114
+ if (_event.id === config.hueDevice) {
115
+ if (!_event.hasOwnProperty('power_state') || _event.power_state.battery_level === undefined) return
116
+
117
+ const knxMsgPayload = {}
118
+ knxMsgPayload.topic = config.GAbatterysensor
119
+ knxMsgPayload.dpt = config.dptbatterysensor
120
+
121
+ knxMsgPayload.payload = _event.power_state.battery_level
122
+ // Send to KNX bus
123
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
124
+ safeSendToKNX({
125
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write'
126
+ }, 'write')
127
+ }
128
+ node.currentDeviceValue = knxMsgPayload.payload
129
+
130
+ // Setup the output msg
131
+ knxMsgPayload.name = node.name
132
+ knxMsgPayload.event = 'power_state'
133
+
134
+ // Send payload
135
+ knxMsgPayload.rawEvent = _event
136
+ node.send(knxMsgPayload)
137
+ node.setNodeStatusHue({
138
+ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload
139
+ })
140
+ }
141
+ } catch (error) {
142
+ updateStatus({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${formatTs(new Date())})` })
143
+ }
144
+ }
145
+
146
+ node.sendResponseToKNX = (_level) => {
147
+ const knxMsgPayload = {}
148
+ knxMsgPayload.topic = config.GAbatterysensor
149
+ knxMsgPayload.dpt = config.dptbatterysensor
150
+
151
+ knxMsgPayload.payload = _level
152
+ // Send to KNX bus
153
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
154
+ safeSendToKNX({
155
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response'
156
+ }, 'response')
157
+ }
158
+ }
159
+
160
+ // On each deploy, unsubscribe+resubscribe
161
+ if (node.serverKNX) {
162
+ node.serverKNX.removeClient(node)
163
+ node.serverKNX.addClient(node)
164
+ }
165
+ if (node.serverHue) {
166
+ node.serverHue.removeClient(node)
167
+ node.serverHue.addClient(node)
168
+ }
169
+
170
+ node.on('input', (msg) => {
171
+
172
+ })
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('knxUltimateHueBattery', knxUltimateHueBattery)
185
+ }
@@ -0,0 +1,185 @@
1
+ // Canonical private runtime profile for HUE Controller: device_software_update.
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 knxUltimateHuedevice_software_update (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
+ const pinsSetting = (config.enableNodePINS === undefined || config.enableNodePINS === 'yes' || config.enableNodePINS === true)
30
+ node.enableNodePINS = pinsSetting ? 'yes' : 'no'
31
+ node.outputs = pinsSetting ? 1 : 0
32
+ if (!node.serverKNX && node.outputs === 0) {
33
+ node.enableNodePINS = 'yes'
34
+ node.outputs = 1
35
+ }
36
+ node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === 'yes')
37
+ node.currentDeviceValue = false
38
+
39
+ const pushStatus = (status) => {
40
+ if (!status) return
41
+ const provider = node.serverKNX
42
+ if (provider && typeof provider.applyStatusUpdate === 'function') {
43
+ provider.applyStatusUpdate(node, status)
44
+ } else {
45
+ node.status(status)
46
+ }
47
+ }
48
+
49
+ const updateStatus = (status) => {
50
+ if (!status) return
51
+ pushStatus(status)
52
+ }
53
+
54
+ const formatTs = (date) => {
55
+ const d = date instanceof Date ? date : new Date(date)
56
+ const provider = node.serverKNX
57
+ if (provider && typeof provider.formatStatusTimestamp === 'function') return provider.formatStatusTimestamp(d)
58
+ return `${d.getDate()}, ${d.toLocaleTimeString()}`
59
+ }
60
+
61
+ const safeSendToKNX = (telegram, context = 'write') => {
62
+ try {
63
+ if (!node.serverKNX || typeof node.serverKNX.sendKNXTelegramToKNXEngine !== 'function') {
64
+ const now = new Date()
65
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX server missing (${context}) (${formatTs(now)})` })
66
+ return
67
+ }
68
+ node.serverKNX.sendKNXTelegramToKNXEngine({ ...telegram, nodecallerid: node.id })
69
+ } catch (error) {
70
+ updateStatus({ fill: 'red', shape: 'dot', text: `KNX send error ${error.message}` })
71
+ }
72
+ }
73
+
74
+ // Used to call the status update from the config node.
75
+ node.setNodeStatus = ({
76
+ fill, shape, text, payload
77
+ }) => {
78
+ try {
79
+ if (payload === undefined) payload = ''
80
+ const dDate = new Date()
81
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
82
+ node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${formatTs(dDate)})`
83
+ pushStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') })
84
+ } catch (error) { }
85
+ }
86
+ // Used to call the status update from the HUE config node.
87
+ node.setNodeStatusHue = ({ fill, shape, text, payload }) => {
88
+ try {
89
+ if (payload === undefined) payload = ''
90
+ const dDate = new Date()
91
+ payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString()
92
+ node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${formatTs(dDate)})`
93
+ pushStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') })
94
+ } catch (error) { }
95
+ }
96
+
97
+ // This function is called by the knx-ultimate config node, to output a msg.payload.
98
+ node.handleSend = (msg) => {
99
+ // Respond to KNX read telegram, by sending the current value as response telegram.
100
+ if (msg.knx.event === 'GroupValue_Read') {
101
+ switch (msg.knx.destination) {
102
+ case config.GAdevice_software_update:
103
+ // To the KNX bus wires
104
+ node.sendResponseToKNX(node.currentDeviceValue)
105
+ break
106
+ default:
107
+ break
108
+ }
109
+ }
110
+ }
111
+
112
+ node.handleSendHUE = (_event) => {
113
+ try {
114
+ // Event status: one of connected, disconnected, connectivity_issue, unidirectional_incoming
115
+ if (_event.id === config.hueDevice) {
116
+ if (!_event.hasOwnProperty('state') || _event.state === undefined) return
117
+
118
+ const knxMsgPayload = {}
119
+ knxMsgPayload.topic = config.GAdevice_software_update
120
+ knxMsgPayload.dpt = config.dptdevice_software_update
121
+
122
+ knxMsgPayload.payload = (_event.state !== 'no_update')
123
+ // Send to KNX bus
124
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
125
+ safeSendToKNX({
126
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write'
127
+ }, 'write')
128
+ }
129
+ node.currentDeviceValue = knxMsgPayload.payload
130
+ // Setup the output msg
131
+ knxMsgPayload.name = node.name
132
+ knxMsgPayload.status = _event.state
133
+ knxMsgPayload.event = 'device_software_update'
134
+ // Send payload
135
+ knxMsgPayload.rawEvent = _event
136
+ node.send(knxMsgPayload)
137
+ node.setNodeStatusHue({
138
+ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: knxMsgPayload.payload
139
+ })
140
+ }
141
+ } catch (error) {
142
+ pushStatus({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${formatTs(new Date())})` })
143
+ }
144
+ }
145
+
146
+ node.sendResponseToKNX = (_level) => {
147
+ const knxMsgPayload = {}
148
+ knxMsgPayload.topic = config.GAdevice_software_update
149
+ knxMsgPayload.dpt = config.dptdevice_software_update
150
+
151
+ knxMsgPayload.payload = _level
152
+ // Send to KNX bus
153
+ if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
154
+ safeSendToKNX({
155
+ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'response'
156
+ }, 'response')
157
+ }
158
+ }
159
+
160
+ // On each deploy, unsubscribe+resubscribe
161
+ if (node.serverKNX) {
162
+ node.serverKNX.removeClient(node)
163
+ node.serverKNX.addClient(node)
164
+ }
165
+ if (node.serverHue) {
166
+ node.serverHue.removeClient(node)
167
+ node.serverHue.addClient(node)
168
+ }
169
+
170
+ node.on('input', (msg) => {
171
+
172
+ })
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('knxUltimateHuedevice_software_update', knxUltimateHuedevice_software_update)
185
+ }