@bldgblocks/node-red-contrib-control 0.1.4

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 (98) hide show
  1. package/README.md +43 -0
  2. package/nodes/accumulate-block.html +71 -0
  3. package/nodes/accumulate-block.js +104 -0
  4. package/nodes/add-block.html +67 -0
  5. package/nodes/add-block.js +97 -0
  6. package/nodes/analog-switch-block.html +65 -0
  7. package/nodes/analog-switch-block.js +129 -0
  8. package/nodes/and-block.html +64 -0
  9. package/nodes/and-block.js +73 -0
  10. package/nodes/average-block.html +97 -0
  11. package/nodes/average-block.js +137 -0
  12. package/nodes/boolean-switch-block.html +59 -0
  13. package/nodes/boolean-switch-block.js +88 -0
  14. package/nodes/boolean-to-number-block.html +59 -0
  15. package/nodes/boolean-to-number-block.js +45 -0
  16. package/nodes/cache-block.html +69 -0
  17. package/nodes/cache-block.js +106 -0
  18. package/nodes/call-status-block.html +111 -0
  19. package/nodes/call-status-block.js +274 -0
  20. package/nodes/changeover-block.html +234 -0
  21. package/nodes/changeover-block.js +392 -0
  22. package/nodes/comment-block.html +83 -0
  23. package/nodes/comment-block.js +53 -0
  24. package/nodes/compare-block.html +64 -0
  25. package/nodes/compare-block.js +84 -0
  26. package/nodes/contextual-label-block.html +67 -0
  27. package/nodes/contextual-label-block.js +52 -0
  28. package/nodes/convert-block.html +179 -0
  29. package/nodes/convert-block.js +289 -0
  30. package/nodes/count-block.html +57 -0
  31. package/nodes/count-block.js +92 -0
  32. package/nodes/debounce-block.html +64 -0
  33. package/nodes/debounce-block.js +140 -0
  34. package/nodes/delay-block.html +104 -0
  35. package/nodes/delay-block.js +180 -0
  36. package/nodes/divide-block.html +65 -0
  37. package/nodes/divide-block.js +123 -0
  38. package/nodes/edge-block.html +71 -0
  39. package/nodes/edge-block.js +120 -0
  40. package/nodes/frequency-block.html +55 -0
  41. package/nodes/frequency-block.js +140 -0
  42. package/nodes/hysteresis-block.html +131 -0
  43. package/nodes/hysteresis-block.js +142 -0
  44. package/nodes/interpolate-block.html +74 -0
  45. package/nodes/interpolate-block.js +141 -0
  46. package/nodes/load-sequence-block.html +134 -0
  47. package/nodes/load-sequence-block.js +272 -0
  48. package/nodes/max-block.html +76 -0
  49. package/nodes/max-block.js +103 -0
  50. package/nodes/memory-block.html +90 -0
  51. package/nodes/memory-block.js +241 -0
  52. package/nodes/min-block.html +77 -0
  53. package/nodes/min-block.js +106 -0
  54. package/nodes/minmax-block.html +89 -0
  55. package/nodes/minmax-block.js +119 -0
  56. package/nodes/modulo-block.html +73 -0
  57. package/nodes/modulo-block.js +126 -0
  58. package/nodes/multiply-block.html +63 -0
  59. package/nodes/multiply-block.js +115 -0
  60. package/nodes/negate-block.html +55 -0
  61. package/nodes/negate-block.js +91 -0
  62. package/nodes/nullify-block.html +111 -0
  63. package/nodes/nullify-block.js +78 -0
  64. package/nodes/on-change-block.html +79 -0
  65. package/nodes/on-change-block.js +191 -0
  66. package/nodes/oneshot-block.html +96 -0
  67. package/nodes/oneshot-block.js +169 -0
  68. package/nodes/or-block.html +64 -0
  69. package/nodes/or-block.js +73 -0
  70. package/nodes/pid-block.html +205 -0
  71. package/nodes/pid-block.js +407 -0
  72. package/nodes/priority-block.html +66 -0
  73. package/nodes/priority-block.js +239 -0
  74. package/nodes/rate-limit-block.html +99 -0
  75. package/nodes/rate-limit-block.js +221 -0
  76. package/nodes/round-block.html +73 -0
  77. package/nodes/round-block.js +89 -0
  78. package/nodes/saw-tooth-wave-block.html +87 -0
  79. package/nodes/saw-tooth-wave-block.js +161 -0
  80. package/nodes/scale-range-block.html +90 -0
  81. package/nodes/scale-range-block.js +137 -0
  82. package/nodes/sine-wave-block.html +88 -0
  83. package/nodes/sine-wave-block.js +142 -0
  84. package/nodes/subtract-block.html +64 -0
  85. package/nodes/subtract-block.js +103 -0
  86. package/nodes/thermistor-block.html +81 -0
  87. package/nodes/thermistor-block.js +146 -0
  88. package/nodes/tick-tock-block.html +66 -0
  89. package/nodes/tick-tock-block.js +110 -0
  90. package/nodes/time-sequence-block.html +67 -0
  91. package/nodes/time-sequence-block.js +144 -0
  92. package/nodes/triangle-wave-block.html +86 -0
  93. package/nodes/triangle-wave-block.js +154 -0
  94. package/nodes/tstat-block.html +311 -0
  95. package/nodes/tstat-block.js +499 -0
  96. package/nodes/units-block.html +150 -0
  97. package/nodes/units-block.js +106 -0
  98. package/package.json +73 -0
@@ -0,0 +1,83 @@
1
+ <script type="text/html" data-template-name="comment-block">
2
+ <div class="form-row">
3
+ <label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
4
+ <input type="text" id="node-input-name" placeholder="Name">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-input-comment" title="Comment shown in status when Default is selected (max 100 characters)"><i class="fa fa-comment"></i> Comment</label>
8
+ <input type="text" id="node-input-comment" placeholder="Enter comment (max 100 chars)" maxlength="100">
9
+ </div>
10
+ <div class="form-row">
11
+ <label for="node-input-statusDisplay" title="Select what to display in the node status"><i class="fa fa-eye"></i> Status Display</label>
12
+ <select id="node-input-statusDisplay">
13
+ <option value="default">Comment to status (Default)</option>
14
+ <option value="name">Name to status</option>
15
+ <option value="none">No status</option>
16
+ </select>
17
+ </div>
18
+ </script>
19
+
20
+ <script type="text/javascript">
21
+ RED.nodes.registerType("comment-block", {
22
+ category: "control",
23
+ color: "#301934",
24
+ defaults: {
25
+ name: { value: "comment" },
26
+ comment: { value: "No comment", validate: function(v) { return v.length < 100 } },
27
+ statusDisplay: { value: "default" }
28
+ },
29
+ inputs: 1,
30
+ outputs: 1,
31
+ inputLabels: ["input"],
32
+ outputLabels: ["output"],
33
+ icon: "font-awesome/fa-comment",
34
+ paletteLabel: "comment",
35
+ label: function() {
36
+ return this.name || this.comment || "comment";
37
+ },
38
+ oneditsave: function() {
39
+ // Update status immediately
40
+ const node = RED.nodes.getNode(this.id);
41
+ let status = {};
42
+ switch (node.statusDisplay) {
43
+ case "default":
44
+ status = { fill: "green", shape: "dot", text: node.comment || "No comment set" };
45
+ break;
46
+ case "name":
47
+ status = { fill: "green", shape: "dot", text: node.name || "comment" };
48
+ break;
49
+ case "none":
50
+ break;
51
+ default:
52
+ }
53
+
54
+ node.status(status);
55
+ }
56
+ });
57
+ </script>
58
+
59
+ <script type="text/markdown" data-help-name="comment-block">
60
+ Displays a configurable comment, node name, or no status persistently and on input.
61
+
62
+ ### Inputs
63
+ : payload (any) : Passthrough.
64
+
65
+ ### Outputs
66
+ : payload (any) : Passthrough.
67
+
68
+ ### Details
69
+ Displays a status (comment, node name, or none) on node creation, editor saves, and input messages.
70
+
71
+ Used for flow annotations or debugging without requiring input to trigger status.
72
+
73
+ ### Status
74
+ - Green (dot): Configuration update
75
+ - Blue (dot): State changed
76
+ - Blue (ring): State unchanged
77
+ - Red (ring): Error
78
+ - Yellow: Warning
79
+
80
+ ### References
81
+ - [Node-RED Documentation](https://nodered.org/docs/)
82
+ - [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
83
+ </script>
@@ -0,0 +1,53 @@
1
+ module.exports = function(RED) {
2
+ function CommentBlockNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ const node = this;
5
+
6
+ // Initialize properties
7
+ node.name = config.name;
8
+ node.comment = config.comment;
9
+ node.statusDisplay = config.statusDisplay;
10
+
11
+ // Ensure comment is within 100 characters
12
+ if (node.comment.length > 100) {
13
+ node.comment = node.comment.substring(0, 100);
14
+ }
15
+
16
+ // Status helper function
17
+ const updateStatus = function() {
18
+ switch (node.statusDisplay) {
19
+ case "default":
20
+ return { fill: "green", shape: "dot", text: node.comment || "No comment set" };
21
+ case "name":
22
+ return { fill: "green", shape: "dot", text: node.name || "comment" };
23
+ case "none":
24
+ default:
25
+ return {};
26
+ }
27
+ };
28
+
29
+ node.status(updateStatus());
30
+
31
+ node.on("input", function(msg, send, done) {
32
+ send = send || function() { node.send.apply(node, arguments); };
33
+
34
+ // Guard against invalid msg
35
+ if (!msg) {
36
+ node.status({ fill: "red", shape: "ring", text: "invalid message" });
37
+ if (done) done();
38
+ return;
39
+ }
40
+
41
+ node.status(updateStatus());
42
+
43
+ send(msg);
44
+ if (done) done();
45
+ });
46
+
47
+ node.on("close", function(done) {
48
+ done();
49
+ });
50
+ }
51
+
52
+ RED.nodes.registerType("comment-block", CommentBlockNode);
53
+ };
@@ -0,0 +1,64 @@
1
+ <!-- UI Template Section: Defines the edit dialog -->
2
+ <script type="text/html" data-template-name="compare-block">
3
+ <div class="form-row">
4
+ <label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
5
+ <input type="text" id="node-input-name" placeholder="Name">
6
+ </div>
7
+ <div class="form-row">
8
+ <label for="node-input-setpoint" title="Default setpoint value for comparison (number)"><i class="fa fa-crosshairs"></i> Setpoint</label>
9
+ <input type="number" id="node-input-setpoint" placeholder="50" step="any">
10
+ </div>
11
+ </script>
12
+
13
+ <!-- JavaScript Section: Registers the node and handles editor logic -->
14
+ <script type="text/javascript">
15
+ RED.nodes.registerType("compare-block", {
16
+ category: "control",
17
+ color: "#301934",
18
+ defaults: {
19
+ name: { value: "" },
20
+ setpoint: { value: 50, required: true, validate: function(v) { return !isNaN(+v) && isFinite(+v); } }
21
+ },
22
+ inputs: 1,
23
+ outputs: 3,
24
+ outputLabels: ["greater than", "equal to", "less than"],
25
+ inputLabels: ["input"],
26
+ icon: "font-awesome/fa-balance-scale",
27
+ paletteLabel: "compare",
28
+ label: function() {
29
+ return this.name || "compare";
30
+ }
31
+ });
32
+ </script>
33
+
34
+ <!-- Help Section -->
35
+ <script type="text/markdown" data-help-name="compare-block">
36
+ Compares a numeric input to a setpoint, outputting three boolean signals.
37
+
38
+ ### Inputs
39
+ : context (string) : Configuration commands - setpoint (`"setpoint"`). Unmatched values trigger error.
40
+ : payload (number) : Input number to compare, or setpoint value for config.
41
+
42
+ ### Outputs
43
+ : greater than (boolean) : `true` if input > setpoint, else `false`.
44
+ : equal to (boolean) : `true` if input = setpoint, else `false`.
45
+ : less than (boolean) : `true` if input < setpoint, else `false`.
46
+
47
+ ### Properties
48
+ : setpoint (number) : Value for comparison.
49
+
50
+ ### Details
51
+ Compares `msg.payload` (number) to a `setpoint` (default: 50), outputting three messages to separate ports:
52
+ `true` for greater than, equal to, or less than the setpoint, respectively; `false` otherwise.
53
+
54
+ ### Status
55
+ - Green (dot): Configuration update
56
+ - Blue (dot): State changed
57
+ - Blue (ring): State unchanged
58
+ - Red (ring): Error
59
+ - Yellow (ring): Warning
60
+
61
+ ### References
62
+ - [Node-RED Documentation](https://nodered.org/docs/)
63
+ - [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
64
+ </script>
@@ -0,0 +1,84 @@
1
+ module.exports = function(RED) {
2
+ function CompareBlockNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ const node = this;
5
+
6
+ node.setpoint = Number(config.setpoint);
7
+
8
+ node.on("input", function(msg, send, done) {
9
+ send = send || function() { node.send.apply(node, arguments); };
10
+
11
+ // Guard against invalid message
12
+ if (!msg) {
13
+ node.status({ fill: "red", shape: "ring", text: "invalid message" });
14
+ if (done) done();
15
+ return;
16
+ }
17
+
18
+ // Handle context updates
19
+ if (msg.hasOwnProperty("context")) {
20
+ if (!msg.hasOwnProperty("payload")) {
21
+ node.status({ fill: "red", shape: "ring", text: "missing payload for setpoint" });
22
+ if (done) done();
23
+ return;
24
+ }
25
+
26
+ if (msg.context === "setpoint") {
27
+ const setpointValue = parseFloat(msg.payload);
28
+ if (!isNaN(setpointValue) && isFinite(setpointValue)) {
29
+ node.setpoint = setpointValue;
30
+ node.status({ fill: "green", shape: "dot", text: `setpoint: ${setpointValue.toFixed(2)}` });
31
+ } else {
32
+ node.status({ fill: "red", shape: "ring", text: "invalid setpoint" });
33
+ }
34
+ if (done) done();
35
+ return;
36
+ } else {
37
+ node.status({ fill: "yellow", shape: "ring", text: "unknown context" });
38
+ if (done) done("Unknown context");
39
+ return;
40
+ }
41
+ }
42
+
43
+ // Validate input
44
+ if (!msg.hasOwnProperty("payload")) {
45
+ node.status({ fill: "red", shape: "ring", text: "missing input" });
46
+ if (done) done();
47
+ return;
48
+ }
49
+
50
+ const inputValue = parseFloat(msg.payload);
51
+ if (isNaN(inputValue) || !isFinite(inputValue)) {
52
+ node.status({ fill: "red", shape: "ring", text: "invalid input" });
53
+ if (done) done();
54
+ return;
55
+ }
56
+
57
+ // Compare input to setpoint
58
+ const greater = inputValue > node.setpoint;
59
+ const equal = inputValue === node.setpoint;
60
+ const less = inputValue < node.setpoint;
61
+ const outputs = [
62
+ { payload: greater },
63
+ { payload: equal },
64
+ { payload: less }
65
+ ];
66
+
67
+ node.status({
68
+ fill: "blue",
69
+ shape: "dot",
70
+ text: `in: ${inputValue.toFixed(2)}, sp: ${node.setpoint.toFixed(2)}, out: [${greater}, ${equal}, ${less}]`
71
+ });
72
+
73
+ send(outputs);
74
+
75
+ if (done) done();
76
+ });
77
+
78
+ node.on("close", function(done) {
79
+ done();
80
+ });
81
+ }
82
+
83
+ RED.nodes.registerType("compare-block", CompareBlockNode);
84
+ };
@@ -0,0 +1,67 @@
1
+ <script type="text/html" data-template-name="contextual-label-block">
2
+ <div class="form-row">
3
+ <label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
4
+ <input type="text" id="node-input-name" placeholder="Name">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-input-contextPropertyName" title="Context property to set (e.g., priority, clear, in1)"><i class="fa fa-key"></i> Context Property</label>
8
+ <input type="text" id="node-input-contextPropertyName" placeholder="e.g., priority">
9
+ </div>
10
+ <div class="form-row">
11
+ <label for="node-input-removeLabel" title="Remove msg.context instead of setting it"><i class="fa fa-trash"></i> Remove Context</label>
12
+ <input type="checkbox" id="node-input-removeLabel" style="width: auto; vertical-align: middle;">
13
+ </div>
14
+ </script>
15
+
16
+ <script type="text/javascript">
17
+ RED.nodes.registerType("contextual-label-block", {
18
+ category: "control",
19
+ color: "#301934",
20
+ defaults: {
21
+ name: { value: "" },
22
+ contextPropertyName: {
23
+ value: "in1",
24
+ required: true,
25
+ validate: function(v) { return v && typeof v === "string" && v.trim().length > 0; }
26
+ },
27
+ removeLabel: { value: false }
28
+ },
29
+ inputs: 1,
30
+ outputs: 1,
31
+ inputLabels: ["input"],
32
+ outputLabels: ["output"],
33
+ icon: "font-awesome/fa-tag",
34
+ paletteLabel: "contextual label",
35
+ label: function() {
36
+ if (this.name) return this.name;
37
+ return this.removeLabel ? "remove context" : `🛈 ${this.contextPropertyName || "context"}`;
38
+ },
39
+ oneditprepare: function() {
40
+ $("#node-input-removeLabel").prop("checked", this.removeLabel || false);
41
+ }
42
+ });
43
+ </script>
44
+
45
+ <script type="text/markdown" data-help-name="contextual-label-block">
46
+ Sets or removes the msg.context property in a message.
47
+
48
+ ### Inputs
49
+ : payload (any) : Input message payload, passed through unchanged.
50
+
51
+ ### Outputs
52
+ : context (string) : Configured context property (e.g., `priority`, `clear`, `in1`) if set, or absent if removed.
53
+ : payload (any) : Original message object with context added or deleted.
54
+
55
+ ### Details
56
+ Either sets `msg.context` to a configured property (e.g., `priority`, `clear`, `in1`) or removes `msg.context` based on the `Remove Context` toggle.
57
+
58
+ ### Status
59
+ - Green (dot): Configuration updates
60
+ - Blue (dot): Outputs when input changes
61
+ - Blue (ring): Outputs when input unchanged
62
+ - Red (ring): Errors
63
+
64
+ ### References
65
+ - [Node-RED Documentation](https://nodered.org/docs/)
66
+ - [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
67
+ </script>
@@ -0,0 +1,52 @@
1
+ module.exports = function(RED) {
2
+ function ContextualLabelBlockNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ const node = this;
5
+
6
+ node.contextPropertyName = config.contextPropertyName || "in1";
7
+ node.removeLabel = config.removeLabel || false;
8
+
9
+ node.status({
10
+ fill: "green",
11
+ shape: "dot",
12
+ text: `mode: ${node.removeLabel ? "remove" : "set"}, property: ${node.contextPropertyName}`
13
+ });
14
+
15
+ node.on("input", function(msg, send, done) {
16
+ send = send || function() { node.send.apply(node, arguments); };
17
+
18
+ if (!msg) {
19
+ node.status({ fill: "red", shape: "ring", text: "missing message" });
20
+ node.warn("Missing message");
21
+ if (done) done();
22
+ return;
23
+ }
24
+
25
+ // Set or remove context property
26
+ if (node.removeLabel) {
27
+ delete msg.context;
28
+ node.status({
29
+ fill: "blue",
30
+ shape: "dot",
31
+ text: `in: ${msg.payload}, out: removed context`
32
+ });
33
+ } else {
34
+ msg.context = node.contextPropertyName;
35
+ node.status({
36
+ fill: "blue",
37
+ shape: "dot",
38
+ text: `in: ${msg.payload}, out: ${node.contextPropertyName}`
39
+ });
40
+ }
41
+
42
+ send(msg);
43
+ if (done) done();
44
+ });
45
+
46
+ node.on("close", function(done) {
47
+ done();
48
+ });
49
+ }
50
+
51
+ RED.nodes.registerType("contextual-label-block", ContextualLabelBlockNode);
52
+ };
@@ -0,0 +1,179 @@
1
+ <script type="text/html" data-template-name="convert-block">
2
+ <div class="form-row">
3
+ <label for="node-input-name" title="Display name shown on the canvas"><i class="fa fa-tag"></i> Name</label>
4
+ <input type="text" id="node-input-name" placeholder="Name">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-input-conversion" title="Select the unit conversion to perform"><i class="fa fa-exchange"></i> Conversion</label>
8
+ <select id="node-input-conversion">
9
+ <optgroup label="Temperature">
10
+ <option value="C to F">C to F (Celsius to Fahrenheit)</option>
11
+ <option value="F to C">F to C (Fahrenheit to Celsius)</option>
12
+ <option value="K to C">K to C (Kelvin to Celsius)</option>
13
+ <option value="C to K">C to K (Celsius to Kelvin)</option>
14
+ <option value="K to F">K to F (Kelvin to Fahrenheit)</option>
15
+ <option value="F to K">F to K (Fahrenheit to Kelvin)</option>
16
+ <option value="R to F">R to F (Rankine to Fahrenheit)</option>
17
+ <option value="F to R">F to R (Fahrenheit to Rankine)</option>
18
+ </optgroup>
19
+ <optgroup label="Math">
20
+ <option value="decimal to %">Decimal to Percent</option>
21
+ <option value="% to decimal">Percent to Decimal</option>
22
+ <option value="rad to deg">Radians to Degrees</option>
23
+ <option value="deg to rad">Degrees to Radians</option>
24
+ </optgroup>
25
+ <optgroup label="Pressure">
26
+ <option value="Pa to inH₂O">Pa to inH₂O (Pascal to Inches of Water)</option>
27
+ <option value="inH₂O to Pa">inH₂O to Pa (Inches of Water to Pascal)</option>
28
+ <option value="Pa to inHg">Pa to inHg (Pascal to Inches of Mercury)</option>
29
+ <option value="inHg to Pa">inHg to Pa (Inches of Mercury to Pascal)</option>
30
+ <option value="Pa to bar">Pa to bar (Pascal to Bar)</option>
31
+ <option value="bar to Pa">bar to Pa (Bar to Pascal)</option>
32
+ <option value="Pa to psi">Pa to psi (Pascal to PSI)</option>
33
+ <option value="psi to Pa">psi to Pa (PSI to Pascal)</option>
34
+ </optgroup>
35
+ <optgroup label="Length">
36
+ <option value="m to ft">m to ft (Meters to Feet)</option>
37
+ <option value="ft to m">ft to m (Feet to Meters)</option>
38
+ <option value="m to in">m to in (Meters to Inches)</option>
39
+ <option value="in to m">in to m (Inches to Meters)</option>
40
+ <option value="mm to in">mm to in (Millimeters to Inches)</option>
41
+ <option value="in to mm">in to mm (Inches to Millimeters)</option>
42
+ </optgroup>
43
+ <optgroup label="Mass & Volume">
44
+ <option value="kg to lb">kg to lb (Kilograms to Pounds)</option>
45
+ <option value="lb to kg">lb to kg (Pounds to Kilograms)</option>
46
+ <option value="L to gal">L to gal (Liters to Gallons)</option>
47
+ <option value="gal to L">gal to L (Gallons to Liters)</option>
48
+ </optgroup>
49
+ <optgroup label="Power & Time">
50
+ <option value="kW to hp">kW to hp (Kilowatts to Horsepower)</option>
51
+ <option value="hp to kW">hp to kW (Horsepower to Kilowatts)</option>
52
+ <option value="s to min">s to min (Seconds to Minutes)</option>
53
+ <option value="min to s">min to s (Minutes to Seconds)</option>
54
+ </optgroup>
55
+ </select>
56
+ </div>
57
+ </script>
58
+
59
+ <script type="text/javascript">
60
+ RED.nodes.registerType("convert-block", {
61
+ category: "control",
62
+ color: "#301934",
63
+ defaults: {
64
+ name: { value: "" },
65
+ conversion: { value: "C to F", required: true }
66
+ },
67
+ inputs: 1,
68
+ outputs: 1,
69
+ inputLabels: ["input"],
70
+ outputLabels: ["converted output"],
71
+ icon: "font-awesome/fa-exchange",
72
+ paletteLabel: "convert",
73
+ label: function() {
74
+ return this.name || `Convert (${this.conversion})`;
75
+ },
76
+ oneditvalidate: function() {
77
+ const conversion = $("#node-input-conversion").val();
78
+ const validConversions = [
79
+ "F to C",
80
+ "C to F",
81
+ "K to C",
82
+ "C to K",
83
+ "K to F",
84
+ "F to K",
85
+ "R to F",
86
+ "F to R",
87
+ "decimal to %",
88
+ "% to decimal",
89
+ "Pa to inH₂O",
90
+ "inH₂O to Pa",
91
+ "Pa to inHg",
92
+ "inHg to Pa",
93
+ "Pa to bar",
94
+ "bar to Pa",
95
+ "Pa to psi",
96
+ "psi to Pa",
97
+ "m to ft",
98
+ "ft to m",
99
+ "m to in",
100
+ "in to m",
101
+ "mm to in",
102
+ "in to mm",
103
+ "kg to lb",
104
+ "lb to kg",
105
+ "L to gal",
106
+ "gal to L",
107
+ "kW to hp",
108
+ "hp to kW",
109
+ "rad to deg",
110
+ "deg to rad",
111
+ "s to min",
112
+ "min to s"
113
+ ];
114
+ return validConversions.includes(conversion);
115
+ }
116
+ });
117
+ </script>
118
+
119
+ <script type="text/markdown" data-help-name="convert-block">
120
+ Converts numeric input payload between temperature or pressure units.
121
+
122
+ ### Inputs
123
+ : context (string) : Configures the conversion (`"conversion"`). Unmatched values ignored.
124
+ : payload (number) : Numeric value to convert
125
+
126
+ ### Outputs
127
+ : payload (number) : Converted value (e.g., °C to °F, Pa to inH₂O).
128
+
129
+ ### Details
130
+ Converts `msg.payload` between units based on the selected conversion.
131
+
132
+ ### validConversions = [
133
+ "F to C",
134
+ "C to F",
135
+ "K to C",
136
+ "C to K",
137
+ "K to F",
138
+ "F to K",
139
+ "R to F",
140
+ "F to R",
141
+ "decimal to %",
142
+ "% to decimal",
143
+ "Pa to inH₂O",
144
+ "inH₂O to Pa",
145
+ "Pa to inHg",
146
+ "inHg to Pa",
147
+ "Pa to bar",
148
+ "bar to Pa",
149
+ "Pa to psi",
150
+ "psi to Pa",
151
+ "m to ft",
152
+ "ft to m",
153
+ "m to in",
154
+ "in to m",
155
+ "mm to in",
156
+ "in to mm",
157
+ "kg to lb",
158
+ "lb to kg",
159
+ "L to gal",
160
+ "gal to L",
161
+ "kW to hp",
162
+ "hp to kW",
163
+ "rad to deg",
164
+ "deg to rad",
165
+ "s to min",
166
+ "min to s"
167
+ ];
168
+
169
+ ### Status
170
+ - Green (dot): Configuration update
171
+ - Blue (dot): State changed
172
+ - Blue (ring): State unchanged
173
+ - Red (ring): Error
174
+ - Yellow (ring): Warning
175
+
176
+ ### References
177
+ - [Node-RED Documentation](https://nodered.org/docs/)
178
+ - [GitHub Repository](https://github.com/BldgBlocks/node-red-contrib-buildingblocks-control.git)
179
+ </script>