node-red-contrib-difference 1.0.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.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # node-red-contrib-difference
2
+
3
+ Node-RED Difference Calculator - calculates the difference between two numeric input values.
4
+
5
+ ## Features
6
+
7
+ - Calculates A - B from two input values
8
+ - Configurable topic names for inputs
9
+ - Absolute value mode (|A - B|)
10
+ - Configurable decimal places (0-10)
11
+ - Outputs detailed input info
12
+
13
+ ## Behavior
14
+
15
+ - Input A identified by configurable topic (default: "a")
16
+ - Input B identified by configurable topic (default: "b")
17
+ - Result = A - B
18
+ - Output triggered on every new message once both values are known
19
+
20
+ ## Example
21
+
22
+ ```
23
+ topic="a", payload=24.5 → stored as A
24
+ topic="b", payload=18.2 → stored as B
25
+ Output: 24.5 - 18.2 = 6.3
26
+ ```
27
+
28
+ ## Configuration
29
+
30
+ | Property | Description | Default |
31
+ |----------|-------------|---------|
32
+ | Topic A | Topic name for input A | "a" |
33
+ | Topic B | Topic name for input B | "b" |
34
+ | Absolute | Output absolute value | false |
35
+ | Decimals | Number of decimal places | 2 |
36
+
37
+ ## Input
38
+
39
+ - `msg.payload` - Numeric value
40
+ - `msg.topic` - **Required** - Must match Topic A or Topic B
41
+
42
+ ### Runtime Override
43
+
44
+ | Property | Description |
45
+ |----------|-------------|
46
+ | `msg.reset` | `true` = reset both values |
47
+
48
+ ## Output
49
+
50
+ ```json
51
+ {
52
+ "payload": 6.3,
53
+ "inputs": {
54
+ "a": { "topic": "a", "value": 24.5 },
55
+ "b": { "topic": "b", "value": 18.2 },
56
+ "result": 6.3,
57
+ "absolute": false
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Use Cases
63
+
64
+ | Application | Description |
65
+ |-------------|-------------|
66
+ | Temperature | Difference between two sensors |
67
+ | Energy | Consumption difference between readings |
68
+ | Level | Water level difference |
69
+
70
+ ## Node Status
71
+
72
+ - **a(24.5) - b(18.2) = 6.3** - Result calculated
73
+ - **Waiting for a...** - Input A missing
74
+ - **Waiting for a and b...** - Both inputs missing
75
+
76
+ ## Author
77
+
78
+ sr.rpo
79
+
80
+ ## License
81
+
82
+ MIT
@@ -0,0 +1,137 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("difference", {
3
+ category: "function",
4
+ color: "#D4600A",
5
+ defaults: {
6
+ name: { value: "" },
7
+ topicA: { value: "a" },
8
+ topicB: { value: "b" },
9
+ absolute: { value: false },
10
+ decimals: { value: 2 },
11
+ },
12
+ inputs: 1,
13
+ outputs: 1,
14
+ icon: "font-awesome/fa-minus",
15
+ label: function () {
16
+ return this.name || "difference";
17
+ },
18
+ paletteLabel: "difference",
19
+ });
20
+ </script>
21
+
22
+ <script type="text/html" data-template-name="difference">
23
+ <div class="form-row">
24
+ <label for="node-input-name"
25
+ ><i class="fa fa-tag"></i>
26
+ <span data-i18n="difference.label.name">Name</span></label
27
+ >
28
+ <input
29
+ type="text"
30
+ id="node-input-name"
31
+ data-i18n="[placeholder]difference.label.name"
32
+ />
33
+ </div>
34
+ <div class="form-row">
35
+ <label for="node-input-topicA"
36
+ ><i class="fa fa-tag"></i>
37
+ <span data-i18n="difference.label.topicA">Topic A</span></label
38
+ >
39
+ <input type="text" id="node-input-topicA" style="width: 150px;" />
40
+ </div>
41
+ <div class="form-row">
42
+ <label for="node-input-topicB"
43
+ ><i class="fa fa-tag"></i>
44
+ <span data-i18n="difference.label.topicB">Topic B</span></label
45
+ >
46
+ <input type="text" id="node-input-topicB" style="width: 150px;" />
47
+ </div>
48
+ <div class="form-row">
49
+ <label for="node-input-absolute"
50
+ ><i class="fa fa-calculator"></i>
51
+ <span data-i18n="difference.label.absolute">Absolute</span></label
52
+ >
53
+ <input
54
+ type="checkbox"
55
+ id="node-input-absolute"
56
+ style="width: auto; margin-left: 0;"
57
+ />
58
+ <span data-i18n="difference.label.absoluteHint" style="margin-left: 5px; color: #888;">
59
+ Output |A - B|
60
+ </span>
61
+ </div>
62
+ <div class="form-row">
63
+ <label for="node-input-decimals"
64
+ ><i class="fa fa-hashtag"></i>
65
+ <span data-i18n="difference.label.decimals">Decimals</span></label
66
+ >
67
+ <input
68
+ type="number"
69
+ id="node-input-decimals"
70
+ min="0"
71
+ max="10"
72
+ style="width: 60px;"
73
+ />
74
+ </div>
75
+ </script>
76
+
77
+ <script type="text/html" data-help-name="difference">
78
+ <p>Calculates the difference between two numeric input values.</p>
79
+
80
+ <h3>Behavior</h3>
81
+ <ul>
82
+ <li>Input A identified by configurable topic (default: "a")</li>
83
+ <li>Input B identified by configurable topic (default: "b")</li>
84
+ <li>Result = A - B</li>
85
+ <li>Output triggered on every new message once both values are known</li>
86
+ </ul>
87
+
88
+ <h3>Example</h3>
89
+ <pre>
90
+ topic="a", payload=24.5 → stored as A
91
+ topic="b", payload=18.2 → stored as B
92
+ Output: 24.5 - 18.2 = 6.3</pre>
93
+
94
+ <h3>Inputs</h3>
95
+ <dl class="message-properties">
96
+ <dt>payload <span class="property-type">number</span></dt>
97
+ <dd>Numeric value for A or B</dd>
98
+ <dt>topic <span class="property-type">string</span></dt>
99
+ <dd><b>Required</b> - Must match Topic A or Topic B</dd>
100
+ <dt>reset <span class="property-type">boolean</span></dt>
101
+ <dd><i>(optional)</i> <code>true</code> = reset both values</dd>
102
+ </dl>
103
+
104
+ <h3>Outputs</h3>
105
+ <dl class="message-properties">
106
+ <dt>payload <span class="property-type">number</span></dt>
107
+ <dd>The calculated difference (A - B)</dd>
108
+ <dt>inputs <span class="property-type">object</span></dt>
109
+ <dd>Detailed info: { a: {...}, b: {...}, result, absolute }</dd>
110
+ </dl>
111
+
112
+ <h3>Configuration</h3>
113
+ <dl class="message-properties">
114
+ <dt>Topic A</dt>
115
+ <dd>Topic name for input A (default: "a")</dd>
116
+ <dt>Topic B</dt>
117
+ <dd>Topic name for input B (default: "b")</dd>
118
+ <dt>Absolute</dt>
119
+ <dd>Output absolute value |A - B| (default: off)</dd>
120
+ <dt>Decimals</dt>
121
+ <dd>Number of decimal places (0-10, default: 2)</dd>
122
+ </dl>
123
+
124
+ <h3>Use Cases</h3>
125
+ <ul>
126
+ <li><b>Temperature:</b> Difference between two sensors</li>
127
+ <li><b>Energy:</b> Consumption difference between readings</li>
128
+ <li><b>Level:</b> Water level difference</li>
129
+ </ul>
130
+
131
+ <h3>Node Status</h3>
132
+ <ul>
133
+ <li><b>a(24.5) - b(18.2) = 6.3</b> - Result calculated</li>
134
+ <li><b>Waiting for a...</b> - Input A missing</li>
135
+ <li><b>Waiting for a and b...</b> - Both inputs missing</li>
136
+ </ul>
137
+ </script>
package/difference.js ADDED
@@ -0,0 +1,118 @@
1
+ module.exports = function(RED) {
2
+ function DifferenceNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ var node = this;
5
+
6
+ this.topicA = config.topicA || "a";
7
+ this.topicB = config.topicB || "b";
8
+ this.absolute = config.absolute === true || config.absolute === "true";
9
+ this.decimals = parseInt(config.decimals);
10
+ if (isNaN(this.decimals) || this.decimals < 0) {
11
+ this.decimals = 2;
12
+ }
13
+ if (this.decimals > 10) {
14
+ this.decimals = 10;
15
+ }
16
+
17
+ this.valueA = null;
18
+ this.valueB = null;
19
+
20
+ updateStatus(node);
21
+
22
+ node.on('input', function(msg, send, done) {
23
+ // Handle reset
24
+ if (msg.reset === true) {
25
+ node.valueA = null;
26
+ node.valueB = null;
27
+ updateStatus(node);
28
+ if (done) done();
29
+ return;
30
+ }
31
+
32
+ // Check for topic
33
+ if (!msg.topic || msg.topic === "") {
34
+ node.warn("msg.topic is required");
35
+ if (done) done();
36
+ return;
37
+ }
38
+
39
+ // Check if topic matches A or B
40
+ var isTopicA = msg.topic === node.topicA;
41
+ var isTopicB = msg.topic === node.topicB;
42
+
43
+ if (!isTopicA && !isTopicB) {
44
+ node.warn("Topic '" + msg.topic + "' not configured (expected '" + node.topicA + "' or '" + node.topicB + "')");
45
+ if (done) done();
46
+ return;
47
+ }
48
+
49
+ // Validate payload is numeric
50
+ var value = parseFloat(msg.payload);
51
+ if (isNaN(value)) {
52
+ node.warn("Payload is not a valid number: " + msg.payload);
53
+ if (done) done();
54
+ return;
55
+ }
56
+
57
+ // Store value
58
+ if (isTopicA) {
59
+ node.valueA = value;
60
+ } else {
61
+ node.valueB = value;
62
+ }
63
+
64
+ // Check if we have both values
65
+ if (node.valueA !== null && node.valueB !== null) {
66
+ var result = node.valueA - node.valueB;
67
+
68
+ if (node.absolute) {
69
+ result = Math.abs(result);
70
+ }
71
+
72
+ // Round to decimals
73
+ result = parseFloat(result.toFixed(node.decimals));
74
+
75
+ msg.payload = result;
76
+ msg.inputs = {
77
+ a: { topic: node.topicA, value: node.valueA },
78
+ b: { topic: node.topicB, value: node.valueB },
79
+ result: result,
80
+ absolute: node.absolute
81
+ };
82
+
83
+ send(msg);
84
+ }
85
+
86
+ updateStatus(node);
87
+ if (done) done();
88
+ });
89
+
90
+ node.on('close', function() {
91
+ node.valueA = null;
92
+ node.valueB = null;
93
+ });
94
+ }
95
+
96
+ function updateStatus(node) {
97
+ if (node.valueA === null && node.valueB === null) {
98
+ node.status({ fill: "yellow", shape: "ring", text: "Waiting for " + node.topicA + " and " + node.topicB + "..." });
99
+ } else if (node.valueA === null) {
100
+ node.status({ fill: "yellow", shape: "ring", text: "Waiting for " + node.topicA + "..." });
101
+ } else if (node.valueB === null) {
102
+ node.status({ fill: "yellow", shape: "ring", text: "Waiting for " + node.topicB + "..." });
103
+ } else {
104
+ var result = node.valueA - node.valueB;
105
+ if (node.absolute) {
106
+ result = Math.abs(result);
107
+ }
108
+ result = parseFloat(result.toFixed(node.decimals));
109
+ node.status({
110
+ fill: "green",
111
+ shape: "dot",
112
+ text: node.topicA + "(" + node.valueA + ") - " + node.topicB + "(" + node.valueB + ") = " + result
113
+ });
114
+ }
115
+ }
116
+
117
+ RED.nodes.registerType("difference", DifferenceNode);
118
+ }
@@ -0,0 +1,61 @@
1
+ <script type="text/html" data-help-name="difference">
2
+ <p>Berechnet die Differenz zwischen zwei numerischen Eingabewerten.</p>
3
+
4
+ <h3>Verhalten</h3>
5
+ <ul>
6
+ <li>Input A identifiziert durch konfigurierbares Topic (Standard: "a")</li>
7
+ <li>Input B identifiziert durch konfigurierbares Topic (Standard: "b")</li>
8
+ <li>Ergebnis = A - B</li>
9
+ <li>Output bei jeder neuen Nachricht, sobald beide Werte bekannt sind</li>
10
+ </ul>
11
+
12
+ <h3>Beispiel</h3>
13
+ <pre>
14
+ topic="a", payload=24.5 → gespeichert als A
15
+ topic="b", payload=18.2 → gespeichert als B
16
+ Ausgabe: 24.5 - 18.2 = 6.3</pre>
17
+
18
+ <h3>Eingaben</h3>
19
+ <dl class="message-properties">
20
+ <dt>payload <span class="property-type">number</span></dt>
21
+ <dd>Numerischer Wert für A oder B</dd>
22
+ <dt>topic <span class="property-type">string</span></dt>
23
+ <dd><b>Erforderlich</b> - Muss Topic A oder Topic B entsprechen</dd>
24
+ <dt>reset <span class="property-type">boolean</span></dt>
25
+ <dd><i>(optional)</i> <code>true</code> = beide Werte zurücksetzen</dd>
26
+ </dl>
27
+
28
+ <h3>Ausgaben</h3>
29
+ <dl class="message-properties">
30
+ <dt>payload <span class="property-type">number</span></dt>
31
+ <dd>Die berechnete Differenz (A - B)</dd>
32
+ <dt>inputs <span class="property-type">object</span></dt>
33
+ <dd>Detaillierte Info: { a: {...}, b: {...}, result, absolute }</dd>
34
+ </dl>
35
+
36
+ <h3>Konfiguration</h3>
37
+ <dl class="message-properties">
38
+ <dt>Topic A</dt>
39
+ <dd>Topic-Name für Input A (Standard: "a")</dd>
40
+ <dt>Topic B</dt>
41
+ <dd>Topic-Name für Input B (Standard: "b")</dd>
42
+ <dt>Absolut</dt>
43
+ <dd>Absoluten Wert |A - B| ausgeben (Standard: aus)</dd>
44
+ <dt>Dezimalstellen</dt>
45
+ <dd>Anzahl der Dezimalstellen (0-10, Standard: 2)</dd>
46
+ </dl>
47
+
48
+ <h3>Anwendungsbeispiele</h3>
49
+ <ul>
50
+ <li><b>Temperatur:</b> Differenz zwischen zwei Sensoren</li>
51
+ <li><b>Energie:</b> Verbrauchsdifferenz zwischen Ablesungen</li>
52
+ <li><b>Füllstand:</b> Wasserstandsdifferenz</li>
53
+ </ul>
54
+
55
+ <h3>Node Status</h3>
56
+ <ul>
57
+ <li><b>a(24.5) - b(18.2) = 6.3</b> - Ergebnis berechnet</li>
58
+ <li><b>Warte auf a...</b> - Input A fehlt</li>
59
+ <li><b>Warte auf a und b...</b> - Beide Inputs fehlen</li>
60
+ </ul>
61
+ </script>
@@ -0,0 +1,12 @@
1
+ {
2
+ "difference": {
3
+ "label": {
4
+ "name": "Name",
5
+ "topicA": "Topic A",
6
+ "topicB": "Topic B",
7
+ "absolute": "Absolut",
8
+ "absoluteHint": "Ausgabe |A - B|",
9
+ "decimals": "Dezimalstellen"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "difference": {
3
+ "label": {
4
+ "name": "Name",
5
+ "topicA": "Topic A",
6
+ "topicB": "Topic B",
7
+ "absolute": "Absolute",
8
+ "absoluteHint": "Output |A - B|",
9
+ "decimals": "Decimals"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,61 @@
1
+ <script type="text/html" data-help-name="difference">
2
+ <p>Calcula la diferencia entre dos valores de entrada numéricos.</p>
3
+
4
+ <h3>Comportamiento</h3>
5
+ <ul>
6
+ <li>Input A identificado por topic configurable (por defecto: "a")</li>
7
+ <li>Input B identificado por topic configurable (por defecto: "b")</li>
8
+ <li>Resultado = A - B</li>
9
+ <li>Salida en cada nuevo mensaje una vez que ambos valores son conocidos</li>
10
+ </ul>
11
+
12
+ <h3>Ejemplo</h3>
13
+ <pre>
14
+ topic="a", payload=24.5 → almacenado como A
15
+ topic="b", payload=18.2 → almacenado como B
16
+ Salida: 24.5 - 18.2 = 6.3</pre>
17
+
18
+ <h3>Entradas</h3>
19
+ <dl class="message-properties">
20
+ <dt>payload <span class="property-type">number</span></dt>
21
+ <dd>Valor numérico para A o B</dd>
22
+ <dt>topic <span class="property-type">string</span></dt>
23
+ <dd><b>Requerido</b> - Debe coincidir con Topic A o Topic B</dd>
24
+ <dt>reset <span class="property-type">boolean</span></dt>
25
+ <dd><i>(opcional)</i> <code>true</code> = restablecer ambos valores</dd>
26
+ </dl>
27
+
28
+ <h3>Salidas</h3>
29
+ <dl class="message-properties">
30
+ <dt>payload <span class="property-type">number</span></dt>
31
+ <dd>La diferencia calculada (A - B)</dd>
32
+ <dt>inputs <span class="property-type">object</span></dt>
33
+ <dd>Info detallada: { a: {...}, b: {...}, result, absolute }</dd>
34
+ </dl>
35
+
36
+ <h3>Configuración</h3>
37
+ <dl class="message-properties">
38
+ <dt>Topic A</dt>
39
+ <dd>Nombre del topic para Input A (por defecto: "a")</dd>
40
+ <dt>Topic B</dt>
41
+ <dd>Nombre del topic para Input B (por defecto: "b")</dd>
42
+ <dt>Absoluto</dt>
43
+ <dd>Salida valor absoluto |A - B| (por defecto: desactivado)</dd>
44
+ <dt>Decimales</dt>
45
+ <dd>Número de decimales (0-10, por defecto: 2)</dd>
46
+ </dl>
47
+
48
+ <h3>Casos de uso</h3>
49
+ <ul>
50
+ <li><b>Temperatura:</b> Diferencia entre dos sensores</li>
51
+ <li><b>Energía:</b> Diferencia de consumo entre lecturas</li>
52
+ <li><b>Nivel:</b> Diferencia de nivel de agua</li>
53
+ </ul>
54
+
55
+ <h3>Estado del Nodo</h3>
56
+ <ul>
57
+ <li><b>a(24.5) - b(18.2) = 6.3</b> - Resultado calculado</li>
58
+ <li><b>Esperando a...</b> - Input A falta</li>
59
+ <li><b>Esperando a y b...</b> - Ambos inputs faltan</li>
60
+ </ul>
61
+ </script>
@@ -0,0 +1,12 @@
1
+ {
2
+ "difference": {
3
+ "label": {
4
+ "name": "Nombre",
5
+ "topicA": "Topic A",
6
+ "topicB": "Topic B",
7
+ "absolute": "Absoluto",
8
+ "absoluteHint": "Salida |A - B|",
9
+ "decimals": "Decimales"
10
+ }
11
+ }
12
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "node-red-contrib-difference",
3
+ "version": "1.0.0",
4
+ "description": "Node-RED difference calculator - calculates the difference between two numeric inputs",
5
+ "files": [
6
+ "difference.js",
7
+ "difference.html",
8
+ "locales"
9
+ ],
10
+ "keywords": [
11
+ "node-red",
12
+ "difference",
13
+ "subtract",
14
+ "minus",
15
+ "math",
16
+ "calculator"
17
+ ],
18
+ "author": "sr.rpo",
19
+ "license": "MIT",
20
+ "node-red": {
21
+ "nodes": {
22
+ "difference": "difference.js"
23
+ }
24
+ },
25
+ "engines": {
26
+ "node": ">=14.0.0"
27
+ },
28
+ "scripts": {
29
+ "test": "mocha \"test/**/*_spec.js\" --exit"
30
+ },
31
+ "devDependencies": {
32
+ "mocha": "^10.2.0",
33
+ "node-red": "^3.1.0",
34
+ "node-red-node-test-helper": "^0.3.6"
35
+ }
36
+ }