node-red-contrib-3dm-space 2.2.0 → 2.2.1

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/3dm-cloud-in.js CHANGED
@@ -1,5 +1,7 @@
1
1
  module.exports = function (RED) {
2
- "use strict";
2
+ "use strict";
3
+
4
+ var roundPayload = require("./payload-precision");
3
5
 
4
6
  /*
5
7
  * Hidden cloud subscribe settings.
@@ -53,7 +55,7 @@ module.exports = function (RED) {
53
55
  return;
54
56
  }
55
57
 
56
- var data = parsePayload(payload);
58
+ var data = roundPayload(parsePayload(payload));
57
59
 
58
60
  if (node.keyFilter) {
59
61
  if (typeof data !== "object" || data === null || Array.isArray(data)) {
package/3dm-cloud-join.js CHANGED
@@ -1,6 +1,8 @@
1
1
  module.exports = function (RED) {
2
2
  "use strict";
3
3
 
4
+ var roundPayload = require("./payload-precision");
5
+
4
6
  function isPlainObject(value) {
5
7
  return value &&
6
8
  typeof value === "object" &&
@@ -79,7 +81,7 @@ module.exports = function (RED) {
79
81
  }
80
82
 
81
83
  var outMsg = node.firstMsg || {};
82
- outMsg.payload = clonePayloadObject(node.buffer);
84
+ outMsg.payload = roundPayload(clonePayloadObject(node.buffer));
83
85
 
84
86
  node.send(outMsg);
85
87
 
package/3dm-cloud-out.js CHANGED
@@ -1,5 +1,7 @@
1
1
  module.exports = function (RED) {
2
- "use strict";
2
+ "use strict";
3
+
4
+ var roundPayload = require("./payload-precision");
3
5
 
4
6
  var fs = require("fs");
5
7
  var path = require("path");
@@ -234,7 +236,7 @@ module.exports = function (RED) {
234
236
 
235
237
  return {
236
238
  ts: ts,
237
- values: payload
239
+ values: roundPayload(payload)
238
240
  };
239
241
  }
240
242
 
@@ -253,6 +255,8 @@ module.exports = function (RED) {
253
255
  return;
254
256
  }
255
257
 
258
+ packet = Object.assign({}, packet, { values: roundPayload(packet.values) });
259
+
256
260
  node.server.publish(
257
261
  TELEMETRY_TOPIC,
258
262
  packet,
@@ -283,6 +287,15 @@ module.exports = function (RED) {
283
287
  }
284
288
 
285
289
  function sendResult(msg, send, done) {
290
+ if (typeof msg.payload === "string") {
291
+ try {
292
+ msg.payload = JSON.stringify(roundPayload(JSON.parse(msg.payload)));
293
+ } catch (err) {
294
+ // Preserve non-JSON text on rejected messages.
295
+ }
296
+ } else {
297
+ msg.payload = roundPayload(msg.payload);
298
+ }
286
299
  send(msg);
287
300
 
288
301
  if (done) {
@@ -1,5 +1,7 @@
1
1
  module.exports = function (RED) {
2
- "use strict";
2
+ "use strict";
3
+
4
+ var roundPayload = require("./payload-precision");
3
5
 
4
6
  var fs = require("fs");
5
7
  var path = require("path");
@@ -694,9 +696,9 @@ module.exports = function (RED) {
694
696
  topic: "configStore",
695
697
  event: eventName || "configStore",
696
698
  payload: {
697
- configStore: latestStore
699
+ configStore: roundPayload(latestStore)
698
700
  },
699
- configStore: latestStore
701
+ configStore: roundPayload(latestStore)
700
702
  };
701
703
 
702
704
  node.send(outputs);
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 2.2.1 — 2026-10-01
4
+
5
+ - Round numeric output payload values to a maximum of five decimal places, including nested objects and arrays.
6
+ - Apply consistent precision to cloud telemetry, queue replay, local telemetry results, incoming attributes, joined messages, and config snapshots.
7
+ - Preserve numeric types, integers, text, stored configuration precision, and internal control comparisons.
8
+ - Include the shared precision helper in the npm package and exclude old release archives.
package/README.md CHANGED
@@ -23,6 +23,13 @@ The package is intended for Smithtek PassPort gateway projects using 3DM.space,
23
23
 
24
24
  ---
25
25
 
26
+
27
+ ## Numeric output precision
28
+
29
+ From version 2.2.1, numeric output payload values are rounded to a maximum of five decimal places. This applies recursively to nested objects and arrays in telemetry, incoming attributes, joined payloads, and config snapshots. For example, `12.12345678` becomes `12.12346`.
30
+
31
+ Values remain numbers; trailing zeros are not added. Integers, strings (including numeric strings), booleans, and null are unchanged. JSON telemetry strings are parsed and their numeric values are rounded before publishing; the local result payload remains a JSON string. Live and queued telemetry use the same precision, including older packets replayed from the queue. Stored configuration and internal control comparisons retain their original precision.
32
+
26
33
  ## Requirements
27
34
 
28
35
  To use these nodes, you need:
@@ -53,7 +60,7 @@ The credentials are copied from 3DM.space into Node-RED in this order:
53
60
 
54
61
  ## Nodes Included
55
62
 
56
- This package includes four nodes:
63
+ This package includes five nodes:
57
64
 
58
65
  - **3DM Cloud Login**
59
66
  - **3DM Out**
package/package.json CHANGED
@@ -1,34 +1,43 @@
1
- {
2
- "name": "node-red-contrib-3dm-space",
3
- "version": "2.2.0",
4
- "description": "Node-RED nodes for 3DM.space SCADA Cloud telemetry, attributes, and local config storage",
5
- "main": "3dm-cloud-config.js",
6
- "license": "GPL-3.0-or-later",
7
- "keywords": [
8
- "node-red",
9
- "node-red-contrib",
10
- "3dm",
11
- "3dm.space",
12
- "smithtek",
13
- "scada",
14
- "telemetry",
15
- "store-and-forward",
16
- "config-store",
17
- "scheduler"
18
- ],
19
- "node-red": {
20
- "nodes": {
21
- "3dm-cloud-config": "3dm-cloud-config.js",
22
- "3dm-cloud-out": "3dm-cloud-out.js",
23
- "3dm-cloud-in": "3dm-cloud-in.js",
24
- "3dm-config-store": "3dm-config-store.js",
25
- "3dm-cloud-join": "3dm-cloud-join.js"
26
- }
27
- },
28
- "dependencies": {
29
- "mqtt": "~4.3.7"
30
- },
31
- "optionalDependencies": {
32
- "node-persistent-queue": "^0.3.1"
33
- }
34
- }
1
+ {
2
+ "name": "node-red-contrib-3dm-space",
3
+ "version": "2.2.1",
4
+ "description": "Node-RED nodes for 3DM.space SCADA Cloud telemetry, attributes, and local config storage",
5
+ "main": "3dm-cloud-config.js",
6
+ "license": "GPL-3.0-or-later",
7
+ "keywords": [
8
+ "node-red",
9
+ "node-red-contrib",
10
+ "3dm",
11
+ "3dm.space",
12
+ "smithtek",
13
+ "scada",
14
+ "telemetry",
15
+ "store-and-forward",
16
+ "config-store",
17
+ "scheduler"
18
+ ],
19
+ "node-red": {
20
+ "nodes": {
21
+ "3dm-cloud-config": "3dm-cloud-config.js",
22
+ "3dm-cloud-out": "3dm-cloud-out.js",
23
+ "3dm-cloud-in": "3dm-cloud-in.js",
24
+ "3dm-config-store": "3dm-config-store.js",
25
+ "3dm-cloud-join": "3dm-cloud-join.js"
26
+ }
27
+ },
28
+ "dependencies": {
29
+ "mqtt": "~4.3.7"
30
+ },
31
+ "optionalDependencies": {
32
+ "node-persistent-queue": "^0.3.1"
33
+ },
34
+ "files": [
35
+ "3dm-*.js",
36
+ "3dm-*.html",
37
+ "payload-precision.js",
38
+ "icons/",
39
+ "icon.png",
40
+ "README.md",
41
+ "CHANGELOG.md"
42
+ ]
43
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ // Round numeric payload values without changing strings or the source objects.
4
+ function roundPayload(value, seen) {
5
+ if (typeof value === "number") {
6
+ return Number.isFinite(value) && !Number.isInteger(value)
7
+ ? Number(value.toFixed(5))
8
+ : value;
9
+ }
10
+
11
+ if (!value || typeof value !== "object") {
12
+ return value;
13
+ }
14
+
15
+ var prototype = Object.getPrototypeOf(value);
16
+ if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {
17
+ return value;
18
+ }
19
+
20
+ seen = seen || new WeakMap();
21
+ if (seen.has(value)) {
22
+ return seen.get(value);
23
+ }
24
+
25
+ var output = Array.isArray(value) ? new Array(value.length) : Object.create(prototype);
26
+ seen.set(value, output);
27
+ Object.keys(value).forEach(function (key) {
28
+ Object.defineProperty(output, key, {
29
+ value: roundPayload(value[key], seen),
30
+ enumerable: true,
31
+ writable: true,
32
+ configurable: true
33
+ });
34
+ });
35
+ return output;
36
+ }
37
+
38
+ module.exports = roundPayload;