node-red-contrib-knx-ultimate 2.4.6 → 2.4.8
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,10 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.4.7** - March 2024<br/>
|
|
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
|
+
- Fixed [this](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/338).<br/>
|
|
12
|
+
|
|
9
13
|
**Version 2.4.6** - Feb 2024<br/>
|
|
10
14
|
- WARNING: this version uses the Node-Red plugin system; the Node-Red version must be **equals or major than 3.1.1**<br/>
|
|
11
15
|
- Changed the KNX Engine keep alive CONNECTIONSTATUS_REQUEST interval from 60 to 30 secs, to allow some KNX/IP interfaces (not strictly following the KNX standard) to work properly.<br/>
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('knxUltimateGarageDoorBarrierOpener', {
|
|
3
|
+
category: "KNX Ultimate",
|
|
4
|
+
color: '#C7E9C0',
|
|
5
|
+
defaults: {
|
|
6
|
+
//buttonState: {value: true},
|
|
7
|
+
server: { type: "knxUltimate-config", required: true },
|
|
8
|
+
name: { value: "" },
|
|
9
|
+
topic: { value: "" },
|
|
10
|
+
GACommand: { value: "" },
|
|
11
|
+
GAStatus: { value: "" },
|
|
12
|
+
GAMoving: { value: "" },
|
|
13
|
+
GAImpulse: { value: "" },
|
|
14
|
+
GABlocking: { value: "" },
|
|
15
|
+
GAObstructionDetected: { value: "" },
|
|
16
|
+
valueStatusWhenOpen: { value: true },
|
|
17
|
+
valueCommandToOpen: { value: true },
|
|
18
|
+
blockingTimeout: { value: 0 },
|
|
19
|
+
onCommandReceivedWhileMoving: { value: "ignore" },
|
|
20
|
+
timeToFullyOpen: { value: 20, required: true, validate: RED.validators.number() },
|
|
21
|
+
timeToFullyClosed: { value: 20, required: true, validate: RED.validators.number() }
|
|
22
|
+
},
|
|
23
|
+
inputs: 0,
|
|
24
|
+
outputs: 1,
|
|
25
|
+
icon: "node-knx-icon.svg",
|
|
26
|
+
label: function () {
|
|
27
|
+
return this.name || "Garage/Door/Barrier opener"
|
|
28
|
+
},
|
|
29
|
+
paletteLabel: "KNX Garage opener",
|
|
30
|
+
// button: {
|
|
31
|
+
// enabled: function() {
|
|
32
|
+
// // return whether or not the button is enabled, based on the current
|
|
33
|
+
// // configuration of the node
|
|
34
|
+
// return !this.changed
|
|
35
|
+
// },
|
|
36
|
+
// visible: function() {
|
|
37
|
+
// // return whether or not the button is visible, based on the current
|
|
38
|
+
// // configuration of the node
|
|
39
|
+
// return this.hasButton
|
|
40
|
+
// },
|
|
41
|
+
// //toggle: "buttonState",
|
|
42
|
+
// onclick: function() {}
|
|
43
|
+
// },
|
|
44
|
+
oneditprepare: function () {
|
|
45
|
+
var node = this;
|
|
46
|
+
var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
function getGroupAddressSimple(_sourceWidgetAutocomplete, _additionalSearchTerm) {
|
|
50
|
+
$(_sourceWidgetAutocomplete).autocomplete({
|
|
51
|
+
minLength: 1,
|
|
52
|
+
source: function (request, response) {
|
|
53
|
+
//$.getJSON("csv", request, function( data, status, xhr ) {
|
|
54
|
+
$.getJSON("knxUltimatecsv?nodeID=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => {
|
|
55
|
+
response(
|
|
56
|
+
$.map(data, function (value, key) {
|
|
57
|
+
var sSearch = value.ga + " (" + value.devicename + ") DPT" + value.dpt;
|
|
58
|
+
for (let index = 0; index < _additionalSearchTerm.length; index++) {
|
|
59
|
+
const sDPT = _additionalSearchTerm[index];
|
|
60
|
+
if (fullSearch(sSearch, request.term + " " + sDPT)) {
|
|
61
|
+
return {
|
|
62
|
+
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
63
|
+
value: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
select: function (event, ui) {
|
|
72
|
+
// Sets Datapoint and device name automatically
|
|
73
|
+
const sDevName = ui.item.value.trim();
|
|
74
|
+
$(_sourceWidgetAutocomplete).val(sDevName);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 31/03/2020 Search Helper
|
|
80
|
+
function fullSearch(sourceText, searchString) {
|
|
81
|
+
// This searches for all words in a string
|
|
82
|
+
var aSearchWords = searchString.toLowerCase().split(" ");
|
|
83
|
+
var i = 0;
|
|
84
|
+
for (let index = 0; index < aSearchWords.length; index++) {
|
|
85
|
+
if (sourceText.toLowerCase().indexOf(aSearchWords[index]) > -1) i += 1;
|
|
86
|
+
}
|
|
87
|
+
return i == aSearchWords.length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (oNodeServer === undefined) {
|
|
91
|
+
// Hide the UI
|
|
92
|
+
$("#divMain").hide();
|
|
93
|
+
} else {
|
|
94
|
+
$("#divMain").show();
|
|
95
|
+
$("#tabs").tabs();
|
|
96
|
+
getGroupAddressSimple("#node-input-GACommand", ["1."]);
|
|
97
|
+
getGroupAddressSimple("#node-input-GAStatus", ["1."]);
|
|
98
|
+
getGroupAddressSimple("#node-input-GAMoving", ["1."]);
|
|
99
|
+
getGroupAddressSimple("#node-input-GAImpulse", ["1."]);
|
|
100
|
+
getGroupAddressSimple("#node-input-GAObstructionDetected", ["1."]);
|
|
101
|
+
getGroupAddressSimple("#node-input-GABlocking", ["1."]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
},
|
|
109
|
+
oneditsave: function () {
|
|
110
|
+
|
|
111
|
+
},
|
|
112
|
+
oneditcancel: function () {
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<script type="text/html" data-template-name="knxUltimateGarageDoorBarrierOpener">
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
<div class="form-row">
|
|
123
|
+
<b>KNX Garage/Door/Barrier opener</b>
|
|
124
|
+
<br />
|
|
125
|
+
<br />
|
|
126
|
+
|
|
127
|
+
<label for="node-input-server">
|
|
128
|
+
<i class="fa fa-circle-o"></i> Gateway
|
|
129
|
+
</label>
|
|
130
|
+
<input type="text" id="node-input-server" />
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div id="divMain">
|
|
134
|
+
<div class="form-row">
|
|
135
|
+
<label for="node-input-name">
|
|
136
|
+
<i class="fa fa-tag"></i> Name
|
|
137
|
+
</label>
|
|
138
|
+
<input type="text" id="node-input-name" />
|
|
139
|
+
</div>
|
|
140
|
+
<div class="form-row">
|
|
141
|
+
<label for="node-input-topic">
|
|
142
|
+
<i class="fa fa-tasks"></i> Topic
|
|
143
|
+
</label>
|
|
144
|
+
<input type="text" id="node-input-topic" />
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div id="tabs">
|
|
148
|
+
<ul>
|
|
149
|
+
<li><a href="#tabs-1"><i class="fa-solid fa-toggle-on"></i> Group Address Input</a></li>
|
|
150
|
+
<li><a href="#tabs-2"><i class="fa-solid fa-long-arrow-right"></i> Group Address Output</a></li>
|
|
151
|
+
<li><a href="#tabs-3"><i class="fa-solid fa-code-merge"></i> Timing/Behaviours</a></li>
|
|
152
|
+
</ul>
|
|
153
|
+
<div id="tabs-1">
|
|
154
|
+
<p>
|
|
155
|
+
<div class="form-row">
|
|
156
|
+
<label for="node-input-GAImpulse">
|
|
157
|
+
<i class="fa fa-question-circle"></i> Impulse
|
|
158
|
+
</label>
|
|
159
|
+
<input type="text" id="node-input-GAImpulse" placeholder="Type name or group address" />
|
|
160
|
+
</div>
|
|
161
|
+
<div class="form-row">
|
|
162
|
+
<label for="node-input-GACommand">
|
|
163
|
+
<i class="fa fa-play-circle-o"></i> Command
|
|
164
|
+
</label>
|
|
165
|
+
<input type="text" id="node-input-GACommand" placeholder="Type name or group address" />
|
|
166
|
+
</div>
|
|
167
|
+
<div class="form-row" id="node-input-valueCommandToOpen">
|
|
168
|
+
<label style="width:85%" for="node-input-valueCommandToOpen">
|
|
169
|
+
<i class="fa fa-sign-in"></i> Behaviour
|
|
170
|
+
</label>
|
|
171
|
+
<select id="node-input-valueCommandToOpen">
|
|
172
|
+
<option value=true>True = open / False = close</option>
|
|
173
|
+
<option value=false>True = close / False = open</option>
|
|
174
|
+
</select>
|
|
175
|
+
|
|
176
|
+
</div>
|
|
177
|
+
<div class="form-row">
|
|
178
|
+
<label for="node-input-GAStatus">
|
|
179
|
+
<i class="fa fa-question-circle"></i> Status
|
|
180
|
+
</label>
|
|
181
|
+
<input type="text" id="node-input-GAStatus" placeholder="Type name or group address" />
|
|
182
|
+
</div>
|
|
183
|
+
<div class="form-row" id="node-input-valueStatusWhenOpen">
|
|
184
|
+
<label style="width:85%" for="node-input-valueStatusWhenOpen">
|
|
185
|
+
<i class="fa fa-sign-in"></i> Behaviour
|
|
186
|
+
</label>
|
|
187
|
+
<select id="node-input-valueStatusWhenOpen">
|
|
188
|
+
<option value=true>True = open / False = closed</option>
|
|
189
|
+
<option value=false>True = closed / False = open</option>
|
|
190
|
+
</select>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="form-row">
|
|
193
|
+
<label for="node-input-GABlocking">
|
|
194
|
+
<i class="fa fa-play-circle-o"></i> Blocking
|
|
195
|
+
</label>
|
|
196
|
+
<input type="text" id="node-input-GABlocking" placeholder="Type name or group address" />
|
|
197
|
+
</div>
|
|
198
|
+
<div class="form-row">
|
|
199
|
+
<label for="node-input-blockingTimeout">
|
|
200
|
+
<i class="fa fa-play-circle-o"></i> Blocking timeout
|
|
201
|
+
</label>
|
|
202
|
+
<input type="text" id="node-input-blockingTimeout" placeholder="In seconds" />
|
|
203
|
+
</div>
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
<div id="tabs-2">
|
|
207
|
+
<p>
|
|
208
|
+
<div class="form-row">
|
|
209
|
+
<label for="node-input-GAObstructionDetected">
|
|
210
|
+
<i class="fa fa-question-circle"></i> Obstruction
|
|
211
|
+
</label>
|
|
212
|
+
<input type="text" id="node-input-GAObstructionDetected" placeholder="Type name or group address" />
|
|
213
|
+
</div>
|
|
214
|
+
<div class="form-row">
|
|
215
|
+
<label for="node-input-GAMoving">
|
|
216
|
+
<i class="fa fa-question-circle"></i> Moving
|
|
217
|
+
</label>
|
|
218
|
+
<input type="text" id="node-input-GAMoving" placeholder="Type name or group address" />
|
|
219
|
+
</div>
|
|
220
|
+
</p>
|
|
221
|
+
</div>
|
|
222
|
+
<div id="tabs-3">
|
|
223
|
+
<p>
|
|
224
|
+
<div class="form-row">
|
|
225
|
+
<label style="width:150px;" for="node-input-onCommandReceivedWhileMoving">
|
|
226
|
+
<i class="fa-solid fa-down-left-and-up-right-to-center"></i> While moving
|
|
227
|
+
</label>
|
|
228
|
+
<select id="node-input-onCommandReceivedWhileMoving">
|
|
229
|
+
<option value="ignore">Ignore commands received</option>
|
|
230
|
+
<option value="obey">Stop moving then obey to new commad</option>
|
|
231
|
+
</select>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="form-row">
|
|
234
|
+
<label style="width:150px;" for="node-input-timeToFullyOpen">
|
|
235
|
+
<i class="fa-regular fa-door-open"></i> Fully open time (s)
|
|
236
|
+
</label>
|
|
237
|
+
<input type="text" id="node-input-timeToFullyOpen" placeholder="In seconds" />
|
|
238
|
+
</div>
|
|
239
|
+
<div class="form-row">
|
|
240
|
+
<label style="width:150px;" for="node-input-timeToFullyClosed">
|
|
241
|
+
<i class="fa-regular fa-door-closed"></i> Fully closed time (s)
|
|
242
|
+
</label>
|
|
243
|
+
<input type="text" id="node-input-timeToFullyClosed" placeholder="In seconds" />
|
|
244
|
+
</div>
|
|
245
|
+
</p>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
</script>
|
|
256
|
+
<script type="text/markdown" data-help-name="knxUltimateGarageDoorBarrierOpener">
|
|
257
|
+
<p>This node lets you control a garage/door/barrier, controlled with an impulse command (for example: push to open, push the same button to close, push the same button to stop the movement).
|
|
258
|
+
|
|
259
|
+
**General**
|
|
260
|
+
|Property|Description|
|
|
261
|
+
|--|--|
|
|
262
|
+
| Gateway | Select the KNX gateway to be used |
|
|
263
|
+
| Name | Self explaining. The node name. |
|
|
264
|
+
| Topic | The msg output topic. |
|
|
265
|
+
|
|
266
|
+
**Group Address Input**
|
|
267
|
+
These GA are transmitted by your devices to the node.
|
|
268
|
+
|Property|Description|
|
|
269
|
+
|--|--|
|
|
270
|
+
| Impulse | The KNX Group Address where you connected the impulsive command (for example: push to open, push the same button to close, push the same button to stop the movement) |
|
|
271
|
+
| Command | The KNX Group Address to command the movement. Pass **true** to open, **false** to close the door.|
|
|
272
|
+
| Invert command value | Invert the command boolean values, that becomes **true** to close, **false** to open the door.|
|
|
273
|
+
| Status | The KNX Group Address of the door status. It's usually the magnetic sensor mounted on the door to detect open/closed position. Must be **true** if the door is open, otherwise **flase**. It's used by the node to make coherent movements. **If leaved blank**, the status is computed using full open/full closed intervals specified in the **Timing/Behaviours** page. |
|
|
274
|
+
| Invert status value | Invert the status boolean values, that becomes **true** if the door is closed, otherwise **flase**. |
|
|
275
|
+
| Blocking | The KNX Group Address for blocking the movement. Pass **true** to open the door and block it in open status, **false** to close the door and block it in closed status.|
|
|
276
|
+
| Blocking timeout| Timeout, in seconds, after the blocking is restored; the door will not move and will wait further commands. Set 0 to never restoring from blocked status. Default is 0.|
|
|
277
|
+
|
|
278
|
+
**Group Address Output**
|
|
279
|
+
These GA are computed ad transmitted to the KNX bus by the node itself.
|
|
280
|
+
|Property|Description|
|
|
281
|
+
|--|--|
|
|
282
|
+
| Obstruction | The KNX Group Address of the obstruction detection. This GA is wrote by the node, whenever it detects an obstruction during movement. It's **true** if an obstruction is detected, otherwise **flase**.|
|
|
283
|
+
| Moving | The KNX Group Address of the moving status. This GA is wrote by the node, whenever the garage/door/barrier is moving. It's **true** while moving, otherwise **false**.|
|
|
284
|
+
|
|
285
|
+
**Timing/Behaviours**
|
|
286
|
+
|Property|Description|
|
|
287
|
+
|--|--|
|
|
288
|
+
| While moving | Define the behaviour while the door is moving to reach it's end position (fully open or fully closed). For example, the door can stop and reverse the direction, or you can choose to ignore sent commands until the door reaches it's end position. |
|
|
289
|
+
| Fully open time | The time (in seconds) of a full movement from fully closed position to fully open position. |
|
|
290
|
+
| Fully closed time | The time (in seconds) of a full movement from fully open position to fully closed position. |
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
<br/>
|
|
294
|
+
|
|
295
|
+
### Outputs
|
|
296
|
+
|
|
297
|
+
1. Standard output
|
|
298
|
+
: payload (string|number|object) : Pin 1 is the standard output of the command.
|
|
299
|
+
|
|
300
|
+
2. Errors
|
|
301
|
+
: error (object) : Pin 2 Contains the detailed error message.
|
|
302
|
+
|
|
303
|
+
### Details
|
|
304
|
+
|
|
305
|
+
`msg.payload` is used as the payload of the published message, in JSON ogbect format.
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
msg = {
|
|
309
|
+
topic: the topic you selected
|
|
310
|
+
payload: {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
````
|
|
314
|
+
|
|
315
|
+
<br/><a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome" target="_blank"><i class="fa fa-code"></i> Samples</a>
|
|
316
|
+
|
|
317
|
+
[Find it useful?](https://www.paypal.me/techtoday)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
</script>
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
const dptlib = require("../KNXEngine/src/dptlib");
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const KNXUtils = require('../KNXEngine/src/protocol/KNXUtils');
|
|
5
|
+
const payloadRounder = require('./utils/payloadManipulation');
|
|
6
|
+
|
|
7
|
+
module.exports = function (RED) {
|
|
8
|
+
|
|
9
|
+
function knxUltimateGarageDoorBarrierOpener(config) {
|
|
10
|
+
RED.nodes.createNode(this, config);
|
|
11
|
+
const node = this;
|
|
12
|
+
node.server = RED.nodes.getNode(config.server);
|
|
13
|
+
// 11/11/2021 Is the node server disabled by the flow "disable" command?
|
|
14
|
+
if (node.server === null) {
|
|
15
|
+
node.status({ fill: 'red', shape: 'dot', text: '[THE GATEWAY NODE HAS BEEN DISABLED]' });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
node.topic = config.topic;
|
|
20
|
+
node.dpt = '1.001';
|
|
21
|
+
node.notifyreadrequest = false;
|
|
22
|
+
node.notifyreadrequestalsorespondtobus = 'false';
|
|
23
|
+
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '';
|
|
24
|
+
node.notifyresponse = false;
|
|
25
|
+
node.notifywrite = true;
|
|
26
|
+
node.initialread = false;
|
|
27
|
+
node.listenallga = true; // Don't remove
|
|
28
|
+
node.outputtype = 'write';
|
|
29
|
+
node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.server.loglevel || 'error' }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
30
|
+
|
|
31
|
+
// From KNX to the node
|
|
32
|
+
node.GACommand = config.GACommand === undefined ? "" : config.GACommand;
|
|
33
|
+
node.GAImpulse = config.GAImpulse === undefined ? "" : config.GAImpulse;
|
|
34
|
+
node.GABlocking = config.GABlocking === undefined ? "" : config.GABlocking;
|
|
35
|
+
|
|
36
|
+
// From the node to KNX
|
|
37
|
+
node.GAStatus = config.GAStatus === undefined ? "" : config.GAStatus;
|
|
38
|
+
node.GAMoving = config.GAMoving === undefined ? "" : config.GAMoving;
|
|
39
|
+
node.GAObstructionDetected = config.GAObstructionDetected === undefined ? "" : config.GAObstructionDetected;
|
|
40
|
+
|
|
41
|
+
node.valueStatusWhenOpen = config.valueStatusWhenOpen;
|
|
42
|
+
node.valueCommandToOpen = config.valueCommandToOpen;
|
|
43
|
+
node.onCommandReceivedWhileMoving = config.onCommandReceivedWhileMoving;
|
|
44
|
+
node.timeToFullyOpen = config.timeToFullyOpen === undefined ? 20 : config.timeToFullyOpen;
|
|
45
|
+
node.timeToFullyClosed = config.timeToFullyClosed === undefined ? 20 : config.timeToFullyClosed;
|
|
46
|
+
node.blockingTimeout = config.blockingTimeout === undefined ? 0 : config.blockingTimeout;
|
|
47
|
+
node.statusClosed = !node.valueStatusWhenOpen; // Door status (closed/open)
|
|
48
|
+
node.timerMovement = null;
|
|
49
|
+
|
|
50
|
+
// Validate the Address
|
|
51
|
+
try {
|
|
52
|
+
KNXUtils.validateKNXAddress(node.GACommand, true);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
node.setNodeStatus({
|
|
55
|
+
fill: 'red', shape: 'dot', text: error.message, payload: '', GA: node.GACommand, dpt: '', devicename: '',
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
KNXUtils.validateKNXAddress(node.GAImpulse, true);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
node.setNodeStatus({
|
|
63
|
+
fill: 'red', shape: 'dot', text: error.message, payload: '', GA: node.GAImpulse, dpt: '', devicename: '',
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Used to call the status update from the config node.
|
|
69
|
+
node.setNodeStatus = ({
|
|
70
|
+
fill, shape, text, payload, GA, dpt, devicename,
|
|
71
|
+
}) => {
|
|
72
|
+
try {
|
|
73
|
+
if (node.server == null) { node.status({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return; }
|
|
74
|
+
const dDate = new Date();
|
|
75
|
+
// 30/08/2019 Display only the things selected in the config
|
|
76
|
+
GA = (typeof GA === 'undefined' || GA === '') ? '' : `(${GA}) `;
|
|
77
|
+
devicename = devicename || '';
|
|
78
|
+
dpt = (typeof dpt === 'undefined' || dpt === '') ? '' : ` DPT${dpt}`;
|
|
79
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
|
|
80
|
+
node.status({ fill, shape, text: `${GA + payload + (node.listenallga === true ? ` ${devicename}` : '')} (${dDate.getDate()}, ${dDate.toLocaleTimeString()} ${text}` });
|
|
81
|
+
} catch (error) { /* empty */ }
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
85
|
+
node.handleSend = (msg) => {
|
|
86
|
+
try {
|
|
87
|
+
switch (msg.knx.destination) {
|
|
88
|
+
case config.GACommand:
|
|
89
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve('1.001'));
|
|
90
|
+
if (msg.payload) {
|
|
91
|
+
let status = '';
|
|
92
|
+
if (msg.payload !== node.statusClosed) {
|
|
93
|
+
status = 'Moving to open position';
|
|
94
|
+
} else {
|
|
95
|
+
status = 'Moving to closed position';
|
|
96
|
+
}
|
|
97
|
+
node.setNodeStatus({
|
|
98
|
+
fill: 'yellow', shape: 'dot', text: status, payload: '',
|
|
99
|
+
});
|
|
100
|
+
node.server.writeQueueAdd({
|
|
101
|
+
grpaddr: node.GAImpulse, payload: true, dpt: '1.001', outputtype: 'write', nodecallerid: node.id
|
|
102
|
+
});
|
|
103
|
+
if (node.timerMovement !== null) clearTimeout(node.timerMovement);
|
|
104
|
+
node.timerMovement = setTimeout(() => {
|
|
105
|
+
|
|
106
|
+
}, node.timeToFullyOpen);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
break;
|
|
110
|
+
case config.GArepeatStatus:
|
|
111
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptrepeat));
|
|
112
|
+
node.toggleGArepeat = msg.payload.decr_incr === 1;
|
|
113
|
+
node.setNodeStatus({
|
|
114
|
+
fill: 'green', shape: 'dot', text: 'KNX->HUE Repeat Status', payload: msg.payload,
|
|
115
|
+
});
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
node.setNodeStatus({
|
|
122
|
+
fill: 'red', shape: 'dot', text: `KNX->HUE error ${error.message}`, payload: '',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// node.send(msg);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
node.on('input', (msg) => {
|
|
129
|
+
if (typeof msg === 'undefined') return;
|
|
130
|
+
if (!node.server) return; // 29/08/2019 Server not instantiate
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
node.on('close', (done) => {
|
|
134
|
+
if (node.server) {
|
|
135
|
+
node.server.removeClient(node);
|
|
136
|
+
try {
|
|
137
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info(`knxUltimateGarageDoorBarrierOpener: Close: node id ${node.id} with topic ${node.topic || ''} has been removed from the server.`);
|
|
138
|
+
} catch (error) { }
|
|
139
|
+
}
|
|
140
|
+
done();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
RED.nodes.registerType('knxUltimateGarageDoorBarrierOpener', knxUltimateGarageDoorBarrierOpener);
|
|
144
|
+
};
|
|
@@ -211,6 +211,7 @@ module.exports = function (RED) {
|
|
|
211
211
|
node.currentHUEDevice.dimming.brightness = Math.round(dbright, 0);
|
|
212
212
|
node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
213
213
|
state = dbright > 0 ? { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } } : { on: { on: false } };
|
|
214
|
+
//state = { on: { on: true }, dimming: { brightness: dbright }, color: { xy: dretXY } };
|
|
214
215
|
} else if (temperatureChoosen !== undefined) {
|
|
215
216
|
// Kelvin
|
|
216
217
|
const mirek = hueColorConverter.ColorConverter.kelvinToMirek(temperatureChoosen);
|
|
@@ -219,8 +220,10 @@ module.exports = function (RED) {
|
|
|
219
220
|
node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
|
|
220
221
|
// Kelvin temp
|
|
221
222
|
state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen }, color_temperature: { mirek: mirek } } : { on: { on: false } };
|
|
223
|
+
//state = { on: { on: true }, dimming: { brightness: brightnessChoosen }, color_temperature: { mirek: mirek } };
|
|
222
224
|
} else if (brightnessChoosen !== undefined) {
|
|
223
225
|
state = brightnessChoosen > 0 ? { on: { on: true }, dimming: { brightness: brightnessChoosen } } : { on: { on: false } };
|
|
226
|
+
//state = { on: { on: true }, dimming: { brightness: brightnessChoosen } };
|
|
224
227
|
} else {
|
|
225
228
|
state = { on: { on: true } };
|
|
226
229
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.4.
|
|
6
|
+
"version": "2.4.8",
|
|
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",
|