@bldgblocks/node-red-contrib-control 0.1.32 → 0.1.33

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.
Files changed (71) hide show
  1. package/nodes/accumulate-block.html +1 -1
  2. package/nodes/add-block.html +1 -1
  3. package/nodes/analog-switch-block.html +1 -1
  4. package/nodes/and-block.html +1 -1
  5. package/nodes/average-block.html +1 -1
  6. package/nodes/boolean-switch-block.html +1 -1
  7. package/nodes/boolean-to-number-block.html +1 -1
  8. package/nodes/cache-block.html +1 -1
  9. package/nodes/call-status-block.html +1 -1
  10. package/nodes/changeover-block.html +1 -1
  11. package/nodes/comment-block.html +1 -1
  12. package/nodes/compare-block.html +1 -1
  13. package/nodes/contextual-label-block.html +1 -1
  14. package/nodes/convert-block.html +1 -1
  15. package/nodes/count-block.html +2 -2
  16. package/nodes/delay-block.html +1 -1
  17. package/nodes/divide-block.html +1 -1
  18. package/nodes/edge-block.html +1 -1
  19. package/nodes/enum-switch-block.html +1 -1
  20. package/nodes/frequency-block.html +1 -1
  21. package/nodes/global-getter.html +34 -3
  22. package/nodes/global-getter.js +71 -45
  23. package/nodes/global-setter.html +21 -10
  24. package/nodes/global-setter.js +154 -79
  25. package/nodes/history-collector.html +283 -0
  26. package/nodes/history-collector.js +150 -0
  27. package/nodes/history-config.html +236 -0
  28. package/nodes/history-config.js +8 -0
  29. package/nodes/hysteresis-block.html +1 -1
  30. package/nodes/interpolate-block.html +1 -1
  31. package/nodes/latch-block.html +1 -1
  32. package/nodes/load-sequence-block.html +1 -1
  33. package/nodes/max-block.html +1 -1
  34. package/nodes/memory-block.html +1 -1
  35. package/nodes/min-block.html +1 -1
  36. package/nodes/minmax-block.html +1 -1
  37. package/nodes/modulo-block.html +1 -1
  38. package/nodes/multiply-block.html +1 -1
  39. package/nodes/negate-block.html +1 -1
  40. package/nodes/network-point-registry.html +86 -0
  41. package/nodes/network-point-registry.js +90 -0
  42. package/nodes/network-read.html +56 -0
  43. package/nodes/network-read.js +59 -0
  44. package/nodes/network-register.html +110 -0
  45. package/nodes/network-register.js +161 -0
  46. package/nodes/network-write.html +64 -0
  47. package/nodes/network-write.js +126 -0
  48. package/nodes/nullify-block.html +1 -1
  49. package/nodes/on-change-block.html +1 -1
  50. package/nodes/oneshot-block.html +1 -1
  51. package/nodes/or-block.html +1 -1
  52. package/nodes/pid-block.html +1 -1
  53. package/nodes/priority-block.html +1 -1
  54. package/nodes/rate-limit-block.html +2 -2
  55. package/nodes/rate-of-change-block.html +1 -1
  56. package/nodes/rate-of-change-block.js +5 -2
  57. package/nodes/round-block.html +6 -5
  58. package/nodes/round-block.js +5 -3
  59. package/nodes/saw-tooth-wave-block.html +2 -2
  60. package/nodes/scale-range-block.html +1 -1
  61. package/nodes/sine-wave-block.html +2 -2
  62. package/nodes/string-builder-block.html +1 -1
  63. package/nodes/subtract-block.html +1 -1
  64. package/nodes/thermistor-block.html +1 -1
  65. package/nodes/tick-tock-block.html +2 -2
  66. package/nodes/time-sequence-block.html +1 -1
  67. package/nodes/triangle-wave-block.html +2 -2
  68. package/nodes/tstat-block.html +1 -1
  69. package/nodes/units-block.html +8 -38
  70. package/nodes/units-block.js +3 -42
  71. package/package.json +11 -4
@@ -14,7 +14,7 @@
14
14
  <!-- JavaScript Section -->
15
15
  <script type="text/javascript">
16
16
  RED.nodes.registerType("on-change-block", {
17
- category: "control",
17
+ category: "bldgblocks control",
18
18
  color: "#301934",
19
19
  defaults: {
20
20
  name: { value: "" },
@@ -26,7 +26,7 @@
26
26
  <!-- JavaScript Section -->
27
27
  <script type="text/javascript">
28
28
  RED.nodes.registerType("oneshot-block", {
29
- category: "control",
29
+ category: "bldgblocks control",
30
30
  color: "#301934",
31
31
  defaults: {
32
32
  name: { value: "" },
@@ -11,7 +11,7 @@
11
11
 
12
12
  <script type="text/javascript">
13
13
  RED.nodes.registerType("or-block", {
14
- category: "control",
14
+ category: "bldgblocks control",
15
15
  color: "#301934",
16
16
  defaults: {
17
17
  name: { value: "" },
@@ -65,7 +65,7 @@
65
65
  <!-- JavaScript Section -->
66
66
  <script type="text/javascript">
67
67
  RED.nodes.registerType("pid-block", {
68
- category: "control",
68
+ category: "bldgblocks control",
69
69
  color: "#301934",
70
70
  defaults: {
71
71
  name: { value: "" },
@@ -9,7 +9,7 @@
9
9
  <!-- JavaScript Section: Registers the node and handles editor logic -->
10
10
  <script type="text/javascript">
11
11
  RED.nodes.registerType("priority-block", {
12
- category: "control",
12
+ category: "bldgblocks control",
13
13
  color: "#301934",
14
14
  defaults: {
15
15
  name: { value: "" }
@@ -29,7 +29,7 @@
29
29
  <!-- JavaScript Section -->
30
30
  <script type="text/javascript">
31
31
  RED.nodes.registerType("rate-limit-block", {
32
- category: "control",
32
+ category: "bldgblocks control",
33
33
  color: "#301934",
34
34
  defaults: {
35
35
  name: { value: "" },
@@ -42,7 +42,7 @@
42
42
  outputs: 1,
43
43
  inputLabels: ["value"],
44
44
  outputLabels: ["processed value"],
45
- icon: "font-awesome/fa-tachometer-alt",
45
+ icon: "font-awesome/fa-circle-up",
46
46
  paletteLabel: "rate limit",
47
47
  label: function() {
48
48
  return this.name || this.mode || "rate limit";
@@ -29,7 +29,7 @@
29
29
 
30
30
  <script type="text/javascript">
31
31
  RED.nodes.registerType("rate-of-change-block", {
32
- category: "control",
32
+ category: "bldgblocks control",
33
33
  color: "#301934",
34
34
  defaults: {
35
35
  name: { value: "" },
@@ -168,7 +168,9 @@ module.exports = function(RED) {
168
168
 
169
169
  // Calculate rate of change (temperature per time unit)
170
170
  let rate = null;
171
- if (node.runtime.samples.length >= node.runtime.maxSamples) { // Need at least 3 points for good regression
171
+ // Require at least 20% of samples for calculation
172
+ if (node.runtime.samples.length >= node.runtime.maxSamples * 0.20) {
173
+ // Use linear regression for more stable rate calculation
172
174
  const n = node.runtime.samples.length;
173
175
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
174
176
 
@@ -192,7 +194,8 @@ module.exports = function(RED) {
192
194
 
193
195
  // Calculate regression sums
194
196
  node.runtime.samples.forEach((sample, i) => {
195
- const x = (sample.timestamp - baseTime) / timeScale; // time in selected units
197
+ // Time in selected units
198
+ const x = (sample.timestamp - baseTime) / timeScale;
196
199
  const y = sample.value;
197
200
 
198
201
  sumX += x;
@@ -7,6 +7,7 @@
7
7
  <div class="form-row">
8
8
  <label for="node-input-precision" title="Rounding precision (0.1 for tenths, 0.5 for halves, 1.0 for whole numbers)"><i class="fa fa-ruler"></i> Precision</label>
9
9
  <select id="node-input-precision">
10
+ <option value="0.01">0.01 (hundreths)</option>
10
11
  <option value="0.1">0.1 (tenths)</option>
11
12
  <option value="0.5">0.5 (halves)</option>
12
13
  <option value="1.0">1.0 (whole)</option>
@@ -17,11 +18,11 @@
17
18
  <!-- JavaScript Section -->
18
19
  <script type="text/javascript">
19
20
  RED.nodes.registerType("round-block", {
20
- category: "control",
21
+ category: "bldgblocks control",
21
22
  color: "#301934",
22
23
  defaults: {
23
24
  name: { value: "" },
24
- precision: { value: "1.0", required: true }
25
+ precision: { value: "0.01", required: true }
25
26
  },
26
27
  inputs: 1,
27
28
  outputs: 1,
@@ -42,17 +43,17 @@ Rounds a float in `msg.payload` to the nearest configurable precision (tenth, ha
42
43
  ### Inputs
43
44
  : payload (number) : Float to round.
44
45
  : context (string, optional) : Action (`"precision"` to set precision). Unknown `msg.context` values are ignored.
45
- : payload (string, for `"precision"`) : Precision value (`"0.1"`, `"0.5"`, `"1.0"`).
46
+ : payload (string, for `"precision"`) : Precision value
46
47
 
47
48
  ### Outputs
48
49
  : msg : Original message with `msg.payload` rounded to configured precision.
49
50
 
50
51
  ### Properties
51
52
  : name (string) : Display name in editor.
52
- : precision (string) : Rounding precision (`"0.1"`, `"0.5"`, `"1.0"`).
53
+ : precision (string) : Rounding precision
53
54
 
54
55
  ### Details
55
- Rounds a float in `msg.payload` to the nearest tenth (0.1), half (0.5), or whole number (1.0), set via editor or `msg.context = "precision"`.
56
+ Rounds a float in `msg.payload` to the nearest hundreth (0.01), tenth (0.1), half (0.5), or whole number (1.0), set via editor or `msg.context = "precision"`.
56
57
 
57
58
  Operates as a passthrough node, modifying `msg.payload` and forwarding the original message.
58
59
 
@@ -10,7 +10,7 @@ module.exports = function(RED) {
10
10
  };
11
11
 
12
12
  // Validate initial config
13
- const validPrecisions = ["0.1", "0.5", "1.0"];
13
+ const validPrecisions = ["0.01", "0.1", "0.5", "1.0"];
14
14
  if (!validPrecisions.includes(node.runtime.precision)) {
15
15
  node.runtime.precision = "1.0";
16
16
  node.status({ fill: "red", shape: "ring", text: "invalid precision, using 1.0" });
@@ -66,7 +66,9 @@ module.exports = function(RED) {
66
66
  // Round based on precision
67
67
  let result;
68
68
  const precision = parseFloat(node.runtime.precision);
69
- if (precision === 0.1) {
69
+ if (precision === 0.01) {
70
+ result = Math.round(input * 100) / 100;
71
+ } else if (precision === 0.1) {
70
72
  result = Math.round(input * 10) / 10;
71
73
  } else if (precision === 0.5) {
72
74
  result = Math.round(input / 0.5) * 0.5;
@@ -75,7 +77,7 @@ module.exports = function(RED) {
75
77
  }
76
78
 
77
79
  msg.payload = result;
78
- node.status({ fill: "blue", shape: "dot", text: `in: ${input.toFixed(2)}, out: ${result.toFixed(2)}` });
80
+ node.status({ fill: "blue", shape: "dot", text: `in: ${input.toFixed(2)}, out: ${result}` });
79
81
  send(msg);
80
82
  if (done) done();
81
83
  });
@@ -26,7 +26,7 @@
26
26
  <!-- JavaScript Section -->
27
27
  <script type="text/javascript">
28
28
  RED.nodes.registerType("saw-tooth-wave-block", {
29
- category: "control",
29
+ category: "bldgblocks control",
30
30
  color: "#301934",
31
31
  defaults: {
32
32
  name: { value: "" },
@@ -39,7 +39,7 @@
39
39
  outputs: 1,
40
40
  inputLabels: ["input"],
41
41
  outputLabels: ["output"],
42
- icon: "font-awesome/fa-wave-square",
42
+ icon: "font-awesome/fa-tree",
43
43
  paletteLabel: "saw tooth wave",
44
44
  label: function() {
45
45
  return this.name || "saw tooth wave";
@@ -29,7 +29,7 @@
29
29
  <!-- JavaScript Section: Registers the node and handles editor logic -->
30
30
  <script type="text/javascript">
31
31
  RED.nodes.registerType("scale-range-block", {
32
- category: "control",
32
+ category: "bldgblocks control",
33
33
  color: "#301934",
34
34
  defaults: {
35
35
  name: { value: "" },
@@ -26,7 +26,7 @@
26
26
  <!-- JavaScript Section -->
27
27
  <script type="text/javascript">
28
28
  RED.nodes.registerType("sine-wave-block", {
29
- category: "control",
29
+ category: "bldgblocks control",
30
30
  color: "#301934",
31
31
  defaults: {
32
32
  name: { value: "" },
@@ -39,7 +39,7 @@
39
39
  outputs: 1,
40
40
  inputLabels: ["input"],
41
41
  outputLabels: ["output"],
42
- icon: "font-awesome/fa-wave-square",
42
+ icon: "font-awesome/fa-circle-up",
43
43
  paletteLabel: "sine wave",
44
44
  label: function() {
45
45
  return this.name || "sine wave";
@@ -30,7 +30,7 @@
30
30
  <!-- JavaScript Section: Registers the node and handles editor logic -->
31
31
  <script type="text/javascript">
32
32
  RED.nodes.registerType("string-builder-block", {
33
- category: "control",
33
+ category: "bldgblocks control",
34
34
  color: "#301934",
35
35
  defaults: {
36
36
  name: { value: "" },
@@ -11,7 +11,7 @@
11
11
 
12
12
  <script type="text/javascript">
13
13
  RED.nodes.registerType("subtract-block", {
14
- category: "control",
14
+ category: "bldgblocks control",
15
15
  color: "#301934",
16
16
  defaults: {
17
17
  name: { value: "" },
@@ -23,7 +23,7 @@
23
23
 
24
24
  <script type="text/javascript">
25
25
  RED.nodes.registerType("thermistor-block", {
26
- category: "control",
26
+ category: "bldgblocks control",
27
27
  color: "#301934",
28
28
  defaults: {
29
29
  name: { value: "" },
@@ -13,7 +13,7 @@
13
13
  <!-- JavaScript Section -->
14
14
  <script type="text/javascript">
15
15
  RED.nodes.registerType("tick-tock-block", {
16
- category: "control",
16
+ category: "bldgblocks control",
17
17
  color: "#301934",
18
18
  defaults: {
19
19
  name: { value: "" },
@@ -23,7 +23,7 @@
23
23
  outputs: 1,
24
24
  inputLabels: ["input"],
25
25
  outputLabels: ["output"],
26
- icon: "font-awesome/fa-wave-square",
26
+ icon: "font-awesome/fa-hourglass-start",
27
27
  paletteLabel: "tick tock",
28
28
  label: function() {
29
29
  return this.name || "tick tock";
@@ -13,7 +13,7 @@
13
13
  <!-- JavaScript Section -->
14
14
  <script type="text/javascript">
15
15
  RED.nodes.registerType("time-sequence-block", {
16
- category: "control",
16
+ category: "bldgblocks control",
17
17
  color: "#301934",
18
18
  defaults: {
19
19
  name: { value: "" },
@@ -26,7 +26,7 @@
26
26
  <!-- JavaScript Section -->
27
27
  <script type="text/javascript">
28
28
  RED.nodes.registerType("triangle-wave-block", {
29
- category: "control",
29
+ category: "bldgblocks control",
30
30
  color: "#301934",
31
31
  defaults: {
32
32
  name: { value: "" },
@@ -39,7 +39,7 @@
39
39
  outputs: 1,
40
40
  inputLabels: ["input"],
41
41
  outputLabels: ["output"],
42
- icon: "font-awesome/fa-wave-square",
42
+ icon: "font-awesome/fa-caret-up",
43
43
  paletteLabel: "triangle wave",
44
44
  label: function() {
45
45
  return this.name || "triangle wave";
@@ -67,7 +67,7 @@
67
67
 
68
68
  <script type="text/javascript">
69
69
  RED.nodes.registerType("tstat-block", {
70
- category: "control",
70
+ category: "bldgblocks control",
71
71
  color: "#301934",
72
72
  defaults: {
73
73
  name: { value: "" },
@@ -7,65 +7,36 @@
7
7
  <div class="form-row">
8
8
  <label for="node-input-unit" title="Unit to append to msg.units"><i class="fa fa-tag"></i> Unit</label>
9
9
  <input type="text" id="node-input-unit">
10
+ <input type="hidden" id="node-input-unitType">
10
11
  </div>
11
12
  </script>
12
13
 
13
14
  <!-- JavaScript Section: Registers the node and handles editor logic -->
14
15
  <script type="text/javascript">
15
16
  RED.nodes.registerType("units-block", {
16
- category: "control",
17
+ category: "bldgblocks control",
17
18
  color: "#301934",
18
19
  defaults: {
19
20
  name: { value: "" },
20
- unit: { value: "°F", required: true }
21
+ unit: { value: "°F" },
22
+ unitType: { value: "str" }
21
23
  },
22
24
  inputs: 1,
23
25
  outputs: 1,
24
26
  inputLabels: ["input"],
25
27
  outputLabels: ["output with units"],
26
- icon: "font-awesome/fa-tag",
28
+ icon: "font-awesome/fa-underline",
27
29
  paletteLabel: "units",
28
30
  label: function() {
29
31
  return this.name || `units ${this.unit}`;
30
32
  },
31
33
  oneditprepare: function() {
32
- // TODO: create a shared configuration file for conversion and units.
33
- const validUnits = [
34
- // Temperature
35
- "°C", "°F", "K", "°R",
36
-
37
- // Humidity/Pressure
38
- "%RH", "Pa", "kPa", "bar", "mbar", "psi", "atm", "inH₂O", "mmH₂O", "inHg",
39
-
40
- // Flow
41
- "CFM", "m³/h", "L/s",
42
-
43
- // Electrical
44
- "V", "mV", "A", "mA", "W", "kW", "hp", "Ω",
45
-
46
- // General/Math
47
- "%",
48
-
49
- // Length
50
- "m", "cm", "mm", "km", "ft", "in",
51
-
52
- // Mass
53
- "kg", "g", "lb",
54
-
55
- // Time
56
- "s", "min", "h",
57
-
58
- // Volume
59
- "L", "mL", "gal",
60
-
61
- // Other
62
- "lx", "cd", "B", "T"
63
- ];
64
34
  $("#node-input-unit").typedInput({
65
35
  default: "str",
66
36
  types: [{
67
37
  value: "str",
68
38
  options: [
39
+ { value: "", label: "Text (none)" },
69
40
  { value: "°C", label: "°C (Celsius)" },
70
41
  { value: "°F", label: "°F (Fahrenheit)" },
71
42
  { value: "K", label: "K (Kelvin)" },
@@ -112,9 +83,8 @@
112
83
  { value: "B", label: "B (Bel)" },
113
84
  { value: "T", label: "T (Tesla)" }
114
85
  ]
115
- }],
116
- typeField: false
117
- }).typedInput("value", this.unit || "°F");
86
+ }], typeField: "#node-input-unitType"
87
+ }).typedInput("type", node.unitType).typedInput("value", node.unit);
118
88
  }
119
89
  });
120
90
  </script>
@@ -1,34 +1,3 @@
1
- const validUnits = [
2
- // Temperature
3
- "°C", "°F", "K", "°R",
4
-
5
- // Humidity/Pressure
6
- "%RH", "Pa", "kPa", "bar", "mbar", "psi", "atm", "inH₂O", "mmH₂O", "inHg",
7
-
8
- // Flow
9
- "CFM", "m³/h", "L/s",
10
-
11
- // Electrical
12
- "V", "mV", "A", "mA", "W", "kW", "hp", "Ω",
13
-
14
- // General/Math
15
- "%",
16
-
17
- // Length
18
- "m", "cm", "mm", "km", "ft", "in",
19
-
20
- // Mass
21
- "kg", "g", "lb",
22
-
23
- // Time
24
- "s", "min", "h",
25
-
26
- // Volume
27
- "L", "mL", "gal",
28
-
29
- // Other
30
- "lx", "cd", "B", "T"
31
- ];
32
1
 
33
2
  module.exports = function(RED) {
34
3
  function UnitsBlockNode(config) {
@@ -41,19 +10,11 @@ module.exports = function(RED) {
41
10
  unit: config.unit
42
11
  };
43
12
 
44
- // Validate configuration
45
- if (!validUnits.includes(node.runtime.unit)) {
46
- node.runtime.unit = "°F";
47
- node.status({ fill: "red", shape: "ring", text: "invalid unit, using °F" });
48
- } else {
49
- node.status({ fill: "green", shape: "dot", text: `in: unit: ${node.runtime.unit}` });
50
- }
51
-
52
13
  node.on("input", function(msg, send, done) {
53
14
  send = send || function() { node.send.apply(node, arguments); };
54
15
 
55
16
  // Validate input
56
- if (!msg || typeof msg !== "object") {
17
+ if (!msg || typeof msg !== "object" || !msg.hasOwnProperty("payload")) {
57
18
  node.status({ fill: "red", shape: "ring", text: "invalid message" });
58
19
 
59
20
  if (done) done();
@@ -65,7 +26,7 @@ module.exports = function(RED) {
65
26
  if (msg.hasOwnProperty("context")) {
66
27
  // Configuration handling
67
28
  if (msg.context === "unit") {
68
- if (typeof msg.payload === "string" && VALID_UNITS.includes(msg.payload)) {
29
+ if (typeof msg.payload === "string") {
69
30
  node.runtime.unit = msg.payload;
70
31
  node.status({ fill: "green", shape: "dot", text: `unit: ${node.runtime.unit}` });
71
32
  } else {
@@ -85,7 +46,7 @@ module.exports = function(RED) {
85
46
  // Process input
86
47
  const payloadPreview = msg.payload !== null ? (typeof msg.payload === "number" ? msg.payload.toFixed(2) : JSON.stringify(msg.payload).slice(0, 20)) : "none";
87
48
 
88
- node.status({ fill: "blue", shape: "dot", text: `in: ${payloadPreview} unit: ${node.runtime.unit}` });
49
+ node.status({ fill: "blue", shape: "dot", text: `in: ${payloadPreview} unit: ${node.runtime.unit !== "" ? node.runtime.unit : "none"}` });
89
50
 
90
51
  msg.units = node.runtime.unit;
91
52
  send(msg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bldgblocks/node-red-contrib-control",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Sedona-inspired control nodes for Node-RED",
5
5
  "keywords": [ "node-red", "sedona", "control", "hvac" ],
6
6
  "files": ["nodes/*.js", "nodes/*.html"],
@@ -31,8 +31,6 @@
31
31
  "edge-block": "nodes/edge-block.js",
32
32
  "enum-switch-block": "nodes/enum-switch-block.js",
33
33
  "frequency-block": "nodes/frequency-block.js",
34
- "global-getter": "nodes/global-getter.js",
35
- "global-setter": "nodes/global-setter.js",
36
34
  "hysteresis-block": "nodes/hysteresis-block.js",
37
35
  "interpolate-block": "nodes/interpolate-block.js",
38
36
  "latch-block": "nodes/latch-block.js",
@@ -63,7 +61,16 @@
63
61
  "time-sequence-block": "nodes/time-sequence-block.js",
64
62
  "triangle-wave-block": "nodes/triangle-wave-block.js",
65
63
  "tstat-block": "nodes/tstat-block.js",
66
- "units-block": "nodes/units-block.js"
64
+ "units-block": "nodes/units-block.js",
65
+ "global-getter": "nodes/global-getter.js",
66
+ "global-setter": "nodes/global-setter.js",
67
+ "history-collector": "nodes/history-collector.js",
68
+ "history-config": "nodes/history-config.js",
69
+ "network-read": "nodes/network-read.js",
70
+ "network-register": "nodes/network-register.js",
71
+ "network-write": "nodes/network-write.js",
72
+ "network-point-registry": "nodes/network-point-registry.js"
73
+
67
74
  }
68
75
  },
69
76
  "author": "buildingblocks",