node-red-contrib-knx-ultimate 1.3.25 → 1.3.29
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 +20 -0
- package/README.md +9 -9
- package/img/c/agata.png +0 -0
- package/img/c/knxuserforum.png +0 -0
- package/img/c/viveresmart.png +0 -0
- package/img/wiki/LoadControlSimple.png +0 -0
- package/img/wiki/viewer1.png +0 -0
- package/img/wiki/viewer2.png +0 -0
- package/nodes/icons/node-eye-icon.svg +10 -0
- package/nodes/knxUltimate-config.js +7 -6
- package/nodes/knxUltimateLoadControl.js +83 -43
- package/nodes/knxUltimateViewer.html +71 -0
- package/nodes/knxUltimateViewer.js +123 -0
- package/nodes/locales/de/knxUltimateViewer.html +14 -0
- package/nodes/locales/de/knxUltimateViewer.json +7 -0
- package/nodes/locales/en-US/knxUltimateViewer.html +14 -0
- package/nodes/locales/en-US/knxUltimateViewer.json +7 -0
- package/nodes/locales/it/knxUltimateViewer.html +14 -0
- package/nodes/locales/it/knxUltimateViewer.json +7 -0
- package/nodes/locales/zh-CN/knxUltimateViewer.html +14 -0
- package/nodes/locales/zh-CN/knxUltimateViewer.json +7 -0
- package/package.json +3 -2
- package/img/covidfree.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
[](https://www.paypal.me/techtoday)
|
|
4
4
|
|
|
5
5
|
<br/>
|
|
6
|
+
|
|
7
|
+
# CHANGELOG
|
|
8
|
+
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 1.3.29</b> - February 2022<br/>
|
|
11
|
+
- Load Control: the timer for shedding won't everytime obey to what you've set. Fixed. <br/>
|
|
12
|
+
- Load Control: Added pre-shedding yellow warning message in the node status.<br/>
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
<b>Version 1.3.28</b> - February 2022<br/>
|
|
16
|
+
- NEW: KNX Viewer: this node allow you to see all datapints and values in a dashboard wirget. https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer<br/>
|
|
17
|
+
</p>
|
|
18
|
+
<p>
|
|
19
|
+
<b>Version 1.3.27</b> - February 2022<br/>
|
|
20
|
+
- Load Control: minor fixes + issue a KNX read of Watt values, in case the GA doesn't automatically send a power value on change.<br/>
|
|
21
|
+
</p>
|
|
22
|
+
<p>
|
|
23
|
+
<b>Version 1.3.26</b> - February 2022<br/>
|
|
24
|
+
- FIX: fix a crash occurring it the KNX Gateway is set to "emulate" (that means, don't write to the bus).<br/>
|
|
25
|
+
</p>
|
|
6
26
|
<p>
|
|
7
27
|
<b>Version 1.3.25</b> - February 2022<br/>
|
|
8
28
|
- FIX: Load Control: measure unit was Wh. Corrected in W. Thank @Mauro of VivereSmart Facebook group https://www.facebook.com/groups/viveresmart<br/>
|
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
[![NPM downloads total][npm-downloads-total-image]][npm-url]
|
|
8
8
|
[![MIT License][license-image]][license-url]
|
|
9
9
|
[](https://standardjs.com)
|
|
10
|
-
[![
|
|
10
|
+
[![Youtube][youtube-image]][youtube-url]
|
|
11
11
|
[](https://www.paypal.me/techtoday)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
|
-
Control your KNX intallation via Node-Red!
|
|
16
|
+
Control your KNX intallation via Node-Red!
|
|
17
17
|
|
|
18
18
|
**You can use it immediately!**
|
|
19
19
|
```javascript
|
|
@@ -29,7 +29,8 @@ payload = {red:255, green:200, blue:30} // Put some colors in our life
|
|
|
29
29
|
* **LOGGER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/Logger-Configuration), creates an XML diagnostic file, compatible with ETS. You can open it with ETS for diagnostic pourposes. Node: the Logger currently doesn't record the telegrams coming from KNX-Ultimate if you use a **KNX/IP Interface**.
|
|
30
30
|
* **GLOBAL CONTEXT node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/GlobalVariable), exposes the group addresses to a Global Context variable, to be used in function nodes.
|
|
31
31
|
* **ALERTER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/Alerter-Configuration). With the Alerter node you can signal to a display or to the node-red-contrib-tts-ultimate node (audio feedback), whenever the selected devices are alerted, i.e. they have payload **true**.
|
|
32
|
-
|
|
32
|
+
* **LOAD CONTROL node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/LoadControl-Configuration). Control your loads (Oven, Washing machine, etc..) and avoit shutting down the main voltage due to too high power consumption.
|
|
33
|
+
* **VIEWER node** [here](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer). View all Group Addresses and values of your KNX BUS, in the Node-Red Dashboard.
|
|
33
34
|
|
|
34
35
|
## CHANGELOG
|
|
35
36
|
|
|
@@ -67,8 +68,7 @@ Click your language to go to the documentation.<br/>
|
|
|
67
68
|
## STARTER PACK
|
|
68
69
|
|
|
69
70
|
* [Wiki and Help](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki)
|
|
70
|
-
* [Youtube
|
|
71
|
-
* [Facebook page](https://www.facebook.com/supergiovaneDev)
|
|
71
|
+
* [Youtube channel](https://www.youtube.com/playlist?list=PL9Yh1bjbLAYpfy1Auz6CKDfXUusgMwOQr)
|
|
72
72
|
* [FAQ + Troubleshoot](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/5.-FAQ-Troubleshoot)
|
|
73
73
|
* [Security best practices](https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/SECURITY)
|
|
74
74
|
|
|
@@ -297,11 +297,11 @@ List of commercial companies, which have given us permission to be mentioned on
|
|
|
297
297
|
## FRIENDLY COMMUNITIES AROUND THE WORLD
|
|
298
298
|
|
|
299
299
|
**Italy**
|
|
300
|
-
* [
|
|
300
|
+
* [](https://www.facebook.com/groups/viveresmart)
|
|
301
301
|
* [VivereSmart TV](https://www.youtube.com/channel/UC6GlFhcbNuoSEejZ_HlCynA)
|
|
302
302
|
|
|
303
303
|
**Germany**
|
|
304
|
-
* [knx-
|
|
304
|
+
* [](https://knx-user-forum.de/forum/öffentlicher-bereich/knx-eib-forum/1389088-knx-node-for-node-red)
|
|
305
305
|
|
|
306
306
|
**China**
|
|
307
307
|
* [QQ group: 837579219 (加群需要备注 “来自github”](tencent://groupwpa/?subcmd=all¶m=7b2267726f757055696e223a3833373537393231392c2274696d655374616d70223a313633303934363639312c22617574684b6579223a22762b72482b466f4a496a75613033794e4a30744a6970756c55753639424f4d55724f464c4a6c474b77346a30326b7a4f7a3338535536517844684d7756414d62222c2261757468223a22227d&jump_from=)
|
|
@@ -319,5 +319,5 @@ List of commercial companies, which have given us permission to be mentioned on
|
|
|
319
319
|
[npm-version-image]: https://img.shields.io/npm/v/node-red-contrib-knx-ultimate.svg
|
|
320
320
|
[npm-downloads-month-image]: https://img.shields.io/npm/dm/node-red-contrib-knx-ultimate.svg
|
|
321
321
|
[npm-downloads-total-image]: https://img.shields.io/npm/dt/node-red-contrib-knx-ultimate.svg
|
|
322
|
-
[
|
|
323
|
-
[
|
|
322
|
+
[youtube-image]: https://img.shields.io/badge/Visit%20me-Youtube-red
|
|
323
|
+
[youtube-url]: https://www.youtube.com/channel/UCA9RsLps1IthT7fDSeUbRZw/playlists
|
package/img/c/agata.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="40px" height="60px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
|
3
|
+
<!-- Generated by Pixelmator Pro 2.2 -->
|
|
4
|
+
<g id="group">
|
|
5
|
+
<g id="group-1">
|
|
6
|
+
<path id="Percorso" d="M34 256 L60.2 282.2 C168.2 390.2 343.9 390.2 451.9 282.2 L478 256 451.8 229.8 C343.8 121.8 168.1 121.8 60.1 229.8 Z M256 382.2 C180.2 382.2 104.4 353.3 46.7 295.6 L13.8 262.7 C10.1 259 10.1 253 13.8 249.2 L46.7 216.3 C162.1 100.9 349.9 100.9 465.3 216.3 L498.2 249.2 C501.9 252.9 501.9 258.9 498.2 262.7 L465.3 295.6 C407.6 353.3 331.8 382.2 256 382.2 Z" fill="#ffffff" fill-opacity="1" stroke="none"/>
|
|
7
|
+
<path id="Percorso-1" d="M256 183.5 C216 183.5 183.5 216 183.5 256 183.5 296 216 328.5 256 328.5 296 328.5 328.5 296 328.5 256 328.5 216 296 183.5 256 183.5 Z M256 347.5 C205.5 347.5 164.5 306.4 164.5 256 164.5 205.5 205.6 164.5 256 164.5 306.4 164.5 347.5 205.6 347.5 256 347.5 306.5 306.5 347.5 256 347.5 Z" fill="#ffffff" fill-opacity="1" stroke="none"/>
|
|
8
|
+
</g>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -1115,15 +1115,16 @@ return msg;`, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-
|
|
|
1115
1115
|
}
|
|
1116
1116
|
|
|
1117
1117
|
// 26/12/2021 If the KNXEngine is busy waiting for telegram's ACK, exit
|
|
1118
|
-
if (
|
|
1119
|
-
node.
|
|
1120
|
-
|
|
1121
|
-
if (node.
|
|
1118
|
+
if (node.host.toUpperCase() !== "EMULATE") {
|
|
1119
|
+
if (!node.knxConnection._getClearToSend()) {
|
|
1120
|
+
node.lockHandleTelegramQueue = false; // Unlock the function
|
|
1121
|
+
if (node.telegramsQueue.length > 0) {
|
|
1122
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.warn("knxUltimate-config: handleTelegramQueue: the KNXEngine is busy or is waiting for a telegram ACK with seqNumner " + node.knxConnection._getSeqNumber() + ". Delay handling queue.");
|
|
1123
|
+
}
|
|
1124
|
+
return;
|
|
1122
1125
|
}
|
|
1123
|
-
return;
|
|
1124
1126
|
}
|
|
1125
1127
|
|
|
1126
|
-
|
|
1127
1128
|
// Retrieving oKNXMessage { grpaddr, payload,dpt,outputtype (write or response),nodecallerid (node caller)}. 06/03/2020 "Read" request does have the lower priority in the queue, so firstly, i search for "read" telegrams and i move it on the top of the queue pile.
|
|
1128
1129
|
var aTelegramsFiltered = [];
|
|
1129
1130
|
aTelegramsFiltered = node.telegramsQueue.filter(a => a.outputtype !== "read");
|
|
@@ -21,6 +21,10 @@ module.exports = function (RED) {
|
|
|
21
21
|
node.inputRBE = "false"
|
|
22
22
|
node.isLoadControlNode = true; // Signal to config node, that this is a Load Control node
|
|
23
23
|
node.initialread = true;
|
|
24
|
+
node.formatmultiplyvalue = 1;
|
|
25
|
+
node.formatnegativevalue = "zero";
|
|
26
|
+
node.formatdecimalsvalue = 0;
|
|
27
|
+
|
|
24
28
|
node.sheddingStage = 0;
|
|
25
29
|
node.timerIncreaseShedding = null;
|
|
26
30
|
node.timerDecreaseShedding = null;
|
|
@@ -77,17 +81,21 @@ module.exports = function (RED) {
|
|
|
77
81
|
node.status({ fill: fill, shape: shape, text: text + " Shed:" + node.sheddingStage + " Power:" + node.totalWatt + "W" + " Limit:" + node.wattLimit + "W (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
|
|
78
82
|
} catch (error) {
|
|
79
83
|
}
|
|
80
|
-
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
// This function is called by the knx-ultimate config node.
|
|
84
87
|
node.handleSend = msg => {
|
|
85
88
|
|
|
86
89
|
// Update the Total Watt?
|
|
87
|
-
if (msg.topic === node.topic) {
|
|
90
|
+
if (msg.topic === node.topic && msg.payload !== "" && msg.payload !== null && msg.payload !== undefined) {
|
|
88
91
|
node.totalWatt = msg.payload;
|
|
89
|
-
// Update current consumption
|
|
90
|
-
node.
|
|
92
|
+
// Update current consumption only if the node is in idle state
|
|
93
|
+
if (node.timerIncreaseShedding === null && node.timerDecreaseShedding === null) {
|
|
94
|
+
if (node.setLocalStatusTotalWattTimer === null) clearInterval(node.setLocalStatusTotalWattTimer);
|
|
95
|
+
node.setLocalStatusTotalWattTimer = setTimeout(() => {
|
|
96
|
+
node.setLocalStatus({ fill: "grey" });
|
|
97
|
+
}, 2000);
|
|
98
|
+
}
|
|
91
99
|
return;
|
|
92
100
|
}
|
|
93
101
|
|
|
@@ -105,7 +113,7 @@ module.exports = function (RED) {
|
|
|
105
113
|
// monitorVal: null
|
|
106
114
|
|
|
107
115
|
var oRow = node.deviceList[i];
|
|
108
|
-
if (msg.topic === oRow.monitorGA) {
|
|
116
|
+
if (msg.topic === oRow.monitorGA && msg.payload !== null && msg.payload !== undefined) {
|
|
109
117
|
oRow.monitorVal = msg.payload;
|
|
110
118
|
//node.setLocalStatus({ fill: "blue", shape: "dot", text: "Updated", payload: oRow.monitorVal, GA: msg.topic, dpt: "", devicename: oRow.monitorName });
|
|
111
119
|
}
|
|
@@ -118,13 +126,16 @@ module.exports = function (RED) {
|
|
|
118
126
|
// 03/02/2022 perform a read on all GA in the list
|
|
119
127
|
node.initialReadAllDevicesInRules = () => {
|
|
120
128
|
if (node.server) {
|
|
129
|
+
// Read status of the Total Power GA
|
|
130
|
+
if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.topic, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
131
|
+
|
|
121
132
|
for (var i = 0; i < node.deviceList.length; i++) {
|
|
122
133
|
let grpaddr = node.deviceList[i].monitorGA;
|
|
123
|
-
if (grpaddr !== undefined && grpaddr !== "") {
|
|
134
|
+
if (grpaddr !== undefined && grpaddr !== "" && grpaddr !== null) {
|
|
124
135
|
try {
|
|
125
136
|
// Check if it's a group address
|
|
126
137
|
let ret = Address.KNXAddress.createFromString(grpaddr, Address.KNXAddress.TYPE_GROUP);
|
|
127
|
-
node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read Power from BUS" });
|
|
138
|
+
//node.setLocalStatus({ fill: "grey", shape: "dot", text: "Read Power from BUS" });
|
|
128
139
|
node.server.writeQueueAdd({ grpaddr: grpaddr, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
129
140
|
} catch (error) {
|
|
130
141
|
node.setLocalStatus({ fill: "grey", shape: "dot", text: "Not a KNX GA " + error.message });
|
|
@@ -136,25 +147,55 @@ module.exports = function (RED) {
|
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (node.timerIncreaseShedding !== null) clearInterval(node.timerIncreaseShedding);
|
|
145
|
-
node.timerIncreaseShedding = setInterval(() => {
|
|
146
|
-
|
|
147
|
-
// Issue a READ request to the main Watt GA
|
|
148
|
-
if (node.topic !== undefined && node.topic !== null && node.topic !== "") node.server.writeQueueAdd({ grpaddr: node.ga, payload: "", dpt: "", outputtype: "read", nodecallerid: node.id });
|
|
150
|
+
node.startMainTimer = () => {
|
|
151
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);// Clear the timer
|
|
152
|
+
node.mainTimer = setInterval(() => {
|
|
153
|
+
// Issue a READ on all GA's
|
|
154
|
+
node.initialReadAllDevicesInRules();
|
|
149
155
|
|
|
150
156
|
// Check consumption
|
|
151
157
|
if (node.totalWatt > node.wattLimit) {
|
|
152
158
|
// Start increasing shedding!
|
|
153
159
|
if (node.sheddingStage < node.deviceList.length) {
|
|
154
|
-
node.
|
|
155
|
-
|
|
160
|
+
if (node.timerIncreaseShedding === null) {
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
node.setLocalStatus({ fill: "yellow", shape: "dot", text: "I'm about to shed the load " + node.sheddingStage, payload: "", GA: "", dpt: "", devicename: "" });
|
|
163
|
+
}, 2000);
|
|
164
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);// Clear the decreasing timer
|
|
165
|
+
node.startTimerIncreaseShedding();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else if (node.totalWatt <= node.wattLimit) {
|
|
169
|
+
// Start decreasing shedding!
|
|
170
|
+
if (node.sheddingStage > 0) {
|
|
171
|
+
if (node.timerDecreaseShedding === null) {
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
node.setLocalStatus({ fill: "yellow", shape: "dot", text: "I'm about to unshed the load " + node.sheddingStage, payload: "", GA: "", dpt: "", devicename: "" });
|
|
174
|
+
}, 2000);
|
|
175
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);// Clear the increasing timer
|
|
176
|
+
node.startTimerDecreaseShedding();
|
|
177
|
+
}
|
|
156
178
|
}
|
|
157
179
|
}
|
|
180
|
+
}, 10000);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Start the timer
|
|
184
|
+
node.startTimerIncreaseShedding = () => {
|
|
185
|
+
|
|
186
|
+
// Increase shedding timer (Switch off devices)
|
|
187
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
188
|
+
node.timerIncreaseShedding = setTimeout(() => {
|
|
189
|
+
if (node.server) {
|
|
190
|
+
// Check consumption
|
|
191
|
+
if (node.totalWatt > node.wattLimit) {
|
|
192
|
+
// Start increasing shedding!
|
|
193
|
+
if (node.sheddingStage < node.deviceList.length) {
|
|
194
|
+
node.increaseShedding();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
node.timerIncreaseShedding = null; // Nullify the timer.
|
|
158
199
|
}, node.sheddingCheckInterval);
|
|
159
200
|
}
|
|
160
201
|
|
|
@@ -162,17 +203,18 @@ module.exports = function (RED) {
|
|
|
162
203
|
node.startTimerDecreaseShedding = () => {
|
|
163
204
|
|
|
164
205
|
// Decrease shedding timer (Switch devices on again)
|
|
165
|
-
if (node.timerDecreaseShedding !== null)
|
|
166
|
-
node.timerDecreaseShedding =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
206
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
207
|
+
node.timerDecreaseShedding = setTimeout(() => {
|
|
208
|
+
if (node.server) {
|
|
209
|
+
// Check consumption
|
|
210
|
+
if (node.totalWatt <= node.wattLimit) {
|
|
211
|
+
// Start decreasing shedding!
|
|
212
|
+
if (node.sheddingStage > 0) {
|
|
213
|
+
node.decreaseShedding();
|
|
214
|
+
}
|
|
173
215
|
}
|
|
174
216
|
}
|
|
175
|
-
|
|
217
|
+
node.timerDecreaseShedding = null; // Nullify timer
|
|
176
218
|
}, node.sheddingRestoreDelay);
|
|
177
219
|
}
|
|
178
220
|
|
|
@@ -256,20 +298,18 @@ module.exports = function (RED) {
|
|
|
256
298
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
|
|
257
299
|
}, 1000);
|
|
258
300
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
301
|
}
|
|
262
302
|
|
|
263
303
|
// Start
|
|
264
|
-
node.
|
|
304
|
+
node.startMainTimer();
|
|
265
305
|
|
|
266
306
|
node.on("input", function (msg) {
|
|
267
307
|
if (typeof msg === "undefined") return;
|
|
268
308
|
|
|
269
309
|
// Reset the shedding and activate all loads
|
|
270
310
|
if (msg.hasOwnProperty("reset")) {
|
|
271
|
-
if (node.timerDecreaseShedding !== null)
|
|
272
|
-
if (node.timerIncreaseShedding !== null)
|
|
311
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
312
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
273
313
|
node.sheddingStage = 0;
|
|
274
314
|
for (let index = 0; index < node.deviceList.length; index++) {
|
|
275
315
|
const oRow = node.deviceList[index];
|
|
@@ -277,16 +317,15 @@ module.exports = function (RED) {
|
|
|
277
317
|
}
|
|
278
318
|
setTimeout(() => {
|
|
279
319
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "All loads have been restored" });
|
|
280
|
-
// Restart shedding timer
|
|
281
|
-
node.startTimerIncreaseShedding();
|
|
282
320
|
}, 1000);
|
|
283
321
|
node.send({ topic: node.name || node.topic, operation: "Reset", payload: node.sheddingStage });
|
|
284
322
|
}
|
|
285
323
|
|
|
286
324
|
// Disable the shedding node
|
|
287
325
|
if (msg.hasOwnProperty("disable")) {
|
|
288
|
-
if (node.timerDecreaseShedding !== null)
|
|
289
|
-
if (node.timerIncreaseShedding !== null)
|
|
326
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
327
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
328
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);
|
|
290
329
|
setTimeout(() => {
|
|
291
330
|
node.setLocalStatus({ fill: "grey", shape: "dot", text: "Disabled" });
|
|
292
331
|
}, 1000);
|
|
@@ -295,12 +334,12 @@ module.exports = function (RED) {
|
|
|
295
334
|
|
|
296
335
|
// Disable the shedding node
|
|
297
336
|
if (msg.hasOwnProperty("enable")) {
|
|
298
|
-
if (node.timerDecreaseShedding !== null)
|
|
299
|
-
if (node.timerIncreaseShedding !== null)
|
|
337
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
338
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
300
339
|
setTimeout(() => {
|
|
301
340
|
node.setLocalStatus({ fill: "green", shape: "dot", text: "Enabled" });
|
|
302
|
-
// Restart
|
|
303
|
-
node.
|
|
341
|
+
// Restart timer
|
|
342
|
+
node.startMainTimer();
|
|
304
343
|
}, 1000);
|
|
305
344
|
node.send({ topic: node.name || node.topic, operation: "Enabled", payload: node.sheddingStage });
|
|
306
345
|
}
|
|
@@ -313,8 +352,9 @@ module.exports = function (RED) {
|
|
|
313
352
|
})
|
|
314
353
|
|
|
315
354
|
node.on("close", function (done) {
|
|
316
|
-
if (node.
|
|
317
|
-
if (node.
|
|
355
|
+
if (node.mainTimer !== null) clearInterval(node.mainTimer);
|
|
356
|
+
if (node.timerDecreaseShedding !== null) clearTimeout(node.timerDecreaseShedding);
|
|
357
|
+
if (node.timerIncreaseShedding !== null) clearTimeout(node.timerIncreaseShedding);
|
|
318
358
|
if (node.server) {
|
|
319
359
|
node.server.removeClient(node)
|
|
320
360
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('knxUltimateViewer', {
|
|
3
|
+
category: "KNX Ultimate",
|
|
4
|
+
color: '#C7E9C0',
|
|
5
|
+
defaults: {
|
|
6
|
+
//buttonState: {value: true},
|
|
7
|
+
server: { type: "knxUltimate-config", required: true },
|
|
8
|
+
name: { value: "KNXViewer", validate: RED.validators.regex(/^[a-z]+$/i) }
|
|
9
|
+
},
|
|
10
|
+
inputs: 0,
|
|
11
|
+
outputs: 1,
|
|
12
|
+
icon: "node-eye-icon.svg",
|
|
13
|
+
label: function () {
|
|
14
|
+
return (this.name);
|
|
15
|
+
},
|
|
16
|
+
paletteLabel: "KNX Viewer",
|
|
17
|
+
// button: {
|
|
18
|
+
// enabled: function() {
|
|
19
|
+
// // return whether or not the button is enabled, based on the current
|
|
20
|
+
// // configuration of the node
|
|
21
|
+
// return !this.changed
|
|
22
|
+
// },
|
|
23
|
+
// visible: function() {
|
|
24
|
+
// // return whether or not the button is visible, based on the current
|
|
25
|
+
// // configuration of the node
|
|
26
|
+
// return this.hasButton
|
|
27
|
+
// },
|
|
28
|
+
// //toggle: "buttonState",
|
|
29
|
+
// onclick: function() {}
|
|
30
|
+
// },
|
|
31
|
+
oneditprepare: function () {
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
},
|
|
35
|
+
oneditsave: function () {
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
},
|
|
39
|
+
oneditcancel: function () {
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<script type="text/x-red" data-template-name="knxUltimateViewer">
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
<div class="form-row">
|
|
50
|
+
<b><span data-i18n="knxUltimateViewer.title"></span></b>  <span style="color:red"
|
|
51
|
+
data-i18n="[html]knxUltimateViewer.helplink"></span>
|
|
52
|
+
<br />
|
|
53
|
+
<label for="node-input-server">
|
|
54
|
+
<img
|
|
55
|
+
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img>
|
|
56
|
+
<span data-i18n="knxUltimateViewer.advanced.node-input-server"></span>
|
|
57
|
+
</label>
|
|
58
|
+
<input type="text" id="node-input-server" />
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="form-row">
|
|
62
|
+
<label for="node-input-name">
|
|
63
|
+
<i class="fa fa-tag"></i>
|
|
64
|
+
<span data-i18n="knxUltimateViewer.node-input-name"></span>
|
|
65
|
+
</label>
|
|
66
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]knxUltimateViewer.node-input-name" />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
</script>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
|
|
3
|
+
function knxUltimateViewer(config) {
|
|
4
|
+
RED.nodes.createNode(this, config)
|
|
5
|
+
var node = this
|
|
6
|
+
node.server = RED.nodes.getNode(config.server)
|
|
7
|
+
node.topic = node.name;
|
|
8
|
+
node.name = config.name === undefined ? "KNXGlobalContext" : config.name;
|
|
9
|
+
node.outputtopic = node.name;
|
|
10
|
+
node.dpt = "";
|
|
11
|
+
node.notifyreadrequest = false
|
|
12
|
+
node.notifyreadrequestalsorespondtobus = "false";
|
|
13
|
+
node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = "";
|
|
14
|
+
node.notifyresponse = true;
|
|
15
|
+
node.notifywrite = true;
|
|
16
|
+
node.initialread = false
|
|
17
|
+
node.listenallga = true;
|
|
18
|
+
node.outputtype = "write";
|
|
19
|
+
node.outputRBE = false // Apply or not RBE to the output (Messages coming from flow)
|
|
20
|
+
node.inputRBE = false // Apply or not RBE to the input (Messages coming from BUS)
|
|
21
|
+
node.currentPayload = "" // Current value for the RBE input and for the .previouspayload msg
|
|
22
|
+
node.passthrough = "no";
|
|
23
|
+
node.formatmultiplyvalue = 1;
|
|
24
|
+
node.formatnegativevalue = "leave";
|
|
25
|
+
node.formatdecimalsvalue = 2;
|
|
26
|
+
|
|
27
|
+
node.exposedGAs = [];
|
|
28
|
+
|
|
29
|
+
// Used to call the status update from the config node.
|
|
30
|
+
node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
|
|
31
|
+
if (node.server == null) { node.status({ fill: "red", shape: "dot", text: "[NO GATEWAY SELECTED]" }); return; }
|
|
32
|
+
GA = GA === undefined ? "" : GA;
|
|
33
|
+
payload = payload === undefined ? "" : payload;
|
|
34
|
+
let dDate = new Date();
|
|
35
|
+
node.status({ fill: fill, shape: shape, text: GA + " " + payload + " " + text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
40
|
+
node.handleSend = msg => {
|
|
41
|
+
try {
|
|
42
|
+
var oGa = node.exposedGAs.find(ga => ga.address === msg.knx.destination);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
let dDate = new Date();
|
|
47
|
+
if (oGa === undefined) {
|
|
48
|
+
node.exposedGAs.push({ address: msg.knx.destination, dpt: msg.knx.dpt, payload: msg.payload, devicename: msg.devicename || "Import ETS file", lastupdate: + dDate.getDate() + ", " + dDate.toLocaleTimeString() });
|
|
49
|
+
} else {
|
|
50
|
+
oGa.dpt = msg.knx.dpt;
|
|
51
|
+
oGa.payload = msg.payload;
|
|
52
|
+
oGa.devicename = msg.devicename || "Import ETS file";
|
|
53
|
+
oGa.lastupdate = dDate.getDate() + ", " + dDate.toLocaleTimeString()
|
|
54
|
+
}
|
|
55
|
+
// Output the payload
|
|
56
|
+
node.createPayload();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
node.createPayload = () => {
|
|
60
|
+
let sHead = `<table>
|
|
61
|
+
<thead>
|
|
62
|
+
<tr>
|
|
63
|
+
<th> GA </th>
|
|
64
|
+
<th> Value </th>
|
|
65
|
+
<th> DPT </th>
|
|
66
|
+
<th> Day, time </th>
|
|
67
|
+
<th> Name </th>
|
|
68
|
+
</tr>
|
|
69
|
+
</thead>
|
|
70
|
+
<tbody>`;
|
|
71
|
+
let sFooter = `</tbody>
|
|
72
|
+
</table>`;
|
|
73
|
+
let sPayload = "";
|
|
74
|
+
|
|
75
|
+
const aSorted = node.exposedGAs.sort((a, b) => {
|
|
76
|
+
if( a.address !== undefined && b.address !== undefined ) {
|
|
77
|
+
return a.address > b.address ? 1 : -1;
|
|
78
|
+
} else {
|
|
79
|
+
return a.address !== undefined ? 1 : -1
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
for (let index = 0; index < aSorted.length; index++) {
|
|
84
|
+
const element = aSorted[index];
|
|
85
|
+
sPayload += `<tr>
|
|
86
|
+
<td>` + element.address + `</td>`;
|
|
87
|
+
if (typeof element.payload === "boolean" && element.payload === true) {
|
|
88
|
+
sPayload += "<td><b><font color=green>True</font></b></td>";
|
|
89
|
+
} else if (typeof element.payload === "boolean" && element.payload === false) {
|
|
90
|
+
sPayload += "<td><font color=red>False</font></td>";
|
|
91
|
+
} else {
|
|
92
|
+
sPayload += "<td>" + element.payload + "</td>";
|
|
93
|
+
}
|
|
94
|
+
sPayload += "<td>" + element.dpt + "</td>"
|
|
95
|
+
sPayload += "<td>" + element.lastupdate + "</td>";
|
|
96
|
+
sPayload += "<td><font size=2pt>" + element.devicename + "</font></td></tr>";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
node.send({ topic: node.name, payload: sHead + sPayload + sFooter });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
node.on("input", function (msg) {
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
node.on("close", function (done) {
|
|
108
|
+
if (node.server) {
|
|
109
|
+
node.server.removeClient(node);
|
|
110
|
+
}
|
|
111
|
+
done();
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// On each deploy, unsubscribe+resubscribe
|
|
115
|
+
if (node.server) {
|
|
116
|
+
node.server.removeClient(node);
|
|
117
|
+
node.server.addClient(node);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
RED.nodes.registerType("knxUltimateViewer", knxUltimateViewer)
|
|
123
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script type="text/x-red" data-help-name="knxUltimate-config">
|
|
2
|
+
<h1>KNX Ultimate - Nodo Global Context</h1>
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/de-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>  Beispiel</a>
|
|
7
|
+
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p>
|
|
11
|
+
<a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
|
|
12
|
+
|
|
13
|
+
</p>
|
|
14
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateViewer": {
|
|
3
|
+
"helplink" : " <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/de-knxUltimateViewer\"><u>Esempio</u></a>",
|
|
4
|
+
"title": "KNX Viewer",
|
|
5
|
+
"node-input-name": "Name"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script type="text/x-red" data-help-name="knxUltimate-config">
|
|
2
|
+
<h1>KNX Ultimate - Nodo Global Context</h1>
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>  Sample</a>
|
|
7
|
+
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p>
|
|
11
|
+
<a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
|
|
12
|
+
|
|
13
|
+
</p>
|
|
14
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateViewer": {
|
|
3
|
+
"helplink" : " <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/knxUltimateViewer\"><u>Esempio</u></a>",
|
|
4
|
+
"title": "KNX Viewer",
|
|
5
|
+
"node-input-name": "Name"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script type="text/x-red" data-help-name="knxUltimate-config">
|
|
2
|
+
<h1>KNX Ultimate - Nodo Global Context</h1>
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/it-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>  Esempio</a>
|
|
7
|
+
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p>
|
|
11
|
+
<a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
|
|
12
|
+
|
|
13
|
+
</p>
|
|
14
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateViewer": {
|
|
3
|
+
"helplink" : " <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/it-knxUltimateViewer\"><u>Esempio</u></a>",
|
|
4
|
+
"title": "KNX Viewer",
|
|
5
|
+
"node-input-name": "Nome"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script type="text/x-red" data-help-name="knxUltimate-config">
|
|
2
|
+
<h1>KNX Ultimate - Nodo Global Context</h1>
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<a href="https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/cn-knxUltimateViewer" target="_blank"><i class="fa fa-info-circle"></i>  Sample</a>
|
|
7
|
+
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p>
|
|
11
|
+
<a href="https://www.paypal.me/techtoday" target="_blank"><img src='https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square' width='30%'></a>
|
|
12
|
+
|
|
13
|
+
</p>
|
|
14
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knxUltimateViewer": {
|
|
3
|
+
"helplink" : " <i class=\"fa fa-question-circle\"></i> <a target=\"_blank\" href=\"https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/cn-knxUltimateViewer\"><u>Esempio</u></a>",
|
|
4
|
+
"title": "KNX Viewer",
|
|
5
|
+
"node-input-name": "节点名称"
|
|
6
|
+
}
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-knx-ultimate",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.29",
|
|
4
4
|
"description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable.",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"fs": "0.0.1-security",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"knxUltimate-config": "/nodes/knxUltimate-config.js",
|
|
29
29
|
"knxUltimateGlobalContext": "/nodes/knxUltimateGlobalContext.js",
|
|
30
30
|
"knxUltimateAlerter": "/nodes/knxUltimateAlerter.js",
|
|
31
|
-
"knxUltimateLoadControl":"/nodes/knxUltimateLoadControl.js"
|
|
31
|
+
"knxUltimateLoadControl":"/nodes/knxUltimateLoadControl.js",
|
|
32
|
+
"knxUltimateViewer":"/nodes/knxUltimateViewer.js"
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
35
|
"repository": {
|
package/img/covidfree.png
DELETED
|
Binary file
|