node-red-contrib-knx-ultimate 2.1.63 → 2.2.2
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/.eslintrc.json +12 -12
- package/CHANGELOG.md +17 -1
- package/KNXEngine/package.json +1 -1
- package/nodes/hue-config.html +2 -2
- package/nodes/hue-config.js +81 -39
- package/nodes/knxUltimate-config.js +158 -156
- package/nodes/knxUltimateHueBattery.html +2 -2
- package/nodes/knxUltimateHueButton.html +1 -1
- package/nodes/knxUltimateHueButton.js +152 -116
- package/nodes/knxUltimateHueLight.html +830 -781
- package/nodes/knxUltimateHueLight.js +145 -149
- package/nodes/knxUltimateHueLightSensor.html +1 -1
- package/nodes/knxUltimateHueLightSensor.js +2 -2
- package/nodes/knxUltimateHueMotion.html +3 -4
- package/nodes/knxUltimateHueMotion.js +73 -61
- package/nodes/knxUltimateHueScene.html +1 -1
- package/nodes/knxUltimateHueScene.js +1 -1
- package/nodes/knxUltimateHueTapDial.html +1 -1
- package/nodes/knxUltimateHueTapDial.js +1 -2
- package/nodes/knxUltimateHueTemperatureSensor.html +2 -2
- package/nodes/knxUltimateLoadControl.html +1 -1
- package/nodes/knxUltimateViewer.html +28 -0
- package/nodes/utils/hueEngine.js +5 -25
- package/package.json +26 -5
- package/nodes/utils/iro.js +0 -1835
|
@@ -1,188 +1,224 @@
|
|
|
1
1
|
module.exports = function (RED) {
|
|
2
|
-
const dptlib = require(
|
|
2
|
+
const dptlib = require("../KNXEngine/src/dptlib");
|
|
3
3
|
|
|
4
4
|
function knxUltimateHueButton(config) {
|
|
5
|
-
RED.nodes.createNode(this, config)
|
|
6
|
-
const node = this
|
|
7
|
-
node.server = RED.nodes.getNode(config.server)
|
|
8
|
-
node.serverHue = RED.nodes.getNode(config.serverHue)
|
|
9
|
-
node.topic = node.name
|
|
10
|
-
node.name = config.name === undefined ? 'Hue' : config.name
|
|
11
|
-
node.dpt = ''
|
|
12
|
-
node.notifyreadrequest = false
|
|
13
|
-
node.notifyreadrequestalsorespondtobus = 'false'
|
|
14
|
-
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''
|
|
15
|
-
node.notifyresponse = false
|
|
16
|
-
node.notifywrite = true
|
|
17
|
-
node.initialread = true
|
|
18
|
-
node.listenallga = true // Don't remove
|
|
19
|
-
node.outputtype = 'write'
|
|
20
|
-
node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
|
|
21
|
-
node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
|
|
22
|
-
node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg
|
|
23
|
-
node.passthrough = 'no'
|
|
24
|
-
node.formatmultiplyvalue = 1
|
|
25
|
-
node.formatnegativevalue = 'leave'
|
|
26
|
-
node.formatdecimalsvalue = 2
|
|
27
|
-
node.short_releaseValue = false
|
|
28
|
-
node.isTimerDimStopRunning = false
|
|
5
|
+
RED.nodes.createNode(this, config);
|
|
6
|
+
const node = this;
|
|
7
|
+
node.server = RED.nodes.getNode(config.server);
|
|
8
|
+
node.serverHue = RED.nodes.getNode(config.serverHue);
|
|
9
|
+
node.topic = node.name;
|
|
10
|
+
node.name = config.name === undefined ? 'Hue' : config.name;
|
|
11
|
+
node.dpt = '';
|
|
12
|
+
node.notifyreadrequest = false;
|
|
13
|
+
node.notifyreadrequestalsorespondtobus = 'false';
|
|
14
|
+
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
15
|
+
node.notifyresponse = false;
|
|
16
|
+
node.notifywrite = true;
|
|
17
|
+
node.initialread = true;
|
|
18
|
+
node.listenallga = true; // Don't remove
|
|
19
|
+
node.outputtype = 'write';
|
|
20
|
+
node.outputRBE = false; // Apply or not RBE to the output (Messages coming from flow)
|
|
21
|
+
node.inputRBE = false; // Apply or not RBE to the input (Messages coming from BUS)
|
|
22
|
+
node.currentPayload = ''; // Current value for the RBE input and for the .previouspayload msg
|
|
23
|
+
node.passthrough = 'no';
|
|
24
|
+
node.formatmultiplyvalue = 1;
|
|
25
|
+
node.formatnegativevalue = 'leave';
|
|
26
|
+
node.formatdecimalsvalue = 2;
|
|
27
|
+
node.short_releaseValue = false;
|
|
28
|
+
node.isTimerDimStopRunning = false;
|
|
29
29
|
|
|
30
30
|
// Used to call the status update from the config node.
|
|
31
|
-
node.setNodeStatus = ({
|
|
31
|
+
node.setNodeStatus = ({
|
|
32
|
+
fill, shape, text, payload,
|
|
33
|
+
}) => {
|
|
32
34
|
|
|
33
|
-
}
|
|
35
|
+
};
|
|
34
36
|
// Used to call the status update from the HUE config node.
|
|
35
|
-
node.setNodeStatusHue = ({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
node.setNodeStatusHue = ({
|
|
38
|
+
fill, shape, text, payload,
|
|
39
|
+
}) => {
|
|
40
|
+
if (payload === undefined) return;
|
|
41
|
+
const dDate = new Date();
|
|
42
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload.toString();
|
|
43
|
+
node.status({ fill, shape, text: `${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})` });
|
|
44
|
+
};
|
|
41
45
|
|
|
42
46
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
43
|
-
node.handleSend = msg => {
|
|
44
|
-
const state = {}
|
|
47
|
+
node.handleSend = (msg) => {
|
|
48
|
+
const state = {};
|
|
45
49
|
try {
|
|
46
50
|
switch (msg.knx.destination) {
|
|
47
51
|
case config.GAshort_releaseStatus:
|
|
48
|
-
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptshort_release))
|
|
49
|
-
node.short_releaseValue = msg.payload
|
|
50
|
-
node.setNodeStatusHue({
|
|
51
|
-
|
|
52
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptshort_release));
|
|
53
|
+
node.short_releaseValue = msg.payload;
|
|
54
|
+
node.setNodeStatusHue({
|
|
55
|
+
fill: 'green', shape: 'dot', text: 'KNX->HUE Short Release Status', payload: msg.payload,
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
52
58
|
case config.GArepeatStatus:
|
|
53
|
-
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptrepeat))
|
|
54
|
-
node.toggleGArepeat = msg.payload.decr_incr === 1
|
|
55
|
-
node.setNodeStatusHue({
|
|
56
|
-
|
|
59
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptrepeat));
|
|
60
|
+
node.toggleGArepeat = msg.payload.decr_incr === 1;
|
|
61
|
+
node.setNodeStatusHue({
|
|
62
|
+
fill: 'green', shape: 'dot', text: 'KNX->HUE Repeat Status', payload: msg.payload,
|
|
63
|
+
});
|
|
64
|
+
break;
|
|
57
65
|
default:
|
|
58
|
-
break
|
|
66
|
+
break;
|
|
59
67
|
}
|
|
60
68
|
} catch (error) {
|
|
61
|
-
node.setNodeStatusHue({
|
|
69
|
+
node.setNodeStatusHue({
|
|
70
|
+
fill: 'red', shape: 'dot', text: `KNX->HUE error ${error.message}`, payload: '',
|
|
71
|
+
});
|
|
62
72
|
}
|
|
63
|
-
}
|
|
73
|
+
};
|
|
64
74
|
|
|
65
|
-
node.handleSendHUE = _event => {
|
|
75
|
+
node.handleSendHUE = (_event) => {
|
|
66
76
|
try {
|
|
67
77
|
if (_event.id === config.hueDevice) {
|
|
68
|
-
const knxMsgPayload = {}
|
|
69
|
-
let flowMsgPayload = true
|
|
78
|
+
const knxMsgPayload = {};
|
|
79
|
+
let flowMsgPayload = true;
|
|
70
80
|
// Handling events with toggles
|
|
71
81
|
// KNX Dimming reminder tips
|
|
72
82
|
// { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
|
|
73
83
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
74
84
|
switch (_event.button.last_event) {
|
|
75
85
|
case 'initial_press':
|
|
76
|
-
if (node.initial_pressValue === undefined) node.initial_pressValue = false
|
|
77
|
-
node.initial_pressValue = config.toggleValues ? !node.initial_pressValue : true
|
|
78
|
-
flowMsgPayload = node.initial_pressValue
|
|
79
|
-
break
|
|
86
|
+
if (node.initial_pressValue === undefined) node.initial_pressValue = false;
|
|
87
|
+
node.initial_pressValue = config.toggleValues ? !node.initial_pressValue : true;
|
|
88
|
+
flowMsgPayload = node.initial_pressValue;
|
|
89
|
+
break;
|
|
80
90
|
case 'long_release':
|
|
81
|
-
flowMsgPayload = node.long_pressValue
|
|
91
|
+
flowMsgPayload = node.long_pressValue;
|
|
82
92
|
// if the dimmer was running, send the STOP telegram to the KNX bus wires, using the GArepeat Group address and dpt.
|
|
83
93
|
if (node.isTimerDimStopRunning) {
|
|
84
|
-
knxMsgPayload.topic = config.GArepeat
|
|
85
|
-
knxMsgPayload.dpt = config.dptrepeat
|
|
86
|
-
node.stopDIM(knxMsgPayload)
|
|
94
|
+
knxMsgPayload.topic = config.GArepeat;
|
|
95
|
+
knxMsgPayload.dpt = config.dptrepeat;
|
|
96
|
+
node.stopDIM(knxMsgPayload);
|
|
87
97
|
}
|
|
88
|
-
break
|
|
98
|
+
break;
|
|
89
99
|
case 'double_short_release':
|
|
90
|
-
if (node.double_short_releaseValue === undefined) node.double_short_releaseValue = false
|
|
91
|
-
node.double_short_releaseValue = config.toggleValues ? !node.double_short_releaseValue : true
|
|
92
|
-
flowMsgPayload = node.double_short_releaseValue
|
|
93
|
-
break
|
|
100
|
+
if (node.double_short_releaseValue === undefined) node.double_short_releaseValue = false;
|
|
101
|
+
node.double_short_releaseValue = config.toggleValues ? !node.double_short_releaseValue : true;
|
|
102
|
+
flowMsgPayload = node.double_short_releaseValue;
|
|
103
|
+
break;
|
|
94
104
|
case 'long_press':
|
|
95
|
-
if (node.long_pressValue === undefined) node.long_pressValue = false
|
|
96
|
-
node.long_pressValue = config.toggleValues ? !node.long_pressValue : true
|
|
97
|
-
flowMsgPayload = node.long_pressValue
|
|
98
|
-
break
|
|
105
|
+
if (node.long_pressValue === undefined) node.long_pressValue = false;
|
|
106
|
+
node.long_pressValue = config.toggleValues ? !node.long_pressValue : true;
|
|
107
|
+
flowMsgPayload = node.long_pressValue;
|
|
108
|
+
break;
|
|
99
109
|
case 'short_release':
|
|
100
|
-
node.short_releaseValue = config.toggleValues ? !node.short_releaseValue : true
|
|
101
|
-
flowMsgPayload = node.short_releaseValue
|
|
110
|
+
node.short_releaseValue = config.toggleValues ? !node.short_releaseValue : true;
|
|
111
|
+
flowMsgPayload = node.short_releaseValue;
|
|
102
112
|
if (config.GAshort_release !== undefined && config.GAshort_release !== '') {
|
|
103
|
-
knxMsgPayload.topic = config.GAshort_release
|
|
104
|
-
knxMsgPayload.dpt = config.dptshort_release
|
|
105
|
-
knxMsgPayload.payload = node.short_releaseValue
|
|
113
|
+
knxMsgPayload.topic = config.GAshort_release;
|
|
114
|
+
knxMsgPayload.dpt = config.dptshort_release;
|
|
115
|
+
knxMsgPayload.payload = node.short_releaseValue;
|
|
106
116
|
// Send to KNX bus
|
|
107
|
-
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined)
|
|
108
|
-
|
|
117
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
118
|
+
node.server.writeQueueAdd({
|
|
119
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
123
|
+
node.setNodeStatusHue({
|
|
124
|
+
fill: 'blue', shape: 'dot', text: `HUE->KNX ${_event.button.last_event}`, payload: knxMsgPayload.payload,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
109
127
|
}
|
|
110
|
-
break
|
|
128
|
+
break;
|
|
111
129
|
case 'repeat':
|
|
112
|
-
flowMsgPayload = true
|
|
130
|
+
flowMsgPayload = true;
|
|
113
131
|
if (config.GArepeat !== undefined && config.GArepeat !== '') {
|
|
114
132
|
if (node.isTimerDimStopRunning === false) {
|
|
115
133
|
// Set KNX Dim up/down start
|
|
116
|
-
knxMsgPayload.topic = config.GArepeat
|
|
117
|
-
knxMsgPayload.dpt = config.dptrepeat
|
|
118
|
-
knxMsgPayload.payload = node.long_pressValue ? { decr_incr: 0, data: 3 } : { decr_incr: 1, data: 3 } // If the light is turned on, the initial DIM direction must be down, otherwise, up
|
|
134
|
+
knxMsgPayload.topic = config.GArepeat;
|
|
135
|
+
knxMsgPayload.dpt = config.dptrepeat;
|
|
136
|
+
knxMsgPayload.payload = node.long_pressValue ? { decr_incr: 0, data: 3 } : { decr_incr: 1, data: 3 }; // If the light is turned on, the initial DIM direction must be down, otherwise, up
|
|
119
137
|
// Send to KNX bus
|
|
120
|
-
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined)
|
|
121
|
-
|
|
138
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
139
|
+
node.server.writeQueueAdd({
|
|
140
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
144
|
+
node.setNodeStatusHue({
|
|
145
|
+
fill: 'blue', shape: 'dot', text: 'HUE->KNX START DIM', payload: '',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
122
148
|
}
|
|
123
|
-
node.startDimStopper(knxMsgPayload)
|
|
149
|
+
node.startDimStopper(knxMsgPayload);
|
|
124
150
|
}
|
|
125
|
-
break
|
|
151
|
+
break;
|
|
126
152
|
default:
|
|
127
|
-
break
|
|
153
|
+
break;
|
|
128
154
|
}
|
|
129
155
|
|
|
130
156
|
// Setup the output msg
|
|
131
|
-
const flowMsg = {}
|
|
132
|
-
flowMsg.name = node.name
|
|
133
|
-
flowMsg.event = _event.button.last_event
|
|
134
|
-
flowMsg.rawEvent = _event
|
|
135
|
-
flowMsg.payload = flowMsgPayload
|
|
136
|
-
node.send(flowMsg)
|
|
137
|
-
//node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: flowMsg.rawEvent + ' ' + flowMsg.payload })
|
|
157
|
+
const flowMsg = {};
|
|
158
|
+
flowMsg.name = node.name;
|
|
159
|
+
flowMsg.event = _event.button.last_event;
|
|
160
|
+
flowMsg.rawEvent = _event;
|
|
161
|
+
flowMsg.payload = flowMsgPayload;
|
|
162
|
+
node.send(flowMsg);
|
|
163
|
+
// node.setNodeStatusHue({ fill: 'blue', shape: 'ring', text: 'HUE->KNX', payload: flowMsg.rawEvent + ' ' + flowMsg.payload })
|
|
138
164
|
}
|
|
139
165
|
} catch (error) {
|
|
140
|
-
node.setNodeStatusHue({
|
|
166
|
+
node.setNodeStatusHue({
|
|
167
|
+
fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message}`, payload: '',
|
|
168
|
+
});
|
|
141
169
|
}
|
|
142
|
-
}
|
|
170
|
+
};
|
|
143
171
|
|
|
144
172
|
// Timer to stop the dimming sequence
|
|
145
173
|
node.startDimStopper = function (knxMsgPayload) {
|
|
146
|
-
if (node.timerDimStop !== undefined) clearTimeout(node.timerDimStop)
|
|
147
|
-
node.isTimerDimStopRunning = true
|
|
174
|
+
if (node.timerDimStop !== undefined) clearTimeout(node.timerDimStop);
|
|
175
|
+
node.isTimerDimStopRunning = true;
|
|
148
176
|
node.timerDimStop = setTimeout(() => {
|
|
149
|
-
node.stopDIM(knxMsgPayload)
|
|
150
|
-
}, 2000)
|
|
151
|
-
}
|
|
177
|
+
node.stopDIM(knxMsgPayload);
|
|
178
|
+
}, 2000);
|
|
179
|
+
};
|
|
152
180
|
|
|
153
181
|
node.stopDIM = function (knxMsgPayload) {
|
|
154
182
|
// KNX Stop DIM
|
|
155
|
-
if (node.timerDimStop !== undefined) clearTimeout(node.timerDimStop)
|
|
156
|
-
node.isTimerDimStopRunning = false
|
|
157
|
-
knxMsgPayload.payload = { decr_incr: 0, data: 0 } // Payload for the output msg
|
|
183
|
+
if (node.timerDimStop !== undefined) clearTimeout(node.timerDimStop);
|
|
184
|
+
node.isTimerDimStopRunning = false;
|
|
185
|
+
knxMsgPayload.payload = { decr_incr: 0, data: 0 }; // Payload for the output msg
|
|
158
186
|
// Send to KNX bus
|
|
159
|
-
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined)
|
|
160
|
-
|
|
161
|
-
|
|
187
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
188
|
+
node.server.writeQueueAdd({
|
|
189
|
+
grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) {
|
|
193
|
+
node.setNodeStatusHue({
|
|
194
|
+
fill: 'grey', shape: 'ring', text: 'HUE->KNX STOP DIM', payload: knxMsgPayload.payload,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
};
|
|
162
198
|
|
|
163
199
|
// On each deploy, unsubscribe+resubscribe
|
|
164
200
|
if (node.server) {
|
|
165
|
-
node.server.removeClient(node)
|
|
166
|
-
node.server.addClient(node)
|
|
201
|
+
node.server.removeClient(node);
|
|
202
|
+
node.server.addClient(node);
|
|
167
203
|
}
|
|
168
204
|
if (node.serverHue) {
|
|
169
|
-
node.serverHue.removeClient(node)
|
|
170
|
-
node.serverHue.addClient(node)
|
|
205
|
+
node.serverHue.removeClient(node);
|
|
206
|
+
node.serverHue.addClient(node);
|
|
171
207
|
}
|
|
172
208
|
|
|
173
|
-
node.on('input',
|
|
209
|
+
node.on('input', (msg) => {
|
|
174
210
|
|
|
175
|
-
})
|
|
211
|
+
});
|
|
176
212
|
|
|
177
|
-
node.on('close',
|
|
213
|
+
node.on('close', (done) => {
|
|
178
214
|
if (node.server) {
|
|
179
|
-
node.server.removeClient(node)
|
|
215
|
+
node.server.removeClient(node);
|
|
180
216
|
}
|
|
181
217
|
if (node.serverHue) {
|
|
182
|
-
node.serverHue.removeClient(node)
|
|
218
|
+
node.serverHue.removeClient(node);
|
|
183
219
|
}
|
|
184
|
-
done()
|
|
185
|
-
})
|
|
220
|
+
done();
|
|
221
|
+
});
|
|
186
222
|
}
|
|
187
|
-
RED.nodes.registerType('knxUltimateHueButton', knxUltimateHueButton)
|
|
188
|
-
}
|
|
223
|
+
RED.nodes.registerType('knxUltimateHueButton', knxUltimateHueButton);
|
|
224
|
+
};
|