node-red-contrib-comparator 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,36 @@
1
+ # node-red-contrib-comparator
2
+
3
+ A Node-RED node that compares two numeric values identified by msg.topic and outputs a boolean result.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install node-red-contrib-comparator
9
+ ```
10
+
11
+ ## Inputs
12
+
13
+ - **payload** `number`: Value for input A or B (identified by msg.topic).
14
+ - **topic** `string`: Set to Topic A or Topic B name.
15
+ - **operator** _(optional)_ `string`: Override operator: > < >= <= == !=
16
+ - **reset** _(optional)_ `boolean`: Clear stored A and B values.
17
+
18
+ ## Outputs
19
+
20
+ - **payload** `boolean`: Comparison result.
21
+ - **comparator** `object`: { a, b, operator, result, tolerance }
22
+
23
+ ## Configuration
24
+
25
+ - **Topic A/B**: msg.topic identifiers (default: a / b).
26
+ - **Operator**: Comparison operator (default: >).
27
+ - **Tolerance**: Dead band for == and != comparisons (default: 0.01).
28
+
29
+ ## Tips
30
+
31
+ - Output fires when both A and B are known.
32
+ - Tolerance applies only to == and != operators.
33
+
34
+ ## License
35
+
36
+ MIT (c) sr.rpo
@@ -0,0 +1,88 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('comparator', {
3
+ category: 'rpo',
4
+ color: '#D4600A',
5
+ defaults: {
6
+ name: { value: '' },
7
+ topicA: { value: 'a' },
8
+ topicB: { value: 'b' },
9
+ operator: { value: '>' },
10
+ tolerance: { value: 0.01 }
11
+ },
12
+ inputs: 1,
13
+ outputs: 1,
14
+ icon: 'font-awesome/fa-balance-scale',
15
+ label: function() { return this.name || ('A ' + (this.operator || '>') + ' B'); },
16
+ labelStyle: function() { return this.name ? 'node_label_italic' : ''; },
17
+ oneditprepare: function() {
18
+ function toggleTolerance() {
19
+ var op = $('#node-input-operator').val();
20
+ var row = $('#node-input-tolerance').closest('.form-row');
21
+ if (op === '==' || op === '!=') { row.show(); } else { row.hide(); }
22
+ }
23
+ $('#node-input-operator').on('change', toggleTolerance);
24
+ toggleTolerance();
25
+ }
26
+ });
27
+ </script>
28
+
29
+ <script type="text/html" data-template-name="comparator">
30
+ <div class="form-row">
31
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
32
+ <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
33
+ </div>
34
+ <div class="form-row">
35
+ <label for="node-input-topicA"><i class="fa fa-font"></i> <span data-i18n="comparator.label.topicA"></span></label>
36
+ <input type="text" id="node-input-topicA" style="width:70%">
37
+ </div>
38
+ <div class="form-row">
39
+ <label for="node-input-topicB"><i class="fa fa-font"></i> <span data-i18n="comparator.label.topicB"></span></label>
40
+ <input type="text" id="node-input-topicB" style="width:70%">
41
+ </div>
42
+ <div class="form-row">
43
+ <label for="node-input-operator"><i class="fa fa-code"></i> <span data-i18n="comparator.label.operator"></span></label>
44
+ <select id="node-input-operator" style="width:70%">
45
+ <option value=">">&gt; (A greater than B)</option>
46
+ <option value="<">&lt; (A less than B)</option>
47
+ <option value=">=">&gt;= (A greater or equal B)</option>
48
+ <option value="<=">&lt;= (A less or equal B)</option>
49
+ <option value="==">== (A equals B)</option>
50
+ <option value="!=">!= (A not equals B)</option>
51
+ </select>
52
+ </div>
53
+ <div class="form-row">
54
+ <label for="node-input-tolerance"><i class="fa fa-arrows-h"></i> <span data-i18n="comparator.label.tolerance"></span></label>
55
+ <input type="number" id="node-input-tolerance" min="0" step="0.001" style="width:70%">
56
+ </div>
57
+ </script>
58
+
59
+ <script type="text/html" data-help-name="comparator">
60
+ <p>Compares two numeric values identified by <code>msg.topic</code> and outputs a boolean result.</p>
61
+
62
+ <h3>Input</h3>
63
+ <dl class="message-properties">
64
+ <dt>payload <span class="property-type">number</span></dt>
65
+ <dd>Numeric value for input A or B (identified by <code>msg.topic</code>).</dd>
66
+ <dt>topic <span class="property-type">string</span></dt>
67
+ <dd>Set to the configured Topic A or Topic B name.</dd>
68
+ <dt class="optional">operator <span class="property-type">string</span></dt>
69
+ <dd>Override operator for this message: <code>&gt;</code> <code>&lt;</code> <code>&gt;=</code> <code>&lt;=</code> <code>==</code> <code>!=</code></dd>
70
+ <dt class="optional">reset <span class="property-type">boolean</span></dt>
71
+ <dd>Set to <code>true</code> to clear stored A and B values.</dd>
72
+ </dl>
73
+
74
+ <h3>Output</h3>
75
+ <dl class="message-properties">
76
+ <dt>payload <span class="property-type">boolean</span></dt>
77
+ <dd>Result of the comparison.</dd>
78
+ <dt>comparator <span class="property-type">object</span></dt>
79
+ <dd><code>{ a, b, operator, result, tolerance }</code></dd>
80
+ </dl>
81
+
82
+ <h3>Tips</h3>
83
+ <ul>
84
+ <li>Output fires every time a new value arrives <strong>and both A and B are known</strong>.</li>
85
+ <li>Tolerance applies only to <code>==</code> and <code>!=</code> operators.</li>
86
+ <li>Send <code>msg.reset = true</code> to clear stored values.</li>
87
+ </ul>
88
+ </script>
package/comparator.js ADDED
@@ -0,0 +1,78 @@
1
+ module.exports = function(RED) {
2
+ function ComparatorNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ var node = this;
5
+
6
+ node.topicA = config.topicA || 'a';
7
+ node.topicB = config.topicB || 'b';
8
+ node.operator = config.operator || '>';
9
+ node.tolerance = parseFloat(config.tolerance) || 0.01;
10
+
11
+ node.valueA = undefined;
12
+ node.valueB = undefined;
13
+
14
+ updateStatus(node);
15
+
16
+ node.on('input', function(msg) {
17
+ if (msg.reset === true) {
18
+ node.valueA = undefined;
19
+ node.valueB = undefined;
20
+ updateStatus(node);
21
+ return;
22
+ }
23
+
24
+ var op = msg.operator !== undefined ? msg.operator : node.operator;
25
+ var tol = msg.tolerance !== undefined ? parseFloat(msg.tolerance) : node.tolerance;
26
+ var topic = (msg.topic !== undefined) ? String(msg.topic) : '';
27
+
28
+ if (topic === node.topicA) {
29
+ var val = parseFloat(msg.payload);
30
+ if (isNaN(val)) { node.warn('Input A is not a number: ' + msg.payload); return; }
31
+ node.valueA = val;
32
+ } else if (topic === node.topicB) {
33
+ var val = parseFloat(msg.payload);
34
+ if (isNaN(val)) { node.warn('Input B is not a number: ' + msg.payload); return; }
35
+ node.valueB = val;
36
+ } else {
37
+ node.warn('Unknown topic: ' + topic + ' (expected "' + node.topicA + '" or "' + node.topicB + '")');
38
+ return;
39
+ }
40
+
41
+ if (node.valueA === undefined) { node.status({ fill: 'grey', shape: 'ring', text: 'Waiting for A...' }); return; }
42
+ if (node.valueB === undefined) { node.status({ fill: 'grey', shape: 'ring', text: 'Waiting for B...' }); return; }
43
+
44
+ var a = node.valueA, b = node.valueB;
45
+ var result;
46
+
47
+ switch (op) {
48
+ case '>': result = a > b; break;
49
+ case '<': result = a < b; break;
50
+ case '>=': result = a >= b; break;
51
+ case '<=': result = a <= b; break;
52
+ case '==': result = Math.abs(a - b) <= tol; break;
53
+ case '!=': result = Math.abs(a - b) > tol; break;
54
+ default: node.warn('Unknown operator: ' + op); return;
55
+ }
56
+
57
+ msg.payload = result;
58
+ msg.comparator = { a: a, b: b, operator: op, result: result, tolerance: tol };
59
+
60
+ if (result) {
61
+ node.status({ fill: 'green', shape: 'dot', text: a + ' ' + op + ' ' + b + ' → true' });
62
+ } else {
63
+ node.status({ fill: 'grey', shape: 'dot', text: a + ' ' + op + ' ' + b + ' → false' });
64
+ }
65
+
66
+ node.send(msg);
67
+ });
68
+
69
+ function updateStatus(n) {
70
+ if (n.valueA === undefined || n.valueB === undefined) {
71
+ var waiting = (n.valueA === undefined) ? 'A' : 'B';
72
+ n.status({ fill: 'grey', shape: 'ring', text: 'Waiting for ' + waiting + '...' });
73
+ }
74
+ }
75
+ }
76
+
77
+ RED.nodes.registerType('comparator', ComparatorNode);
78
+ };
@@ -0,0 +1,10 @@
1
+ {
2
+ "comparator": {
3
+ "label": {
4
+ "topicA": "Topic A",
5
+ "topicB": "Topic B",
6
+ "operator": "Operator",
7
+ "tolerance": "Toleranz (für == / !=)"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "comparator": {
3
+ "label": {
4
+ "topicA": "Topic A",
5
+ "topicB": "Topic B",
6
+ "operator": "Operator",
7
+ "tolerance": "Tolerance (for == / !=)"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "comparator": {
3
+ "label": {
4
+ "topicA": "Tema A",
5
+ "topicB": "Tema B",
6
+ "operator": "Operador",
7
+ "tolerance": "Tolerancia (para == / !=)"
8
+ }
9
+ }
10
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "node-red-contrib-comparator",
3
+ "version": "1.0.0",
4
+ "description": "Compares two numeric values with configurable operator and outputs boolean result",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Wobi848/node-red-contrib-rpo-suite.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/Wobi848/node-red-contrib-rpo-suite/issues"
11
+ },
12
+ "files": [
13
+ "comparator.js",
14
+ "comparator.html",
15
+ "locales",
16
+ "examples"
17
+ ],
18
+ "keywords": [
19
+ "node-red",
20
+ "compare",
21
+ "comparator",
22
+ "greater",
23
+ "less",
24
+ "equal",
25
+ "boolean"
26
+ ],
27
+ "author": "sr.rpo",
28
+ "license": "MIT",
29
+ "node-red": {
30
+ "version": "1.0.0",
31
+ "nodes": {
32
+ "comparator": "comparator.js"
33
+ }
34
+ },
35
+ "engines": {
36
+ "node": ">=14.0.0"
37
+ },
38
+ "scripts": {
39
+ "test": "mocha \"test/**/*_spec.js\" --exit"
40
+ },
41
+ "devDependencies": {
42
+ "mocha": "^10.2.0",
43
+ "node-red": "^3.1.0",
44
+ "node-red-node-test-helper": "^0.3.6"
45
+ }
46
+ }