@mschaeffler/node-red-logic 4.0.1 → 4.2.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/logic_and.js +1 -1
- package/logic_blinker.html +188 -0
- package/logic_blinker.js +130 -0
- package/logic_bool.js +1 -1
- package/logic_not.js +1 -1
- package/logic_or.js +1 -1
- package/logic_timerelay.html +4 -4
- package/logic_timerelay.js +1 -1
- package/math_3point.js +1 -1
- package/math_falling.js +1 -1
- package/math_hysteresis.js +1 -1
- package/math_kalman.html +194 -0
- package/math_kalman.js +223 -0
- package/math_mean.html +2 -2
- package/math_mean.js +6 -6
- package/math_reduce.js +1 -1
- package/math_rising.js +1 -1
- package/math_statistics.html +0 -1
- package/math_statistics.js +2 -2
- package/package.json +5 -3
- package/tools.js +1 -1
package/logic_and.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = function(RED) {
|
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
9
|
this.topic = config.topic ?? "";
|
|
10
|
-
this.property = config.property
|
|
10
|
+
this.property = config.property ?? "payload";
|
|
11
11
|
this.minData = Number( config.minData ?? 1 );
|
|
12
12
|
this.filter = Boolean( config.filter );
|
|
13
13
|
this.last = null;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('blinker',{
|
|
3
|
+
category: 'logic',
|
|
4
|
+
color: "#C0DEED",
|
|
5
|
+
defaults: {
|
|
6
|
+
name:{value:""},
|
|
7
|
+
property:{value:"payload",required:true},
|
|
8
|
+
onTime:{value:"1",required:true},
|
|
9
|
+
onTimeUnit:{value:"secs"},
|
|
10
|
+
offTime:{value:"1",required:true},
|
|
11
|
+
offTimeUnit:{value:"secs"},
|
|
12
|
+
outputFirst:{value:true},
|
|
13
|
+
outputFirstType:{value:"bool"},
|
|
14
|
+
outputOn:{value:true,required:true},
|
|
15
|
+
outputOnType:{value:"bool"},
|
|
16
|
+
outputOff:{value:false,required:true},
|
|
17
|
+
outputOffType:{value:"bool"},
|
|
18
|
+
outputLast:{value:false},
|
|
19
|
+
outputLastType:{value:"bool"},
|
|
20
|
+
filter:{value:false},
|
|
21
|
+
showState:{value:false}
|
|
22
|
+
},
|
|
23
|
+
inputs:1,
|
|
24
|
+
outputs:1,
|
|
25
|
+
icon: "font-awesome/fa-refresh",
|
|
26
|
+
label: function() {
|
|
27
|
+
return this.name||"blinker";
|
|
28
|
+
},
|
|
29
|
+
labelStyle: function() {
|
|
30
|
+
return this.name?"node_label_italic":"";
|
|
31
|
+
},
|
|
32
|
+
paletteLabel: "blinker",
|
|
33
|
+
inputLabels: 'input',
|
|
34
|
+
outputLabels: 'blinken',
|
|
35
|
+
oneditprepare: function() {
|
|
36
|
+
$("#node-input-property").typedInput({
|
|
37
|
+
type:"msg",
|
|
38
|
+
types:["msg"],
|
|
39
|
+
typeField: "#node-input-property-type"
|
|
40
|
+
})
|
|
41
|
+
var optionNothing = {value:"nul",label:"none",hasValue:false};
|
|
42
|
+
var optionMessage = {value:"msg",label:"message",hasValue:false};
|
|
43
|
+
$("#node-input-outputFirst").typedInput({
|
|
44
|
+
type:"bool",
|
|
45
|
+
types:["str","num","bool","json",optionNothing,optionMessage],
|
|
46
|
+
typeField: "#node-input-outputFirstType"
|
|
47
|
+
})
|
|
48
|
+
$("#node-input-outputOn").typedInput({
|
|
49
|
+
type:"bool",
|
|
50
|
+
types:["str","num","bool","json"],
|
|
51
|
+
typeField: "#node-input-outputOnType"
|
|
52
|
+
})
|
|
53
|
+
$("#node-input-outputOff").typedInput({
|
|
54
|
+
type:"bool",
|
|
55
|
+
types:["str","num","bool","json"],
|
|
56
|
+
typeField: "#node-input-outputOffType"
|
|
57
|
+
})
|
|
58
|
+
$("#node-input-outputLast").typedInput({
|
|
59
|
+
type:"bool",
|
|
60
|
+
types:["str","num","bool","json",optionNothing,optionMessage],
|
|
61
|
+
typeField: "#node-input-outputLastType"
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<script type="text/html" data-template-name="blinker">
|
|
68
|
+
<div class="form-row">
|
|
69
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
70
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
71
|
+
</div>
|
|
72
|
+
<div class="form-row">
|
|
73
|
+
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> Eigenschaft</label>
|
|
74
|
+
<input type="text" id="node-input-property">
|
|
75
|
+
<input type="hidden" id="node-input-property-type">
|
|
76
|
+
</div>
|
|
77
|
+
<div class="form-row">
|
|
78
|
+
<label for="node-input-onTime"><i class="fa fa-hourglass-start"></i> on time</label>
|
|
79
|
+
<input type="number" id="node-input-onTime" style="width:70px;" placeholder="Time">
|
|
80
|
+
<select id="node-input-onTimeUnit" style="width:140px;">
|
|
81
|
+
<option value="msecs">MilliSeconds</option>
|
|
82
|
+
<option value="secs">Seconds</option>
|
|
83
|
+
<option value="mins">Minutes</option>
|
|
84
|
+
<option value="hours">Hours</option>
|
|
85
|
+
</select>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="form-row">
|
|
88
|
+
<label for="node-input-offTime"><i class="fa fa-hourglass-end"></i> off time</label>
|
|
89
|
+
<input type="number" id="node-input-offTime" style="width:70px;" placeholder="Time">
|
|
90
|
+
<select id="node-input-offTimeUnit" style="width:140px;">
|
|
91
|
+
<option value="msecs">MilliSeconds</option>
|
|
92
|
+
<option value="secs">Seconds</option>
|
|
93
|
+
<option value="mins">Minutes</option>
|
|
94
|
+
<option value="hours">Hours</option>
|
|
95
|
+
</select>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="form-row">
|
|
98
|
+
<label for="node-input-outputFirst"><i class="fa fa-sign-out"></i> 1st Output</label>
|
|
99
|
+
<input type="text" id="node-input-outputFirst">
|
|
100
|
+
<input type="hidden" id="node-input-outputFirstType">
|
|
101
|
+
</div>
|
|
102
|
+
<div class="form-row">
|
|
103
|
+
<label for="node-input-outputOn"><i class="fa fa-sign-out"></i> on Output</label>
|
|
104
|
+
<input type="text" id="node-input-outputOn">
|
|
105
|
+
<input type="hidden" id="node-input-outputOnType">
|
|
106
|
+
</div>
|
|
107
|
+
<div class="form-row">
|
|
108
|
+
<label for="node-input-outputOff"><i class="fa fa-sign-out"></i> off Output</label>
|
|
109
|
+
<input type="text" id="node-input-outputOff">
|
|
110
|
+
<input type="hidden" id="node-input-outputOffType">
|
|
111
|
+
</div>
|
|
112
|
+
<div class="form-row">
|
|
113
|
+
<label for="node-input-outputLast"><i class="fa fa-sign-out"></i> last Output</label>
|
|
114
|
+
<input type="text" id="node-input-outputLast">
|
|
115
|
+
<input type="hidden" id="node-input-outputLastType">
|
|
116
|
+
</div>
|
|
117
|
+
<div class="form-row">
|
|
118
|
+
<label for="node-input-filter"><i class="fa fa-filter"></i> Filter</label>
|
|
119
|
+
<input type="checkbox" id="node-input-filter" style="display:inline-block; width:20px; vertical-align:baseline;">
|
|
120
|
+
</div>
|
|
121
|
+
<div class="form-row">
|
|
122
|
+
<label for="node-input-howStatus"><i class="fa fa-star-o"></i> Status</label>
|
|
123
|
+
<input type="checkbox" id="node-input-showState" style="display:inline-block; width:20px; vertical-align:baseline;">
|
|
124
|
+
</div>
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<script type="text/html" data-help-name="blinker">
|
|
128
|
+
|
|
129
|
+
<p>.</p>
|
|
130
|
+
<p>If the msg property <code>invalid</code> is present in the message, all nodes ignore the message.</p>
|
|
131
|
+
|
|
132
|
+
<table>
|
|
133
|
+
<tr> <th>type</th> <th>input</th> <th>output</th> </tr>
|
|
134
|
+
<tr> <td>boolean</td> <td>false</td> <td>false</td> </tr>
|
|
135
|
+
<tr> <td></td> <td>true</td> <td>true</td> </tr>
|
|
136
|
+
<tr> <td>number</td> <td>0</td> <td>false</td> </tr>
|
|
137
|
+
<tr> <td></td> <td>1</td> <td>true</td> </tr>
|
|
138
|
+
<tr> <td>string</td> <td>false</td> <td>false</td> </tr>
|
|
139
|
+
<tr> <td></td> <td>true</td> <td>true</td> </tr>
|
|
140
|
+
<tr> <td></td> <td>0</td> <td>false</td> </tr>
|
|
141
|
+
<tr> <td></td> <td>1</td> <td>true</td> </tr>
|
|
142
|
+
<tr> <td></td> <td>off</td> <td>false</td> </tr>
|
|
143
|
+
<tr> <td></td> <td>on</td> <td>true</td> </tr>
|
|
144
|
+
</table>
|
|
145
|
+
|
|
146
|
+
<h3>Input</h3>
|
|
147
|
+
|
|
148
|
+
<p>The message property to be used as payload can be defined with the <code>Property</code> property.</p>
|
|
149
|
+
|
|
150
|
+
<dl class="message-properties">
|
|
151
|
+
<dt>payload <span class="property-type"></span></dt>
|
|
152
|
+
<dd> input value.</dd>
|
|
153
|
+
</dl>
|
|
154
|
+
|
|
155
|
+
<h3>Output</h3>
|
|
156
|
+
|
|
157
|
+
<dl class="message-properties">
|
|
158
|
+
<dt>payload <span class="property-type">boolean</span></dt>
|
|
159
|
+
<dd> blinking signal.</dd>
|
|
160
|
+
<dt>state <span class="property-type">boolean</span></dt>
|
|
161
|
+
<dd> received boolean value.</dd>
|
|
162
|
+
</dl>
|
|
163
|
+
|
|
164
|
+
<h3>Parameter</h3>
|
|
165
|
+
|
|
166
|
+
<dl class="message-properties">
|
|
167
|
+
<dt>Property <span class="property-type">string</span></dt>
|
|
168
|
+
<dd> defines the message property to be used as payload.</dd>
|
|
169
|
+
<dt>on time <span class="property-type">number</span></dt>
|
|
170
|
+
<dd> time to be in on state.</dd>
|
|
171
|
+
<dt>off time <span class="property-type">number</span></dt>
|
|
172
|
+
<dd> time to be in off state.</dd>
|
|
173
|
+
<dt>first Output <span class="property-type">multi</span></dt>
|
|
174
|
+
<dd> value to be sent as first (on) mesage.</dd>
|
|
175
|
+
<dt>on Output <span class="property-type">multi</span></dt>
|
|
176
|
+
<dd> value to be sent in on case inside the sequence.</dd>
|
|
177
|
+
<dt>off Output <span class="property-type">multi</span></dt>
|
|
178
|
+
<dd> value to be sent in off case inside the sequence.</dd>
|
|
179
|
+
<dt>Status <span class="property-type">boolean</span></dt>
|
|
180
|
+
<dt>last Output <span class="property-type">multi</span></dt>
|
|
181
|
+
<dd> value to be sent as last (off) message.</dd>
|
|
182
|
+
<dt>Filter <span class="property-type">boolean</span></dt>
|
|
183
|
+
<dd> block sending of unchanged <code>property</code>.</dd>
|
|
184
|
+
<dt>Status <span class="property-type">boolean</span></dt>
|
|
185
|
+
<dd> shows the actual value as a node status.</dd>
|
|
186
|
+
</dl>
|
|
187
|
+
|
|
188
|
+
</script>
|
package/logic_blinker.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
var tools = require('./tools.js');
|
|
3
|
+
|
|
4
|
+
function BlinkerNode(config) {
|
|
5
|
+
RED.nodes.createNode(this,config);
|
|
6
|
+
//this.config = config;
|
|
7
|
+
var node = this;
|
|
8
|
+
this.property = config.property ?? "payload";
|
|
9
|
+
this.onTime = Number( config.onTime ?? 1 ) * tools.timeUnits( config.onTimeUnit );
|
|
10
|
+
this.offTime = Number( config.offTime ?? 1 ) * tools.timeUnits( config.offTimeUnit );
|
|
11
|
+
this.firstType = config.outputFirstType ?? "bool";
|
|
12
|
+
this.lastType = config.outputLastType ?? "bool";
|
|
13
|
+
this.outputFirst = RED.util.evaluateNodeProperty( config.outputFirst ?? "true", this.firstType );
|
|
14
|
+
this.outputOn = RED.util.evaluateNodeProperty( config.outputOn ?? "true", config.outputOnType ?? "bool" );
|
|
15
|
+
this.outputOff = RED.util.evaluateNodeProperty( config.outputOff ?? "false", config.outputOffType ?? "bool" );
|
|
16
|
+
this.outputLast = RED.util.evaluateNodeProperty( config.outputLast ?? "false", this.lastType );
|
|
17
|
+
this.filter = Boolean( config.filter );
|
|
18
|
+
this.showState = Boolean( config.showState );
|
|
19
|
+
this.timerOn = null;
|
|
20
|
+
this.timerOff = null;
|
|
21
|
+
this.last = null;
|
|
22
|
+
node.status( "" );
|
|
23
|
+
|
|
24
|
+
function setStatus(color,text)
|
|
25
|
+
{
|
|
26
|
+
if( node.showState )
|
|
27
|
+
{
|
|
28
|
+
node.status( { text:text, shape:"dot", fill:color } );
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sendFirst()
|
|
33
|
+
{
|
|
34
|
+
clearTimeout( node.timerOn );
|
|
35
|
+
clearTimeout( node.timerOff );
|
|
36
|
+
switch( node.firstType )
|
|
37
|
+
{
|
|
38
|
+
case "nul":
|
|
39
|
+
break;
|
|
40
|
+
case "msg":
|
|
41
|
+
node.send( node.msg );
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
let msg = RED.util.cloneMessage( node.msg );
|
|
45
|
+
msg.payload = node.outputFirst;
|
|
46
|
+
node.send( msg );
|
|
47
|
+
}
|
|
48
|
+
setStatus( "green", "first" );
|
|
49
|
+
node.timerOn = null;
|
|
50
|
+
node.timerOff = setTimeout( sendOff, node.onTime );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sendOn()
|
|
54
|
+
{
|
|
55
|
+
node.msg.payload = node.outputOn;
|
|
56
|
+
node.send( node.msg );
|
|
57
|
+
setStatus( "green", "on" );
|
|
58
|
+
node.timerOn = null;
|
|
59
|
+
node.timerOff = setTimeout( sendOff, node.onTime );
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function sendOff()
|
|
63
|
+
{
|
|
64
|
+
node.msg.payload = node.outputOff;
|
|
65
|
+
node.send( node.msg );
|
|
66
|
+
setStatus( "gray", "off" );
|
|
67
|
+
node.timerOn = setTimeout( sendOn, node.offTime );
|
|
68
|
+
node.timerOff = null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sendLast()
|
|
72
|
+
{
|
|
73
|
+
clearTimeout( node.timerOn );
|
|
74
|
+
clearTimeout( node.timerOff );
|
|
75
|
+
node.timerOn = null;
|
|
76
|
+
node.timerOff = null;
|
|
77
|
+
switch( node.lastType )
|
|
78
|
+
{
|
|
79
|
+
case "nul":
|
|
80
|
+
break;
|
|
81
|
+
case "msg":
|
|
82
|
+
node.send( node.msg );
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
let msg = RED.util.cloneMessage( node.msg );
|
|
86
|
+
msg.payload = node.outputLast;
|
|
87
|
+
node.send( msg );
|
|
88
|
+
}
|
|
89
|
+
setStatus( "gray", "last" );
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
node.on('input', function(msg,send,done) {
|
|
93
|
+
if( msg.invalid )
|
|
94
|
+
{
|
|
95
|
+
done();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
node.state = tools.property2boolean( RED.util.getMessageProperty( msg, node.property ) );
|
|
99
|
+
if( node.state !== null )
|
|
100
|
+
{
|
|
101
|
+
if( !node.filter || node.state !== node.last )
|
|
102
|
+
{
|
|
103
|
+
node.last = node.state;
|
|
104
|
+
node.msg = msg;
|
|
105
|
+
node.msg.state = node.state;
|
|
106
|
+
if( node.state )
|
|
107
|
+
{
|
|
108
|
+
sendFirst();
|
|
109
|
+
}
|
|
110
|
+
else
|
|
111
|
+
{
|
|
112
|
+
sendLast();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
{
|
|
118
|
+
setStatus( "red", "error" );
|
|
119
|
+
}
|
|
120
|
+
done();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
node.on('close', function() {
|
|
124
|
+
clearTimeout( node.timerOn );
|
|
125
|
+
clearTimeout( node.timerOff );
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
RED.nodes.registerType("blinker",BlinkerNode);
|
|
130
|
+
}
|
package/logic_bool.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
this.topic = config.topic ?? "";
|
|
9
|
-
this.property = config.property
|
|
9
|
+
this.property = config.property ?? "payload";
|
|
10
10
|
this.showState = Boolean( config.showState );
|
|
11
11
|
this.filter = Boolean( config.filter );
|
|
12
12
|
this.last = null;
|
package/logic_not.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = function(RED) {
|
|
|
5
5
|
RED.nodes.createNode(this,config);
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
|
-
this.property = config.property
|
|
8
|
+
this.property = config.property ?? "payload";
|
|
9
9
|
this.showState = Boolean( config.showState );
|
|
10
10
|
this.filter = Boolean( config.filter );
|
|
11
11
|
this.last = null;
|
package/logic_or.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = function(RED) {
|
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
9
|
this.topic = config.topic ?? "";
|
|
10
|
-
this.property = config.property
|
|
10
|
+
this.property = config.property ?? "payload";
|
|
11
11
|
this.minData = Number( config.minData ?? 1 );
|
|
12
12
|
this.filter = Boolean( config.filter );
|
|
13
13
|
this.last = null;
|
package/logic_timerelay.html
CHANGED
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
</div>
|
|
80
80
|
<div class="form-row">
|
|
81
81
|
<label for="node-input-delay"><i class="fa fa-play"></i> Delay</label>
|
|
82
|
-
<input type="
|
|
82
|
+
<input type="number" id="node-input-delay" style="width:70px;" placeholder="Time">
|
|
83
83
|
<select id="node-input-delayUnit" style="width:140px;">
|
|
84
84
|
<option value="msecs">MilliSeconds</option>
|
|
85
85
|
<option value="secs">Seconds</option>
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
</div>
|
|
90
90
|
<div class="form-row">
|
|
91
91
|
<label for="node-input-postrun"><i class="fa fa-stop"></i> Postrun</label>
|
|
92
|
-
<input type="
|
|
92
|
+
<input type="number" id="node-input-postrun" style="width:70px;" placeholder="Time">
|
|
93
93
|
<select id="node-input-postrunUnit" style="width:140px;">
|
|
94
94
|
<option value="msecs">MilliSeconds</option>
|
|
95
95
|
<option value="secs">Seconds</option>
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
</div>
|
|
100
100
|
<div class="form-row">
|
|
101
101
|
<label for="node-input-minOn"><i class="fa fa-hourglass-start"></i> min on time</label>
|
|
102
|
-
<input type="
|
|
102
|
+
<input type="number" id="node-input-minOn" style="width:70px;" placeholder="Time">
|
|
103
103
|
<select id="node-input-minOnUnit" style="width:140px;">
|
|
104
104
|
<option value="msecs">MilliSeconds</option>
|
|
105
105
|
<option value="secs">Seconds</option>
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
<div class="form-row">
|
|
111
111
|
<label for="node-input-maxOn"><i class="fa fa-hourglass-end"></i> max on time</label>
|
|
112
|
-
<input type="
|
|
112
|
+
<input type="number" id="node-input-maxOn" style="width:70px;" placeholder="Time">
|
|
113
113
|
<select id="node-input-maxOnUnit" style="width:140px;">
|
|
114
114
|
<option value="msecs">MilliSeconds</option>
|
|
115
115
|
<option value="secs">Seconds</option>
|
package/logic_timerelay.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = function(RED) {
|
|
|
5
5
|
RED.nodes.createNode(this,config);
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
|
-
this.property = config.property
|
|
8
|
+
this.property = config.property ?? "payload";
|
|
9
9
|
this.delay = Number( config.delay ?? 0 ) * tools.timeUnits( config.delayUnit );
|
|
10
10
|
this.postrun = Number( config.postrun ?? 0 ) * tools.timeUnits( config.postrunUnit );
|
|
11
11
|
this.minOn = Number( config.minOn ?? 0 ) * tools.timeUnits( config.minOnUnit );
|
package/math_3point.js
CHANGED
|
@@ -8,7 +8,7 @@ module.exports = function(RED) {
|
|
|
8
8
|
//this.config = config;
|
|
9
9
|
var node = this;
|
|
10
10
|
var context = this.context();
|
|
11
|
-
this.topic = config.topic
|
|
11
|
+
this.topic = config.topic ?? "";
|
|
12
12
|
this.property = config.property ?? "payload";
|
|
13
13
|
this.propertyType = config.propertyType ?? "msg";
|
|
14
14
|
this.thresholdUpRise = Number( config.thresholdUpRise );
|
package/math_falling.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
|
-
this.topic = config.topic
|
|
9
|
+
this.topic = config.topic ?? "";
|
|
10
10
|
this.property = config.property ?? "payload";
|
|
11
11
|
this.propertyType = config.propertyType ?? "msg";
|
|
12
12
|
this.threshold = Number( config.threshold );
|
package/math_hysteresis.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
|
-
this.topic = config.topic
|
|
9
|
+
this.topic = config.topic ?? "";
|
|
10
10
|
this.property = config.property ?? "payload";
|
|
11
11
|
this.propertyType = config.propertyType ?? "msg";
|
|
12
12
|
this.threshold_rise = Number( config.threshold_raise );
|
package/math_kalman.html
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('kalman',{
|
|
3
|
+
category: 'logic',
|
|
4
|
+
color: "#C0DEED",
|
|
5
|
+
defaults: {
|
|
6
|
+
name:{value:""},
|
|
7
|
+
topic:{value:""},
|
|
8
|
+
property:{value:"payload", required:true},
|
|
9
|
+
propertyType:{value:"msg"},
|
|
10
|
+
control:{value:0, required:true},
|
|
11
|
+
controlType:{value:"num"},
|
|
12
|
+
processNoise:{value:1, validate:RED.validators.number(), required:true },
|
|
13
|
+
measurementNoise:{value:1, validate:RED.validators.number(), required:true },
|
|
14
|
+
stateVector:{value:1, validate:RED.validators.number(), required:true },
|
|
15
|
+
controlVector:{value:0, validate:RED.validators.number(), required:true },
|
|
16
|
+
measurementVector:{value:1, validate:RED.validators.number(), required:true },
|
|
17
|
+
adaptionCount:{value:0, validate:RED.validators.number(), required:true },
|
|
18
|
+
contextStore:{value:"none",required:true},
|
|
19
|
+
filter:{value:0, validate:RED.validators.number()},
|
|
20
|
+
filterUnit:{value:"secs"},
|
|
21
|
+
filterVal:{value:0, validate:RED.validators.number()},
|
|
22
|
+
filterMul:{value:10, validate:RED.validators.number()},
|
|
23
|
+
zeroIsZero:{value:false},
|
|
24
|
+
decimals:{validate:RED.validators.number()},
|
|
25
|
+
showState:{value:false}
|
|
26
|
+
},
|
|
27
|
+
inputs:1,
|
|
28
|
+
outputs:1,
|
|
29
|
+
icon:"font-awesome/fa-area-chart",
|
|
30
|
+
label: function() {
|
|
31
|
+
return this.name||this.topic||"kalman";
|
|
32
|
+
},
|
|
33
|
+
labelStyle: function() {
|
|
34
|
+
return this.name?"node_label_italic":"";
|
|
35
|
+
},
|
|
36
|
+
inputLabels: 'input',
|
|
37
|
+
outputLabels: 'output',
|
|
38
|
+
oneditprepare: function() {
|
|
39
|
+
const node = this;
|
|
40
|
+
$("#node-input-property").typedInput({
|
|
41
|
+
type:"msg",
|
|
42
|
+
types:["msg","jsonata"],
|
|
43
|
+
typeField: "#node-input-propertyType"
|
|
44
|
+
});
|
|
45
|
+
$("#node-input-control").typedInput({
|
|
46
|
+
type:"num",
|
|
47
|
+
types:["msg","num"],
|
|
48
|
+
typeField: "#node-input-controlType"
|
|
49
|
+
});
|
|
50
|
+
// populate store names
|
|
51
|
+
$("#node-input-contextStore").empty();
|
|
52
|
+
const defaultStore = RED.settings.hasOwnProperty("context") ? RED.settings.context.default : '';
|
|
53
|
+
RED.settings.context.stores.forEach(function(item){
|
|
54
|
+
if( item ) {
|
|
55
|
+
const opt = $(`<option value="${item}">${item}${item===defaultStore?" (default)":""}</option>`);
|
|
56
|
+
$("#node-input-contextStore").append(opt);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const currentStore = node.contextStore || '';
|
|
60
|
+
// select the current option
|
|
61
|
+
$("#node-input-contextStore").val(currentStore);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<script type="text/html" data-template-name="kalman">
|
|
67
|
+
<div class="form-row">
|
|
68
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> name</label>
|
|
69
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
70
|
+
</div>
|
|
71
|
+
<div class="form-row">
|
|
72
|
+
<label for="node-input-topic"><i class="fa fa-envelope-o"></i> topic</label>
|
|
73
|
+
<input type="text" id="node-input-topic" placeholder="Topic">
|
|
74
|
+
</div>
|
|
75
|
+
<div class="form-row">
|
|
76
|
+
<label for="node-input-property"> measured value</label>
|
|
77
|
+
<input type="text" id="node-input-property">
|
|
78
|
+
<input type="hidden" id="node-input-propertyType">
|
|
79
|
+
</div>
|
|
80
|
+
<div class="form-row">
|
|
81
|
+
<label for="node-input-control"> control value</label>
|
|
82
|
+
<input type="text" id="node-input-control">
|
|
83
|
+
<input type="hidden" id="node-input-controlType">
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="form-row">
|
|
87
|
+
<label for="node-input-processNoise"> process noise</label>
|
|
88
|
+
<input type="text" id="node-input-processNoise">
|
|
89
|
+
</div>
|
|
90
|
+
<div class="form-row">
|
|
91
|
+
<label for="node-input-measurementNoise"> measure noise</label>
|
|
92
|
+
<input type="text" id="node-input-measurementNoise">
|
|
93
|
+
</div>
|
|
94
|
+
<div class="form-row">
|
|
95
|
+
<label for="node-input-stateVector"> process factor</label>
|
|
96
|
+
<input type="text" id="node-input-stateVector">
|
|
97
|
+
</div>
|
|
98
|
+
<div class="form-row">
|
|
99
|
+
<label for="node-input-controlVector"> control factor</label>
|
|
100
|
+
<input type="text" id="node-input-controlVector">
|
|
101
|
+
</div>
|
|
102
|
+
<div class="form-row">
|
|
103
|
+
<label for="node-input-measurementVector"> measur. factor</label>
|
|
104
|
+
<input type="text" id="node-input-measurementVector">
|
|
105
|
+
</div>
|
|
106
|
+
<div class="form-row">
|
|
107
|
+
<label for="node-input-adaptionCount"> adaption counts</label>
|
|
108
|
+
<input type="number" id="node-input-adaptionCount">
|
|
109
|
+
</div>
|
|
110
|
+
<div class="form-row">
|
|
111
|
+
<label for="node-input-contextStore"><i class="fa fa-database"></i> context store</label>
|
|
112
|
+
<select type="text" id="node-input-contextStore"></select>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="form-row">
|
|
116
|
+
<label for="node-input-filter"><i class="fa fa-filter"></i> filter time</label>
|
|
117
|
+
<input type="number" id="node-input-filter" style="width:70px;" placeholder="Time">
|
|
118
|
+
<select id="node-input-filterUnit" style="width:140px;">
|
|
119
|
+
<option value="msecs">MilliSeconds</option>
|
|
120
|
+
<option value="secs">Seconds</option>
|
|
121
|
+
<option value="mins">Minutes</option>
|
|
122
|
+
<option value="hours">Hours</option>
|
|
123
|
+
</select>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="form-row">
|
|
126
|
+
<label for="node-input-filterVal"><i class="fa fa-filter"></i> filter value</label>
|
|
127
|
+
<input type="text" id="node-input-filterVal">
|
|
128
|
+
</div>
|
|
129
|
+
<div class="form-row">
|
|
130
|
+
<label for="node-input-filterMul"><i class="fa fa-filter"></i> filter repetition</label>
|
|
131
|
+
<input type="text" id="node-input-filterMul">
|
|
132
|
+
</div>
|
|
133
|
+
<div class="form-row">
|
|
134
|
+
<label for="node-input-decimals"><i class="fa fa-sort-numeric-asc"></i> decimals</label>
|
|
135
|
+
<input type="number" id="node-input-decimals">
|
|
136
|
+
</div>
|
|
137
|
+
<div class="form-row">
|
|
138
|
+
<label for="node-input-zeroIsZero"><i class="fa fa-exclamation-circle"></i> null is null</label>
|
|
139
|
+
<input type="checkbox" id="node-input-zeroIsZero" style="display:inline-block; width:20px; vertical-align:baseline;">
|
|
140
|
+
</div>
|
|
141
|
+
<div class="form-row">
|
|
142
|
+
<label for="node-input-showStatus"><i class="fa fa-star-o"></i> status</label>
|
|
143
|
+
<input type="checkbox" id="node-input-showState" style="display:inline-block; width:20px; vertical-align:baseline;">
|
|
144
|
+
</div>
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<script type="text/html" data-help-name="kalman">
|
|
148
|
+
|
|
149
|
+
<p>Filter the values of one or more data streams wuth an adaptive Kalman filter.</p>
|
|
150
|
+
<p>If the msg property <code>invalid</code> is present in the message, all nodes ignore the message.</p>
|
|
151
|
+
|
|
152
|
+
<h3>Input</h3>
|
|
153
|
+
|
|
154
|
+
<p>The different inputs are differentiated by topics.</p>
|
|
155
|
+
<p>The message property to be used as payload can be defined with the <code>Property</code> property.</p>
|
|
156
|
+
|
|
157
|
+
<dl class="message-properties">
|
|
158
|
+
<dt>topic <span class="property-type">string</span></dt>
|
|
159
|
+
<dd> name of the input channel.</dd>
|
|
160
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
161
|
+
<dd> input value for <code>topic</code>.</dd>
|
|
162
|
+
<dt>control <span class="property-type">number</span></dt>
|
|
163
|
+
<dd> control value, THe message property can be selected with the <code>Control</code> property.</dd>
|
|
164
|
+
</dl>
|
|
165
|
+
|
|
166
|
+
<h3>Output</h3>
|
|
167
|
+
|
|
168
|
+
<dl class="message-properties">
|
|
169
|
+
<dt>payload <span class="property-type">number</span></dt>
|
|
170
|
+
<dd> filtered value.</dd>
|
|
171
|
+
</dl>
|
|
172
|
+
|
|
173
|
+
<h3>Parameter</h3>
|
|
174
|
+
|
|
175
|
+
<dl class="message-properties">
|
|
176
|
+
<dt>Topic <span class="property-type">string</span></dt>
|
|
177
|
+
<dd> defines the topic for the output message, "" is <code>msg.topic</code>.</dd>
|
|
178
|
+
<dt>Property <span class="property-type">string</span></dt>
|
|
179
|
+
<dd> defines the message property to be used as payload.</dd>
|
|
180
|
+
|
|
181
|
+
<dt>Filterzeit <span class="property-type">number</span></dt>
|
|
182
|
+
<dd> send output only after this time.</dd>
|
|
183
|
+
<dt>Filter Wert <span class="property-type">number</span></dt>
|
|
184
|
+
<dd> send output only if change is bigger.</dd>
|
|
185
|
+
<dt>Filter Wiederholungsfaktor <span class="property-type">number</span></dt>
|
|
186
|
+
<dd> do send output if change is lower then <code>Filter Wert</code> after n times <code>Filterzeit</code>.</dd>
|
|
187
|
+
<dt>Nachkommastellen <span class="property-type">number</span></dt>
|
|
188
|
+
<dd> Anzahl an Nachkommastellen..</dd>
|
|
189
|
+
<dt>Null ist Null <span class="property-type">boolean</span></dt>
|
|
190
|
+
<dd> If 0 is received, 0 is send immediately.</dd>
|
|
191
|
+
<dt>Status <span class="property-type">boolean</span></dt>
|
|
192
|
+
<dd> shows the actual value as a node status.</dd>
|
|
193
|
+
</dl>
|
|
194
|
+
</script>
|
package/math_kalman.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
var tools = require('./tools.js');
|
|
3
|
+
|
|
4
|
+
function KalmanNode(config) {
|
|
5
|
+
RED.nodes.createNode(this,config);
|
|
6
|
+
//this.config = config;
|
|
7
|
+
var node = this;
|
|
8
|
+
var context = this.context();
|
|
9
|
+
this.topic = config.topic ?? "";
|
|
10
|
+
this.property = config.property ?? "payload";
|
|
11
|
+
this.propertyType = config.propertyType ?? "msg";
|
|
12
|
+
this.control = config.control ?? 0;
|
|
13
|
+
this.controlType = config.controlType ?? "num";
|
|
14
|
+
this.processNoise = Number( config.processNoise ?? 1 );
|
|
15
|
+
this.measurementNoise = Number( config.measurementNoise ?? 1 );
|
|
16
|
+
this.stateVector = Number( config.stateVector ?? 1 );
|
|
17
|
+
this.controlVector = Number( config.controlVector ?? 0 );
|
|
18
|
+
this.measurementVector = Number( config.measurementVector ?? 1 );
|
|
19
|
+
this.adaptionCount = Number( config.adaptionCount ?? 0 );
|
|
20
|
+
this.contextStore = config.contextStore ?? "";
|
|
21
|
+
this.filterTime = Number( config.filter ?? 0 ) * tools.timeUnits( config.filterUnit );
|
|
22
|
+
this.filterValue = Number( config.filterVal ?? 0 );
|
|
23
|
+
this.filterLongTime = this.filterTime * Number( config.filterMul ?? 10 );
|
|
24
|
+
this.zeroIsZero = Boolean( config.zeroIsZero );
|
|
25
|
+
this.round = config.decimals ? Math.pow( 10, Number( config.decimals ) ) : null;
|
|
26
|
+
this.showState = Boolean( config.showState );
|
|
27
|
+
this.data = {};
|
|
28
|
+
this.last = {};
|
|
29
|
+
this.adaptation = {};
|
|
30
|
+
if( this.propertyType === "jsonata" )
|
|
31
|
+
{
|
|
32
|
+
try {
|
|
33
|
+
this.propertyPrepared = RED.util.prepareJSONataExpression( this.property, this );
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
node.error(RED._("debug.invalid-exp", {error: this.property}));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
node.status( "" );
|
|
41
|
+
context.get( "data", node.contextStore, function(err,value)
|
|
42
|
+
{
|
|
43
|
+
if( err )
|
|
44
|
+
{
|
|
45
|
+
node.error( err );
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
{
|
|
49
|
+
//console.log( "context read", value );
|
|
50
|
+
if( value !== undefined )
|
|
51
|
+
{
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
for( const i in value )
|
|
54
|
+
{
|
|
55
|
+
value[i].time = now;
|
|
56
|
+
value[i].processNoise = node.processNoise;
|
|
57
|
+
}
|
|
58
|
+
node.data = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
function setStatus(color,text)
|
|
64
|
+
{
|
|
65
|
+
if( node.showState )
|
|
66
|
+
{
|
|
67
|
+
node.status({ fill:color, shape:"dot", text:text });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
node.on('input', function(msg,send,done) {
|
|
72
|
+
if( msg.invalid )
|
|
73
|
+
{
|
|
74
|
+
done();
|
|
75
|
+
}
|
|
76
|
+
else if( msg.reset || msg.topic==="init" )
|
|
77
|
+
{
|
|
78
|
+
node.data = {};
|
|
79
|
+
node.last = {};
|
|
80
|
+
node.adaptation = {};
|
|
81
|
+
context.set( "data", node.data, node.contextStore );
|
|
82
|
+
node.status( "" );
|
|
83
|
+
done();
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
{
|
|
87
|
+
function getPayload(callback)
|
|
88
|
+
{
|
|
89
|
+
if( node.propertyPrepared )
|
|
90
|
+
{
|
|
91
|
+
RED.util.evaluateJSONataExpression( node.propertyPrepared, msg, function(err, value)
|
|
92
|
+
{
|
|
93
|
+
if( err )
|
|
94
|
+
{
|
|
95
|
+
done( err.message );
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
{
|
|
99
|
+
callback( value );
|
|
100
|
+
}
|
|
101
|
+
} );
|
|
102
|
+
}
|
|
103
|
+
else
|
|
104
|
+
{
|
|
105
|
+
callback( RED.util.getMessageProperty( msg, node.property ) );
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
getPayload( function(value)
|
|
109
|
+
{
|
|
110
|
+
if( node.topic )
|
|
111
|
+
{
|
|
112
|
+
msg.topic = node.topic;
|
|
113
|
+
}
|
|
114
|
+
const payload = Number( value );
|
|
115
|
+
if( ! isNaN( payload ) )
|
|
116
|
+
{
|
|
117
|
+
const control = RED.util.evaluateNodeProperty( node.control, node.controlType, node, msg );
|
|
118
|
+
if( ! isNaN( control ) )
|
|
119
|
+
{
|
|
120
|
+
node.data[msg.topic] ??= {};
|
|
121
|
+
const data = node.data[msg.topic];
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
|
|
124
|
+
function kalmanFilter(measurement,control)
|
|
125
|
+
{
|
|
126
|
+
if( data.estimate === undefined )
|
|
127
|
+
{
|
|
128
|
+
data.processNoise = node.processNoise;
|
|
129
|
+
data.estimate = measurement / node.measurementVector;
|
|
130
|
+
data.covariance = node.measurementNoise / node.measurementVector**2;
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
{
|
|
134
|
+
// measurement time
|
|
135
|
+
const deltaTime = ( now - data.time ) / 1000;
|
|
136
|
+
const stateVector = 1 + ( node.stateVector - 1 ) * deltaTime;
|
|
137
|
+
const controlVector = node.controlVector * deltaTime;
|
|
138
|
+
// prediction
|
|
139
|
+
const predX = stateVector * data.estimate + controlVector * control;
|
|
140
|
+
const predCov = stateVector**2 * data.covariance + node.processNoise;
|
|
141
|
+
// innovation
|
|
142
|
+
const innovation = measurement - node.measurementVector * predX;
|
|
143
|
+
// adaptation
|
|
144
|
+
if( node.adaptionCount )
|
|
145
|
+
{
|
|
146
|
+
const adaptation = node.adaptation[msg.topic] ?? [];
|
|
147
|
+
adaptation.push( innovation );
|
|
148
|
+
if( adaptation.length > node.adaptionCount )
|
|
149
|
+
{
|
|
150
|
+
adaptation.shift();
|
|
151
|
+
const mean = adaptation.reduce( (a,b) => a + b ) / adaptation.length;
|
|
152
|
+
const variance = adaptation.reduce( (a,b) => a + ( b - mean ) ** 2, 0 ) / ( adaptation.length - 1 );
|
|
153
|
+
data.r = Math.max( variance, 1e-6 );
|
|
154
|
+
data.r2 = ( 2 * variance + node.processNoise ) / 3;
|
|
155
|
+
}
|
|
156
|
+
node.adaptation[msg.topic] = adaptation;
|
|
157
|
+
}
|
|
158
|
+
// kalman gain
|
|
159
|
+
const gain = predCov * node.measurementVector / ( predCov * node.measurementVector**2 + node.measurementNoise );
|
|
160
|
+
// correction
|
|
161
|
+
data.estimate = predX + gain * innovation;
|
|
162
|
+
data.covariance = ( 1 - gain * node.measurementVector ) * predCov;
|
|
163
|
+
}
|
|
164
|
+
data.time = now;
|
|
165
|
+
return data.estimate;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function sendValue(value)
|
|
169
|
+
{
|
|
170
|
+
msg.payload = value;
|
|
171
|
+
node.last[msg.topic] = {value:value,time:now};
|
|
172
|
+
setStatus( "green", tools.formatNumber(value) );
|
|
173
|
+
send( msg );
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if( node.zeroIsZero && payload === 0 )
|
|
177
|
+
{
|
|
178
|
+
data.estimate = 0;
|
|
179
|
+
sendValue( 0 );
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
{
|
|
183
|
+
let value = kalmanFilter( payload, control );
|
|
184
|
+
const help = node.last[msg.topic];
|
|
185
|
+
if( help === undefined ||
|
|
186
|
+
( help.time + node.filterTime < now && tools.distance( help.value, value ) >= node.filterValue ) ||
|
|
187
|
+
( node.filterLongTime > 0 && help.time + node.filterLongTime < now ) )
|
|
188
|
+
{
|
|
189
|
+
if( node.round )
|
|
190
|
+
{
|
|
191
|
+
value = Math.round( value * node.round ) / node.round;
|
|
192
|
+
}
|
|
193
|
+
sendValue( value );
|
|
194
|
+
}
|
|
195
|
+
else
|
|
196
|
+
{
|
|
197
|
+
setStatus( "gray", tools.formatNumber(value) );
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
context.set( "data", node.data, node.contextStore );
|
|
201
|
+
}
|
|
202
|
+
else
|
|
203
|
+
{
|
|
204
|
+
setStatus( "red", "control is NaN" );
|
|
205
|
+
node.warn( `control is NaN (${msg.topic}=${control})` );
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
{
|
|
210
|
+
if( value !== undefined )
|
|
211
|
+
{
|
|
212
|
+
setStatus( "red", "payload is NaN" );
|
|
213
|
+
node.warn( `payload is NaN (${msg.topic}=${value})` );
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
done();
|
|
217
|
+
} );
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
RED.nodes.registerType("kalman",KalmanNode);
|
|
223
|
+
}
|
package/math_mean.html
CHANGED
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<input type="text" id="node-input-topic" placeholder="Topic">
|
|
62
62
|
</div>
|
|
63
63
|
<div class="form-row">
|
|
64
|
-
<label for="node-input-
|
|
64
|
+
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> Eigenschaft</label>
|
|
65
65
|
<input type="text" id="node-input-property">
|
|
66
66
|
<input type="hidden" id="node-input-propertyType">
|
|
67
67
|
</div>
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
<dd> do send output if change is lower then <code>Filter Wert</code> after n times <code>Filterzeit</code>.</dd>
|
|
155
155
|
<dt>Nachkommastellen <span class="property-type">number</span></dt>
|
|
156
156
|
<dd> Anzahl an Nachkommastellen..</dd>
|
|
157
|
-
<dt>Null ist
|
|
157
|
+
<dt>Null ist Null <span class="property-type">boolean</span></dt>
|
|
158
158
|
<dd> If 0 is received, 0 is send immediately.</dd>
|
|
159
159
|
<dt>Status <span class="property-type">boolean</span></dt>
|
|
160
160
|
<dd> shows the actual value as a node status.</dd>
|
package/math_mean.js
CHANGED
|
@@ -6,8 +6,8 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
|
-
this.topic = config.topic
|
|
10
|
-
this.property = config.property
|
|
9
|
+
this.topic = config.topic ?? "";
|
|
10
|
+
this.property = config.property ?? "payload";
|
|
11
11
|
this.propertyType = config.propertyType ?? "msg";
|
|
12
12
|
this.deltaTime = Number( config.deltaTime ?? 60 ) * tools.timeUnits( config.deltaUnit );
|
|
13
13
|
this.minData = Number( config.minData ?? 1 );
|
|
@@ -75,6 +75,10 @@ module.exports = function(RED) {
|
|
|
75
75
|
}
|
|
76
76
|
getPayload( function(value)
|
|
77
77
|
{
|
|
78
|
+
if( node.topic )
|
|
79
|
+
{
|
|
80
|
+
msg.topic = node.topic;
|
|
81
|
+
}
|
|
78
82
|
const payload = Number( value );
|
|
79
83
|
if( ! isNaN( payload ) )
|
|
80
84
|
{
|
|
@@ -95,10 +99,6 @@ module.exports = function(RED) {
|
|
|
95
99
|
msg.count = count;
|
|
96
100
|
last[msg.topic] = {value:value,time:now};
|
|
97
101
|
context.set( "last", last );
|
|
98
|
-
if( node.topic )
|
|
99
|
-
{
|
|
100
|
-
msg.topic = node.topic;
|
|
101
|
-
}
|
|
102
102
|
node.status({fill:"green",shape:"dot",text:`${item.length} / ${tools.formatNumber(value)}`});
|
|
103
103
|
send( msg );
|
|
104
104
|
}
|
package/math_reduce.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = function(RED) {
|
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
9
|
this.topic = config.topic ?? "";
|
|
10
|
-
this.property = config.property
|
|
10
|
+
this.property = config.property ?? "payload";
|
|
11
11
|
this.propertyType = config.propertyType ?? "msg";
|
|
12
12
|
this.minMean = Number( config.minMean ?? 1 );
|
|
13
13
|
this.maxMean = Number( config.maxMean ?? 1 );
|
package/math_rising.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
|
-
this.topic = config.topic
|
|
9
|
+
this.topic = config.topic ?? "";
|
|
10
10
|
this.property = config.property ?? "payload";
|
|
11
11
|
this.propertyType = config.propertyType ?? "msg";
|
|
12
12
|
this.threshold = Number( config.threshold );
|
package/math_statistics.html
CHANGED
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
});
|
|
41
41
|
// populate store names
|
|
42
42
|
$("#node-input-contextStore").empty();
|
|
43
|
-
$("#node-input-contextStore").append(`<option value="none">None</option>`);
|
|
44
43
|
const defaultStore = RED.settings.hasOwnProperty('context') ? RED.settings.context.default : ''
|
|
45
44
|
RED.settings.context.stores.forEach(function(item){
|
|
46
45
|
if( item ) {
|
package/math_statistics.js
CHANGED
|
@@ -6,11 +6,11 @@ module.exports = function(RED) {
|
|
|
6
6
|
//this.config = config;
|
|
7
7
|
var node = this;
|
|
8
8
|
var context = this.context();
|
|
9
|
-
this.property = config.property
|
|
9
|
+
this.property = config.property ?? "payload";
|
|
10
10
|
this.propertyType = config.propertyType ?? "msg";
|
|
11
11
|
this.deltaTime = Number( config.deltaTime ?? 60 ) * tools.timeUnits( config.deltaUnit );
|
|
12
12
|
this.minData = Number( config.minData ?? 1 );
|
|
13
|
-
this.contextStore = config.contextStore ?? "
|
|
13
|
+
this.contextStore = config.contextStore ?? "";
|
|
14
14
|
this.showState = Boolean( config.showState );
|
|
15
15
|
if( this.propertyType === "jsonata" )
|
|
16
16
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mschaeffler/node-red-logic",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "nodes for simple operations.",
|
|
5
5
|
"license": "LGPL-2.1",
|
|
6
6
|
"homepage": "https://github.com/m-schaeffler/node-red-my-nodes/tree/main/node-red-logic",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"or": "logic_or.js",
|
|
24
24
|
"and": "logic_and.js",
|
|
25
25
|
"timerelay": "logic_timerelay.js",
|
|
26
|
+
"blinker": "logic_blinker.js",
|
|
26
27
|
"tonumber": "math_number.js",
|
|
27
28
|
"negate": "math_minus.js",
|
|
28
29
|
"reduce": "math_reduce.js",
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
"hysteresisEdge": "math_hysteresis.js",
|
|
32
33
|
"threePoint": "math_3point.js",
|
|
33
34
|
"statistics": "math_statistics.js",
|
|
34
|
-
"mean": "math_mean.js"
|
|
35
|
+
"mean": "math_mean.js",
|
|
36
|
+
"kalman": "math_kalman.js"
|
|
35
37
|
}
|
|
36
38
|
},
|
|
37
39
|
"author": {
|
|
@@ -39,6 +41,6 @@
|
|
|
39
41
|
"url": "https://github.com/m-schaeffler"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
42
|
-
"test": "mocha \"test
|
|
44
|
+
"test": "mocha \"test/math_kalman_spec.js\""
|
|
43
45
|
}
|
|
44
46
|
}
|
package/tools.js
CHANGED