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 +36 -0
- package/comparator.html +88 -0
- package/comparator.js +78 -0
- package/locales/de/comparator.json +10 -0
- package/locales/en/comparator.json +10 -0
- package/locales/es/comparator.json +10 -0
- package/package.json +46 -0
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
|
package/comparator.html
ADDED
|
@@ -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=">">> (A greater than B)</option>
|
|
46
|
+
<option value="<">< (A less than B)</option>
|
|
47
|
+
<option value=">=">>= (A greater or equal B)</option>
|
|
48
|
+
<option value="<="><= (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>></code> <code><</code> <code>>=</code> <code><=</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
|
+
};
|
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
|
+
}
|