node-red-contrib-3dm-space 1.0.10 → 2.1.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.
@@ -0,0 +1,80 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('3dm-cloud-join', {
3
+ category: '3DM.space',
4
+ color: '#6D6D6D',
5
+ defaults: {
6
+ timeout: {
7
+ value: 5,
8
+ required: true,
9
+ validate: RED.validators.number()
10
+ }
11
+ },
12
+ inputs: 1,
13
+ outputs: 1,
14
+ icon: 'font-awesome/fa-compress',
15
+ paletteLabel: '3DM Join',
16
+ label: function () {
17
+ return '3DM Join';
18
+ }
19
+ });
20
+ </script>
21
+
22
+ <script type="text/html" data-template-name="3dm-cloud-join">
23
+ <div class="form-row">
24
+ <label for="node-input-timeout">
25
+ <i class="fa fa-clock-o"></i> Timeout
26
+ </label>
27
+ <input
28
+ type="number"
29
+ id="node-input-timeout"
30
+ min="1"
31
+ step="1"
32
+ placeholder="5"
33
+ style="width: 120px;"
34
+ >
35
+ <span style="margin-left: 6px;">seconds</span>
36
+ </div>
37
+ </script>
38
+
39
+ <script type="text/html" data-help-name="3dm-cloud-join">
40
+ <p>
41
+ The <b>3DM Join</b> node collects JSON object payloads, merges all keys together,
42
+ then sends one combined payload after the configured timeout.
43
+ </p>
44
+
45
+ <h3>Input</h3>
46
+ <p>
47
+ The node expects <code>msg.payload</code> to be a JSON object.
48
+ </p>
49
+
50
+ <pre>{
51
+ "tank_level": 65
52
+ }</pre>
53
+
54
+ <h3>Output</h3>
55
+ <p>
56
+ When the timeout expires, the node outputs one message containing all collected keys.
57
+ </p>
58
+
59
+ <pre>{
60
+ "tank_level": 65,
61
+ "pump_run": true,
62
+ "dc_voltage": 24.4
63
+ }</pre>
64
+
65
+ <h3>Timeout</h3>
66
+ <p>
67
+ The timeout starts from the first valid message received. The timer does not restart
68
+ when more messages arrive.
69
+ </p>
70
+
71
+ <h3>Duplicate Keys</h3>
72
+ <p>
73
+ If the same key arrives more than once during the collection window, the latest value is used.
74
+ </p>
75
+
76
+ <h3>Invalid Payloads</h3>
77
+ <p>
78
+ Payloads that are not JSON objects are ignored.
79
+ </p>
80
+ </script>
@@ -0,0 +1,155 @@
1
+ module.exports = function (RED) {
2
+ "use strict";
3
+
4
+ function isPlainObject(value) {
5
+ return value &&
6
+ typeof value === "object" &&
7
+ !Array.isArray(value) &&
8
+ !Buffer.isBuffer(value);
9
+ }
10
+
11
+ function cleanTimeoutSeconds(value) {
12
+ var seconds = Number(value);
13
+
14
+ if (!isFinite(seconds) || seconds < 1) {
15
+ seconds = 5;
16
+ }
17
+
18
+ seconds = Math.floor(seconds);
19
+
20
+ if (seconds > 86400) {
21
+ seconds = 86400;
22
+ }
23
+
24
+ return seconds;
25
+ }
26
+
27
+ function clonePayloadObject(obj) {
28
+ var out = {};
29
+
30
+ Object.keys(obj).forEach(function (key) {
31
+ out[key] = obj[key];
32
+ });
33
+
34
+ return out;
35
+ }
36
+
37
+ function ThreeDMCloudJoinNode(config) {
38
+ RED.nodes.createNode(this, config);
39
+
40
+ var node = this;
41
+
42
+ node.timeoutSeconds = cleanTimeoutSeconds(config.timeout);
43
+ node.timeoutMs = node.timeoutSeconds * 1000;
44
+
45
+ node.timer = null;
46
+ node.buffer = {};
47
+ node.messageCount = 0;
48
+ node.firstMsg = null;
49
+
50
+ function setStatus(fill, shape, text) {
51
+ node.status({
52
+ fill: fill,
53
+ shape: shape,
54
+ text: text
55
+ });
56
+ }
57
+
58
+ function clearTimer() {
59
+ if (node.timer) {
60
+ clearTimeout(node.timer);
61
+ node.timer = null;
62
+ }
63
+ }
64
+
65
+ function resetBatch() {
66
+ clearTimer();
67
+ node.buffer = {};
68
+ node.messageCount = 0;
69
+ node.firstMsg = null;
70
+ setStatus("grey", "ring", "waiting");
71
+ }
72
+
73
+ function flushBatch() {
74
+ clearTimer();
75
+
76
+ if (node.messageCount < 1) {
77
+ resetBatch();
78
+ return;
79
+ }
80
+
81
+ var outMsg = node.firstMsg || {};
82
+ outMsg.payload = clonePayloadObject(node.buffer);
83
+
84
+ node.send(outMsg);
85
+
86
+ setStatus(
87
+ "green",
88
+ "dot",
89
+ "sent " + Object.keys(node.buffer).length + " keys"
90
+ );
91
+
92
+ node.buffer = {};
93
+ node.messageCount = 0;
94
+ node.firstMsg = null;
95
+ }
96
+
97
+ function startBatch(msg) {
98
+ node.buffer = {};
99
+ node.messageCount = 0;
100
+ node.firstMsg = RED.util.cloneMessage(msg);
101
+
102
+ node.timer = setTimeout(function () {
103
+ flushBatch();
104
+ }, node.timeoutMs);
105
+ }
106
+
107
+ node.on("input", function (msg, send, done) {
108
+ var payload = msg.payload;
109
+
110
+ if (!isPlainObject(payload)) {
111
+ setStatus("red", "ring", "bad payload");
112
+ if (done) {
113
+ done();
114
+ }
115
+ return;
116
+ }
117
+
118
+ if (!node.timer) {
119
+ startBatch(msg);
120
+ }
121
+
122
+ Object.keys(payload).forEach(function (key) {
123
+ node.buffer[key] = payload[key];
124
+ });
125
+
126
+ node.messageCount += 1;
127
+
128
+ setStatus(
129
+ "blue",
130
+ "dot",
131
+ "collecting " + Object.keys(node.buffer).length + " keys"
132
+ );
133
+
134
+ if (done) {
135
+ done();
136
+ }
137
+ });
138
+
139
+ node.on("close", function (removed, done) {
140
+ if (typeof removed === "function") {
141
+ done = removed;
142
+ }
143
+
144
+ resetBatch();
145
+
146
+ if (done) {
147
+ done();
148
+ }
149
+ });
150
+
151
+ resetBatch();
152
+ }
153
+
154
+ RED.nodes.registerType("3dm-cloud-join", ThreeDMCloudJoinNode);
155
+ };
package/3dm-cloud-out.js CHANGED
@@ -56,7 +56,7 @@ module.exports = function (RED) {
56
56
  * 60000 = 1 update per minute.
57
57
  * This only applies to live online data when the queue is empty.
58
58
  */
59
- var LIVE_PUBLISH_INTERVAL_MS = 60000;
59
+ var LIVE_PUBLISH_INTERVAL_MS = 45000;
60
60
 
61
61
  var RATE_LIMIT_TEXT = "rate limited: slow your payloads down!";
62
62
  var QUEUE_FULL_TEXT = "queue full: message rejected";