node-red-contrib-arithmetic 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/arithmetic.html +78 -0
- package/arithmetic.js +82 -0
- package/locales/de/arithmetic.json +10 -0
- package/locales/en/arithmetic.json +10 -0
- package/locales/es/arithmetic.json +10 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# node-red-contrib-arithmetic
|
|
2
|
+
|
|
3
|
+
A Node-RED node that performs arithmetic operations (+, -, x, /, %, ^) on two numeric inputs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install node-red-contrib-arithmetic
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
|
|
13
|
+
- **payload** `number`: Value for A or B (identified by msg.topic).
|
|
14
|
+
- **topic** `string`: Set to Topic A or Topic B name.
|
|
15
|
+
- **operator** _(optional)_ `string`: Override operator: + - x / % ^
|
|
16
|
+
- **reset** _(optional)_ `boolean`: Clear stored A and B values.
|
|
17
|
+
|
|
18
|
+
## Outputs
|
|
19
|
+
|
|
20
|
+
- **payload** `number`: Result rounded to configured decimal places.
|
|
21
|
+
- **arithmetic** `object`: { a, b, operator, result, decimals }
|
|
22
|
+
|
|
23
|
+
## Configuration
|
|
24
|
+
|
|
25
|
+
- **Topic A/B**: msg.topic identifiers (default: a / b).
|
|
26
|
+
- **Operator**: Arithmetic operator (default: +).
|
|
27
|
+
- **Decimal Places**: Output precision 0-10 (default: 2).
|
|
28
|
+
|
|
29
|
+
## Tips
|
|
30
|
+
|
|
31
|
+
- Division and modulo by zero are blocked with a warning - no output sent.
|
|
32
|
+
- Output fires when both A and B are known.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT (c) sr.rpo
|
package/arithmetic.html
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('arithmetic', {
|
|
3
|
+
category: 'rpo',
|
|
4
|
+
color: '#D4600A',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: { value: '' },
|
|
7
|
+
topicA: { value: 'a' },
|
|
8
|
+
topicB: { value: 'b' },
|
|
9
|
+
operator: { value: '+' },
|
|
10
|
+
decimals: { value: 2 }
|
|
11
|
+
},
|
|
12
|
+
inputs: 1,
|
|
13
|
+
outputs: 1,
|
|
14
|
+
icon: 'font-awesome/fa-calculator',
|
|
15
|
+
label: function() { return this.name || ('A ' + (this.operator || '+') + ' B'); },
|
|
16
|
+
labelStyle: function() { return this.name ? 'node_label_italic' : ''; }
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script type="text/html" data-template-name="arithmetic">
|
|
21
|
+
<div class="form-row">
|
|
22
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
|
|
23
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
|
|
24
|
+
</div>
|
|
25
|
+
<div class="form-row">
|
|
26
|
+
<label for="node-input-topicA"><i class="fa fa-font"></i> <span data-i18n="arithmetic.label.topicA"></span></label>
|
|
27
|
+
<input type="text" id="node-input-topicA" style="width:70%">
|
|
28
|
+
</div>
|
|
29
|
+
<div class="form-row">
|
|
30
|
+
<label for="node-input-topicB"><i class="fa fa-font"></i> <span data-i18n="arithmetic.label.topicB"></span></label>
|
|
31
|
+
<input type="text" id="node-input-topicB" style="width:70%">
|
|
32
|
+
</div>
|
|
33
|
+
<div class="form-row">
|
|
34
|
+
<label for="node-input-operator"><i class="fa fa-calculator"></i> <span data-i18n="arithmetic.label.operator"></span></label>
|
|
35
|
+
<select id="node-input-operator" style="width:70%">
|
|
36
|
+
<option value="+">+ (addition)</option>
|
|
37
|
+
<option value="-">- (subtraction)</option>
|
|
38
|
+
<option value="×">× (multiplication)</option>
|
|
39
|
+
<option value="÷">÷ (division)</option>
|
|
40
|
+
<option value="%">% (modulo)</option>
|
|
41
|
+
<option value="^">^ (power)</option>
|
|
42
|
+
</select>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="form-row">
|
|
45
|
+
<label for="node-input-decimals"><i class="fa fa-sort-numeric-asc"></i> <span data-i18n="arithmetic.label.decimals"></span></label>
|
|
46
|
+
<input type="number" id="node-input-decimals" min="0" max="10" style="width:70%">
|
|
47
|
+
</div>
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<script type="text/html" data-help-name="arithmetic">
|
|
51
|
+
<p>Performs arithmetic operations on two numeric inputs identified by <code>msg.topic</code>.</p>
|
|
52
|
+
|
|
53
|
+
<h3>Input</h3>
|
|
54
|
+
<dl class="message-properties">
|
|
55
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
56
|
+
<dd>Numeric value for input A or B (identified by <code>msg.topic</code>).</dd>
|
|
57
|
+
<dt>topic <span class="property-type">string</span></dt>
|
|
58
|
+
<dd>Set to the configured Topic A or Topic B name.</dd>
|
|
59
|
+
<dt class="optional">operator <span class="property-type">string</span></dt>
|
|
60
|
+
<dd>Override operator: <code>+</code> <code>-</code> <code>×</code> <code>÷</code> <code>%</code> <code>^</code></dd>
|
|
61
|
+
<dt class="optional">reset <span class="property-type">boolean</span></dt>
|
|
62
|
+
<dd>Set to <code>true</code> to clear stored A and B values.</dd>
|
|
63
|
+
</dl>
|
|
64
|
+
|
|
65
|
+
<h3>Output</h3>
|
|
66
|
+
<dl class="message-properties">
|
|
67
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
68
|
+
<dd>Arithmetic result rounded to configured decimal places.</dd>
|
|
69
|
+
<dt>arithmetic <span class="property-type">object</span></dt>
|
|
70
|
+
<dd><code>{ a, b, operator, result, decimals }</code></dd>
|
|
71
|
+
</dl>
|
|
72
|
+
|
|
73
|
+
<h3>Tips</h3>
|
|
74
|
+
<ul>
|
|
75
|
+
<li>Division and modulo by zero are blocked with a warning — no output is sent.</li>
|
|
76
|
+
<li>Output fires every time a new value arrives <strong>and both A and B are known</strong>.</li>
|
|
77
|
+
</ul>
|
|
78
|
+
</script>
|
package/arithmetic.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
function ArithmeticNode(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.decimals = parseInt(config.decimals, 10);
|
|
10
|
+
if (isNaN(node.decimals) || node.decimals < 0) node.decimals = 2;
|
|
11
|
+
|
|
12
|
+
node.valueA = undefined;
|
|
13
|
+
node.valueB = undefined;
|
|
14
|
+
|
|
15
|
+
updateStatus(node);
|
|
16
|
+
|
|
17
|
+
node.on('input', function(msg) {
|
|
18
|
+
if (msg.reset === true) {
|
|
19
|
+
node.valueA = undefined;
|
|
20
|
+
node.valueB = undefined;
|
|
21
|
+
updateStatus(node);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var op = msg.operator !== undefined ? msg.operator : node.operator;
|
|
26
|
+
var dec = node.decimals;
|
|
27
|
+
var topic = (msg.topic !== undefined) ? String(msg.topic) : '';
|
|
28
|
+
|
|
29
|
+
if (topic === node.topicA) {
|
|
30
|
+
var val = parseFloat(msg.payload);
|
|
31
|
+
if (isNaN(val)) { node.warn('Input A is not a number: ' + msg.payload); return; }
|
|
32
|
+
node.valueA = val;
|
|
33
|
+
} else if (topic === node.topicB) {
|
|
34
|
+
var val = parseFloat(msg.payload);
|
|
35
|
+
if (isNaN(val)) { node.warn('Input B is not a number: ' + msg.payload); return; }
|
|
36
|
+
node.valueB = val;
|
|
37
|
+
} else {
|
|
38
|
+
node.warn('Unknown topic: ' + topic + ' (expected "' + node.topicA + '" or "' + node.topicB + '")');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (node.valueA === undefined) { node.status({ fill: 'grey', shape: 'ring', text: 'Waiting for A...' }); return; }
|
|
43
|
+
if (node.valueB === undefined) { node.status({ fill: 'grey', shape: 'ring', text: 'Waiting for B...' }); return; }
|
|
44
|
+
|
|
45
|
+
var a = node.valueA, b = node.valueB;
|
|
46
|
+
var result;
|
|
47
|
+
|
|
48
|
+
switch (op) {
|
|
49
|
+
case '+': result = a + b; break;
|
|
50
|
+
case '-': result = a - b; break;
|
|
51
|
+
case '×': result = a * b; break;
|
|
52
|
+
case '÷':
|
|
53
|
+
if (b === 0) { node.warn('Division by zero'); node.status({ fill: 'yellow', shape: 'dot', text: 'Division by zero' }); return; }
|
|
54
|
+
result = a / b;
|
|
55
|
+
break;
|
|
56
|
+
case '%':
|
|
57
|
+
if (b === 0) { node.warn('Modulo by zero'); node.status({ fill: 'yellow', shape: 'dot', text: 'Modulo by zero' }); return; }
|
|
58
|
+
result = a % b;
|
|
59
|
+
break;
|
|
60
|
+
case '^': result = Math.pow(a, b); break;
|
|
61
|
+
default: node.warn('Unknown operator: ' + op); return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
result = parseFloat(result.toFixed(dec));
|
|
65
|
+
|
|
66
|
+
msg.payload = result;
|
|
67
|
+
msg.arithmetic = { a: a, b: b, operator: op, result: result, decimals: dec };
|
|
68
|
+
|
|
69
|
+
node.status({ fill: 'green', shape: 'dot', text: a + ' ' + op + ' ' + b + ' = ' + result });
|
|
70
|
+
node.send(msg);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
function updateStatus(n) {
|
|
74
|
+
if (n.valueA === undefined || n.valueB === undefined) {
|
|
75
|
+
var waiting = (n.valueA === undefined) ? 'A' : 'B';
|
|
76
|
+
n.status({ fill: 'grey', shape: 'ring', text: 'Waiting for ' + waiting + '...' });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
RED.nodes.registerType('arithmetic', ArithmeticNode);
|
|
82
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-arithmetic",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Performs arithmetic operations (+,-,×,÷,%,^) on two numeric inputs",
|
|
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
|
+
"arithmetic.js",
|
|
14
|
+
"arithmetic.html",
|
|
15
|
+
"locales",
|
|
16
|
+
"examples"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"node-red",
|
|
20
|
+
"arithmetic",
|
|
21
|
+
"math",
|
|
22
|
+
"add",
|
|
23
|
+
"subtract",
|
|
24
|
+
"multiply",
|
|
25
|
+
"divide",
|
|
26
|
+
"calculator"
|
|
27
|
+
],
|
|
28
|
+
"author": "sr.rpo",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"node-red": {
|
|
31
|
+
"version": "1.0.0",
|
|
32
|
+
"nodes": {
|
|
33
|
+
"arithmetic": "arithmetic.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=14.0.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "mocha \"test/**/*_spec.js\" --exit"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"mocha": "^10.2.0",
|
|
44
|
+
"node-red": "^3.1.0",
|
|
45
|
+
"node-red-node-test-helper": "^0.3.6"
|
|
46
|
+
}
|
|
47
|
+
}
|