smart-nodes 0.3.22 → 0.3.26
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/LICENSE.md +1 -1
- package/README.md +51 -21
- package/central/central.js +10 -8
- package/compare/compare.html +48 -0
- package/compare/compare.js +36 -20
- package/counter/counter.html +104 -0
- package/counter/counter.js +120 -0
- package/delay/delay.js +39 -37
- package/examples/heating-curve.json +480 -0
- package/examples/heating-curve.png +0 -0
- package/examples/mixing-valve.json +622 -0
- package/examples/mixing-valve.png +0 -0
- package/forwarder/forwarder.js +28 -26
- package/heating-curve/heating-curve.html +348 -0
- package/heating-curve/heating-curve.js +134 -0
- package/hysteresis/hysteresis.js +47 -46
- package/icons/smart_counter.png +0 -0
- package/light-control/light-control.js +30 -28
- package/logic/logic.html +18 -2
- package/logic/logic.js +25 -23
- package/long-press-control/long-press-control.js +6 -4
- package/mixing-valve/mixing-valve.html +188 -0
- package/mixing-valve/mixing-valve.js +357 -0
- package/multi-press-control/multi-press-control.js +3 -1
- package/package.json +8 -1
- package/scene-control/scene-control.js +29 -27
- package/scheduler/scheduler.js +27 -25
- package/shutter-complex-control/shutter-complex-control.js +62 -62
- package/shutter-control/shutter-control.js +39 -37
- package/smart_helper.js +135 -20
- package/statistic/statistic.js +30 -28
- package/text-exec/text-exec.js +3 -1
- package/LICENSE +0 -21
package/LICENSE.md
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# General information
|
|
2
2
|
|
|
3
3
|
The smart nodes was created to control smart home devices like lights, power outlets shutter and some more.
|
|
4
|
-
This
|
|
4
|
+
This nodes are designed to work with the [node-red-contrib-knx-ultimate](https://github.com/Supergiovane/node-red-contrib-knx-ultimate) but it could also work with other smart technologies.
|
|
5
5
|
|
|
6
6
|
The knx binary input and output/switching modules should be configured very stupid. Closing a binary input should output 1 and releasing 0.
|
|
7
7
|
Same for the output/switching modules, 1 should turn on and 0 turn out. That makes it easy to count the presses or check for short and long presses.
|
|
@@ -62,8 +62,8 @@ This node can control multiple light/scene controls or shutter controls at the s
|
|
|
62
62
|
|
|
63
63
|
## 5. Long press control
|
|
64
64
|
|
|
65
|
-
This
|
|
66
|
-
The time can be configured in this
|
|
65
|
+
This node is used to detect a short or a long press.
|
|
66
|
+
The time can be configured in this node.
|
|
67
67
|
|
|
68
68
|
### **Features:**
|
|
69
69
|
|
|
@@ -71,8 +71,8 @@ The time can be configured in this control.
|
|
|
71
71
|
|
|
72
72
|
## 6. Multi press control
|
|
73
73
|
|
|
74
|
-
This
|
|
75
|
-
The max wait time between presses can be configured in this
|
|
74
|
+
This node is used to detect multiple presses on a button.
|
|
75
|
+
The max wait time between presses can be configured in this node.
|
|
76
76
|
You can also choose 2-4 press detection.
|
|
77
77
|
|
|
78
78
|
### **Features:**
|
|
@@ -81,7 +81,7 @@ You can also choose 2-4 press detection.
|
|
|
81
81
|
|
|
82
82
|
## 7. Hysteresis
|
|
83
83
|
|
|
84
|
-
This
|
|
84
|
+
This node is checking if the input value reachs a defined value until the upper message is send. When the lower level is reached, the lower masssage will be send
|
|
85
85
|
|
|
86
86
|
### **Features:**
|
|
87
87
|
|
|
@@ -90,7 +90,7 @@ This control is checking if the input value reachs a defined value until the upp
|
|
|
90
90
|
|
|
91
91
|
## 8. Logic
|
|
92
92
|
|
|
93
|
-
This
|
|
93
|
+
This node can be used for AND, OR and XOR logics.
|
|
94
94
|
|
|
95
95
|
### **Features:**
|
|
96
96
|
|
|
@@ -102,7 +102,7 @@ This control can be used for AND, OR and XOR logics.
|
|
|
102
102
|
|
|
103
103
|
## 9. Statistic
|
|
104
104
|
|
|
105
|
-
This
|
|
105
|
+
This node can be used for getting the following types of values: Minimum, Maximum, Sum, Difference, Absolute Value, Absolute Difference, Average and Running average.
|
|
106
106
|
|
|
107
107
|
### **Features:**
|
|
108
108
|
|
|
@@ -113,7 +113,7 @@ This control can be used for getting the following types of values: Minimum, Max
|
|
|
113
113
|
|
|
114
114
|
## 10. Compare
|
|
115
115
|
|
|
116
|
-
This
|
|
116
|
+
This node can compare 2 values with the following operators: < <= == >= > !=
|
|
117
117
|
|
|
118
118
|
### **Features:**
|
|
119
119
|
|
|
@@ -123,7 +123,7 @@ This control can compare 2 values with the following operators: < <= == >= > !=
|
|
|
123
123
|
|
|
124
124
|
## 11. Delay
|
|
125
125
|
|
|
126
|
-
This
|
|
126
|
+
This node can delay incomming messages.
|
|
127
127
|
|
|
128
128
|
### **Features:**
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ This control can delay incomming messages.
|
|
|
134
134
|
|
|
135
135
|
## 12. Forwarder
|
|
136
136
|
|
|
137
|
-
This
|
|
137
|
+
This node can control if an incomming message should be forwarded or not.
|
|
138
138
|
|
|
139
139
|
### **Features:**
|
|
140
140
|
|
|
@@ -144,7 +144,7 @@ This control can control if an incomming message should be forwarded or not.
|
|
|
144
144
|
|
|
145
145
|
## 13. Scheduler
|
|
146
146
|
|
|
147
|
-
This
|
|
147
|
+
This node can send a defined message on defined times.
|
|
148
148
|
|
|
149
149
|
### **Features:**
|
|
150
150
|
|
|
@@ -156,20 +156,50 @@ This control can send a defined message on defined times.
|
|
|
156
156
|
|
|
157
157
|
## 14. Text execution
|
|
158
158
|
|
|
159
|
-
This
|
|
159
|
+
This node parses a text and performs actions to the selected and matching smart nodes
|
|
160
160
|
|
|
161
161
|
### **Features:**
|
|
162
162
|
|
|
163
|
-
- Control light and scenes (only on or off) in german and english
|
|
164
|
-
- Control shutter in german and english
|
|
163
|
+
- Control light and scenes (only on or off) in german and english.
|
|
164
|
+
- Control shutter in german and english.
|
|
165
165
|
|
|
166
166
|
### Examples
|
|
167
167
|
|
|
168
168
|
[ text ] is optional
|
|
169
169
|
|
|
170
|
-
- Turn on [the light in] the living room and the kitchen off
|
|
171
|
-
- Open [the shutter in] the kitchen and turn studio off
|
|
172
|
-
- Close [the shutter in the] sleeping room
|
|
173
|
-
- Living room to 10
|
|
174
|
-
- Turn on living room except the couch light
|
|
175
|
-
- Sends debug message to output
|
|
170
|
+
- Turn on [the light in] the living room and the kitchen off.
|
|
171
|
+
- Open [the shutter in] the kitchen and turn studio off.
|
|
172
|
+
- Close [the shutter in the] sleeping room.
|
|
173
|
+
- Living room to 10 %.
|
|
174
|
+
- Turn on living room except the couch light.
|
|
175
|
+
- Sends debug message to output.
|
|
176
|
+
|
|
177
|
+
## 15. Mixing valve
|
|
178
|
+
|
|
179
|
+
This node can control a mixing valve to get the required tput temperature. It is working for heating and cooling.
|
|
180
|
+
|
|
181
|
+
### **Features:**
|
|
182
|
+
|
|
183
|
+
- Define target temperature.
|
|
184
|
+
- Support heating and cooling.
|
|
185
|
+
- Define off mode to do nothing, open or close the valve.
|
|
186
|
+
|
|
187
|
+
## 16. Heating curve
|
|
188
|
+
|
|
189
|
+
This node calculates the needed flow temperature regarding to the target room temperature and the current outside temperature.
|
|
190
|
+
|
|
191
|
+
### **Features:**
|
|
192
|
+
|
|
193
|
+
- Define slope and offset values.
|
|
194
|
+
- Limit flow temperature with min and max values.
|
|
195
|
+
- Preview of different curves in the node editor.
|
|
196
|
+
|
|
197
|
+
## 17. Counter
|
|
198
|
+
|
|
199
|
+
This node countes up and down within a specified range.
|
|
200
|
+
|
|
201
|
+
### **Features:**
|
|
202
|
+
|
|
203
|
+
- Define min, max and step values.
|
|
204
|
+
- Set to a specific value
|
|
205
|
+
- Increment and decrement by default or by a given value.
|
package/central/central.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module.exports = function (RED)
|
|
2
2
|
{
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
3
5
|
function CentralControlNode(config)
|
|
4
6
|
{
|
|
5
7
|
const node = this;
|
|
@@ -33,7 +35,7 @@ module.exports = function (RED)
|
|
|
33
35
|
node.on("input", function (msg)
|
|
34
36
|
{
|
|
35
37
|
// copy message, so original message stays unchanged
|
|
36
|
-
let
|
|
38
|
+
let new_msg = Object.assign({}, msg);
|
|
37
39
|
|
|
38
40
|
// make connected nodes behavior homogenous
|
|
39
41
|
switch (config.mode)
|
|
@@ -42,16 +44,16 @@ module.exports = function (RED)
|
|
|
42
44
|
if (msg.topic == "up_stop")
|
|
43
45
|
{
|
|
44
46
|
if (isAllOff())
|
|
45
|
-
|
|
47
|
+
new_msg.topic = "up";
|
|
46
48
|
else
|
|
47
|
-
|
|
49
|
+
new_msg.topic = "stop";
|
|
48
50
|
}
|
|
49
51
|
else if (msg.topic == "down_stop")
|
|
50
52
|
{
|
|
51
53
|
if (isAllOff())
|
|
52
|
-
|
|
54
|
+
new_msg.topic = "down";
|
|
53
55
|
else
|
|
54
|
-
|
|
56
|
+
new_msg.topic = "stop";
|
|
55
57
|
}
|
|
56
58
|
break;
|
|
57
59
|
|
|
@@ -59,9 +61,9 @@ module.exports = function (RED)
|
|
|
59
61
|
if (msg.topic == "toggle")
|
|
60
62
|
{
|
|
61
63
|
if (isAllOff())
|
|
62
|
-
|
|
64
|
+
new_msg.topic = "on";
|
|
63
65
|
else
|
|
64
|
-
|
|
66
|
+
new_msg.topic = "off";
|
|
65
67
|
}
|
|
66
68
|
break;
|
|
67
69
|
}
|
|
@@ -71,7 +73,7 @@ module.exports = function (RED)
|
|
|
71
73
|
{
|
|
72
74
|
// console.log(node.id + " -> " + link);
|
|
73
75
|
// console.log(newMsg);
|
|
74
|
-
RED.events.emit("node:" + link,
|
|
76
|
+
RED.events.emit("node:" + link, new_msg);
|
|
75
77
|
});
|
|
76
78
|
});
|
|
77
79
|
|
package/compare/compare.html
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
category: "Smart Nodes",
|
|
4
4
|
paletteLabel: "Compare",
|
|
5
5
|
color: "#E2D96E",
|
|
6
|
+
color: "#E2D96E",
|
|
6
7
|
defaults: {
|
|
7
8
|
name: { value: "" },
|
|
8
9
|
comparator: { value: "EQUAL" }, // EQUAL, GREATER, GREATER_EQUAL, SMALLER, SMALLER_EQUAL, UNEQUAL
|
|
@@ -10,6 +11,10 @@
|
|
|
10
11
|
value1_type: { value: "null" },
|
|
11
12
|
value2: { value: "" },
|
|
12
13
|
value2_type: { value: "null" },
|
|
14
|
+
out_false: { value: '{"topic": ""}' },
|
|
15
|
+
out_false_type: { value: 'json' },
|
|
16
|
+
out_true: { value: '{"topic": ""}' },
|
|
17
|
+
out_true_type: { value: 'json' },
|
|
13
18
|
save_state: { value: true },
|
|
14
19
|
resend_on_start: { value: true }
|
|
15
20
|
},
|
|
@@ -60,6 +65,28 @@
|
|
|
60
65
|
typeField: "#node-input-value2_type"
|
|
61
66
|
});
|
|
62
67
|
|
|
68
|
+
$("#node-input-out_true").typedInput({
|
|
69
|
+
type: "json",
|
|
70
|
+
types: ["json", {
|
|
71
|
+
value: "null",
|
|
72
|
+
label: "Nichts senden",
|
|
73
|
+
icon: "fa fa-times",
|
|
74
|
+
hasValue: false,
|
|
75
|
+
}],
|
|
76
|
+
typeField: "#node-input-out_true_type"
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
$("#node-input-out_false").typedInput({
|
|
80
|
+
type: "json",
|
|
81
|
+
types: ["json", {
|
|
82
|
+
value: "null",
|
|
83
|
+
label: "Nichts senden",
|
|
84
|
+
icon: "fa fa-times",
|
|
85
|
+
hasValue: false,
|
|
86
|
+
}],
|
|
87
|
+
typeField: "#node-input-out_false_type"
|
|
88
|
+
});
|
|
89
|
+
|
|
63
90
|
$("#node-input-save_state").on("change", ev =>
|
|
64
91
|
{
|
|
65
92
|
if (ev.target.checked)
|
|
@@ -95,6 +122,27 @@
|
|
|
95
122
|
<input type="hidden" id="node-input-value2_type">
|
|
96
123
|
</div>
|
|
97
124
|
<hr/>
|
|
125
|
+
<h4 style="margin: 0.5rem 0;">Ausgangsnachrichten</h4>
|
|
126
|
+
<div class="form-row">
|
|
127
|
+
<label for="node-input-out_true"><i class="fa fa-check-circle"></i> Wahr</label>
|
|
128
|
+
<input type="text" id="node-input-out_true"/>
|
|
129
|
+
<input type="hidden" id="node-input-out_true_type">
|
|
130
|
+
</div>
|
|
131
|
+
<div class="form-row">
|
|
132
|
+
<label for="node-input-out_false"><i class="fa fa-times-circle"></i> Falsch</label>
|
|
133
|
+
<input type="text" id="node-input-out_false" />
|
|
134
|
+
<input type="hidden" id="node-input-out_false_type">
|
|
135
|
+
</div>
|
|
136
|
+
<div class="form-row">
|
|
137
|
+
<span><strong>Hinweis:</strong></span>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="form-row">
|
|
140
|
+
<span>
|
|
141
|
+
<code>msg.payload</code> wird automatisch auf das Logik-Ergebnis gesetzt.<br/>
|
|
142
|
+
<code>msg.comperator</code> wird automatisch auf den Logik-Operator gesetzt.
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
145
|
+
<hr/>
|
|
98
146
|
<h4 style="margin: 0.5rem 0;">Systemstart</h4>
|
|
99
147
|
<div class="form-row">
|
|
100
148
|
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
package/compare/compare.js
CHANGED
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
module.exports = function (RED)
|
|
2
2
|
{
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
3
5
|
function CompareNode(config)
|
|
4
6
|
{
|
|
5
7
|
const node = this;
|
|
6
8
|
RED.nodes.createNode(node, config);
|
|
7
9
|
|
|
8
|
-
const
|
|
10
|
+
const smart_context = require("../persistence.js")(RED);
|
|
9
11
|
const helper = require("../smart_helper.js");
|
|
10
12
|
|
|
11
|
-
var
|
|
13
|
+
var node_settings = {
|
|
12
14
|
values: [
|
|
13
15
|
helper.evaluateNodeProperty(RED, config.value1, config.value1_type),
|
|
14
16
|
helper.evaluateNodeProperty(RED, config.value2, config.value2_type)
|
|
15
17
|
],
|
|
16
|
-
|
|
18
|
+
last_message: null,
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
if (config.save_state)
|
|
20
22
|
{
|
|
21
23
|
// load old saved values
|
|
22
|
-
|
|
24
|
+
node_settings = Object.assign(node_settings, smart_context.get(node.id));
|
|
23
25
|
}
|
|
24
26
|
else
|
|
25
27
|
{
|
|
26
28
|
// delete old saved values
|
|
27
|
-
|
|
29
|
+
smart_context.del(node.id);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// dynamic config
|
|
31
33
|
let comparator = config.comparator;
|
|
34
|
+
let out_true = helper.evaluateNodeProperty(RED, config.out_true, config.out_true_type);
|
|
35
|
+
let out_false = helper.evaluateNodeProperty(RED, config.out_false, config.out_false_type);
|
|
32
36
|
|
|
33
37
|
// runtime values
|
|
34
38
|
|
|
@@ -51,47 +55,59 @@ module.exports = function (RED)
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
// Save new value
|
|
54
|
-
|
|
58
|
+
node_settings.values[input - 1] = num;
|
|
55
59
|
|
|
56
60
|
let result = getResult();
|
|
57
61
|
setStatus(result);
|
|
58
62
|
|
|
59
63
|
if (result != null)
|
|
60
64
|
{
|
|
61
|
-
|
|
65
|
+
let msg = result ? out_true : out_false;
|
|
66
|
+
|
|
67
|
+
if (msg != null)
|
|
68
|
+
{
|
|
69
|
+
msg = Object.assign(
|
|
70
|
+
{},
|
|
71
|
+
msg,
|
|
72
|
+
{ payload: result, comparator: comparator }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
node_settings.last_message = msg;
|
|
62
77
|
|
|
63
78
|
if (config.save_state)
|
|
64
|
-
|
|
79
|
+
smart_context.set(node.id, node_settings);
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
if (msg != null)
|
|
82
|
+
node.send(msg);
|
|
67
83
|
}
|
|
68
84
|
});
|
|
69
85
|
|
|
70
86
|
let getResult = () =>
|
|
71
87
|
{
|
|
72
88
|
// Wait until both values are set
|
|
73
|
-
if (
|
|
89
|
+
if (node_settings.values[0] == null || node_settings.values[1] == null)
|
|
74
90
|
return null;
|
|
75
91
|
|
|
76
92
|
switch (comparator)
|
|
77
93
|
{
|
|
78
94
|
case "SMALLER":
|
|
79
|
-
return
|
|
95
|
+
return node_settings.values[0] < node_settings.values[1];
|
|
80
96
|
|
|
81
97
|
case "SMALLER_EQUAL":
|
|
82
|
-
return
|
|
98
|
+
return node_settings.values[0] <= node_settings.values[1];
|
|
83
99
|
|
|
84
100
|
case "EQUAL":
|
|
85
|
-
return
|
|
101
|
+
return node_settings.values[0] === node_settings.values[1];
|
|
86
102
|
|
|
87
103
|
case "UNEQUAL":
|
|
88
|
-
return
|
|
104
|
+
return node_settings.values[0] !== node_settings.values[1];
|
|
89
105
|
|
|
90
106
|
case "GREATER_EQUAL":
|
|
91
|
-
return
|
|
107
|
+
return node_settings.values[0] >= node_settings.values[1];
|
|
92
108
|
|
|
93
109
|
case "GREATER":
|
|
94
|
-
return
|
|
110
|
+
return node_settings.values[0] > node_settings.values[1];
|
|
95
111
|
}
|
|
96
112
|
|
|
97
113
|
return null;
|
|
@@ -129,22 +145,22 @@ module.exports = function (RED)
|
|
|
129
145
|
if (result == null)
|
|
130
146
|
node.status({});
|
|
131
147
|
else
|
|
132
|
-
node.status({ fill: "yellow", shape: "ring", text:
|
|
148
|
+
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": " + node_settings.values.join(" " + getComparatorSign() + " ") + " => " + result });
|
|
133
149
|
}
|
|
134
150
|
|
|
135
151
|
node.on("close", function ()
|
|
136
152
|
{
|
|
137
153
|
});
|
|
138
154
|
|
|
139
|
-
if (config.save_state && config.resend_on_start &&
|
|
155
|
+
if (config.save_state && config.resend_on_start && node_settings.last_message != null)
|
|
140
156
|
{
|
|
141
157
|
setTimeout(() =>
|
|
142
158
|
{
|
|
143
|
-
node.send(
|
|
159
|
+
node.send(node_settings.last_message);
|
|
144
160
|
}, 10000);
|
|
145
161
|
}
|
|
146
162
|
|
|
147
|
-
setStatus(
|
|
163
|
+
setStatus(node_settings.last_message?.payload);
|
|
148
164
|
}
|
|
149
165
|
|
|
150
166
|
RED.nodes.registerType("smart_compare", CompareNode);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("smart_counter", {
|
|
3
|
+
category: "Smart Nodes",
|
|
4
|
+
paletteLabel: "Counter",
|
|
5
|
+
color: "#6EE2D9",
|
|
6
|
+
defaults: {
|
|
7
|
+
name: { value: "" },
|
|
8
|
+
start: { value: 0 },
|
|
9
|
+
step: { value: 1 },
|
|
10
|
+
min: { value: 0 },
|
|
11
|
+
max: { value: 10 },
|
|
12
|
+
save_state: { value: true },
|
|
13
|
+
resend_on_start: { value: true }
|
|
14
|
+
},
|
|
15
|
+
inputs: 1,
|
|
16
|
+
outputs: 1,
|
|
17
|
+
icon: "smart_counter.png",
|
|
18
|
+
label: function ()
|
|
19
|
+
{
|
|
20
|
+
return this.name || "Counter";
|
|
21
|
+
},
|
|
22
|
+
oneditprepare: function ()
|
|
23
|
+
{
|
|
24
|
+
$("#node-input-save_state").on("change", ev =>
|
|
25
|
+
{
|
|
26
|
+
if (ev.target.checked)
|
|
27
|
+
$("#resend_on_start_row").show();
|
|
28
|
+
else
|
|
29
|
+
$("#resend_on_start_row").hide();
|
|
30
|
+
});
|
|
31
|
+
$("#node-input-save_state").trigger("change");
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script type="text/html" data-template-name="smart_counter">
|
|
37
|
+
<div class="form-row">
|
|
38
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
39
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
40
|
+
</div>
|
|
41
|
+
<div class="form-row">
|
|
42
|
+
<label for="node-input-start"><i class="fa fa-cog"></i> Start</label>
|
|
43
|
+
<input type="number" id="node-input-start" placeholder="Startwert">
|
|
44
|
+
</div>
|
|
45
|
+
<div class="form-row">
|
|
46
|
+
<label for="node-input-step"><i class="fa fa-forward"></i> Schritt</label>
|
|
47
|
+
<input type="number" id="node-input-step" placeholder="Schrittweite">
|
|
48
|
+
</div>
|
|
49
|
+
<div class="form-row">
|
|
50
|
+
<label for="node-input-min"><i class="fa fa-arrow-down"></i> Min</label>
|
|
51
|
+
<input type="number" id="node-input-min" placeholder="Min">
|
|
52
|
+
</div>
|
|
53
|
+
<div class="form-row">
|
|
54
|
+
<label for="node-input-max"><i class="fa fa-arrow-up"></i> Max</label>
|
|
55
|
+
<input type="number" id="node-input-max" placeholder="Max">
|
|
56
|
+
</div>
|
|
57
|
+
<hr/>
|
|
58
|
+
<h4 style="margin: 0.5rem 0;">Systemstart</h4>
|
|
59
|
+
<div class="form-row">
|
|
60
|
+
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
|
61
|
+
<label for="node-input-save_state" style="width: calc(100% - 30px);">Zustand speichern</label>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="form-row" id="resend_on_start_row">
|
|
64
|
+
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
65
|
+
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);">Letze Nachricht 10 Sekunden nach dem Start senden</label>
|
|
66
|
+
</div>
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<script type="text/html" data-help-name="smart_counter">
|
|
70
|
+
<p>Diese Node kann verwendet werden um nach oben oder unten zu zählen.</p>
|
|
71
|
+
<p>
|
|
72
|
+
Die Standardschrittweite kann vorgegeben oder durch einkommende Nachrichten temporär überschrieben werden.
|
|
73
|
+
Es sind auch Kommazahlen möglich.
|
|
74
|
+
</p>
|
|
75
|
+
<p>
|
|
76
|
+
<b>Hinweis:</b> Smart Nodes verwenden Topics im Format <code>name#nummer</code>, damit können verschiedene Smart Nodes mit dem gleichen Topic angesteuert werden.<br/>
|
|
77
|
+
Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
|
|
78
|
+
</p>
|
|
79
|
+
<p>
|
|
80
|
+
Folgende topics werden akzeptiert:
|
|
81
|
+
<table>
|
|
82
|
+
<thead>
|
|
83
|
+
<tr>
|
|
84
|
+
<th>Topic</th>
|
|
85
|
+
<th>Beschreibung</th>
|
|
86
|
+
</tr>
|
|
87
|
+
</thead>
|
|
88
|
+
<tbody>
|
|
89
|
+
<tr>
|
|
90
|
+
<td><code>up</code></td>
|
|
91
|
+
<td>Erhöht den Wert um <code>msg.payload</code> oder falls nicht gesetzt (oder <code>null</code>) um die konfigurierte Schrittweite. Der neue Wert ist dabei immer innerhalb der Min- und Max-Werte.</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td><code>down</code></td>
|
|
95
|
+
<td>Verringert den Wert um <code>msg.payload</code> oder falls nicht gesetzt (oder <code>null</code>) um die konfigurierte Schrittweite. Der neue Wert ist dabei immer innerhalb der Min- und Max-Werte.</td>
|
|
96
|
+
</tr>
|
|
97
|
+
<tr>
|
|
98
|
+
<td><code>reset</code></td>
|
|
99
|
+
<td>Setzt den Wert auf <code>msg.payload</code> oder falls nicht gesetzt (oder <code>null</code>) auf den Startwert zurück. Der neue Wert ist dabei immer innerhalb der Min- und Max-Werte.</td>
|
|
100
|
+
</tr>
|
|
101
|
+
</tbody>
|
|
102
|
+
</table>
|
|
103
|
+
</p>
|
|
104
|
+
</script>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module.exports = function (RED)
|
|
2
|
+
{
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
function CounterNode(config)
|
|
6
|
+
{
|
|
7
|
+
const node = this;
|
|
8
|
+
RED.nodes.createNode(node, config);
|
|
9
|
+
|
|
10
|
+
const smart_context = require("../persistence.js")(RED);
|
|
11
|
+
const helper = require("../smart_helper.js");
|
|
12
|
+
|
|
13
|
+
var node_settings = {
|
|
14
|
+
value: null,
|
|
15
|
+
last_message: null,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (config.save_state)
|
|
19
|
+
{
|
|
20
|
+
// load old saved values
|
|
21
|
+
node_settings = Object.assign(node_settings, smart_context.get(node.id));
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
{
|
|
25
|
+
// delete old saved values
|
|
26
|
+
smart_context.del(node.id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// dynamic config
|
|
30
|
+
let start = config.start;
|
|
31
|
+
let step = config.step;
|
|
32
|
+
let min = config.min;
|
|
33
|
+
let max = config.max;
|
|
34
|
+
|
|
35
|
+
// runtime values
|
|
36
|
+
|
|
37
|
+
node.on("input", function (msg)
|
|
38
|
+
{
|
|
39
|
+
handleTopic(msg);
|
|
40
|
+
sendResult();
|
|
41
|
+
setStatus();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
let handleTopic = msg =>
|
|
45
|
+
{
|
|
46
|
+
let real_topic = helper.getTopicName(msg.topic);
|
|
47
|
+
let temp_value;
|
|
48
|
+
switch (real_topic)
|
|
49
|
+
{
|
|
50
|
+
case "up":
|
|
51
|
+
temp_value = parseFloat(msg.payload);
|
|
52
|
+
if (isNaN(temp_value) && !isFinite(temp_value))
|
|
53
|
+
temp_value = step;
|
|
54
|
+
|
|
55
|
+
node_settings.value += temp_value;
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case "down":
|
|
59
|
+
temp_value = parseFloat(msg.payload);
|
|
60
|
+
if (isNaN(temp_value) && !isFinite(temp_value))
|
|
61
|
+
temp_value = step;
|
|
62
|
+
|
|
63
|
+
node_settings.value -= temp_value;
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
case "reset":
|
|
67
|
+
temp_value = parseFloat(msg.payload);
|
|
68
|
+
if (isNaN(temp_value) && !isFinite(temp_value))
|
|
69
|
+
temp_value = start;
|
|
70
|
+
|
|
71
|
+
node_settings.value = temp_value;
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
default:
|
|
75
|
+
node.error("Invalid topic: " + real_topic);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check value is in range
|
|
80
|
+
node_settings.value = Math.min(max, Math.max(min, node_settings.value));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let sendResult = () =>
|
|
84
|
+
{
|
|
85
|
+
// Nothing changed, nothing to do
|
|
86
|
+
if (node_settings.value == node_settings.last_message?.payload)
|
|
87
|
+
return;
|
|
88
|
+
|
|
89
|
+
node_settings.last_message = { payload: node_settings.value };
|
|
90
|
+
smart_context.set(node.id, node_settings);
|
|
91
|
+
|
|
92
|
+
node.send(node_settings.last_message);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
node.on("close", function ()
|
|
96
|
+
{
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// updates the status
|
|
100
|
+
let setStatus = () =>
|
|
101
|
+
{
|
|
102
|
+
if (node_settings.value == null)
|
|
103
|
+
node.status({});
|
|
104
|
+
else
|
|
105
|
+
node.status({ fill: "yellow", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Value = " + node_settings.value });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (config.save_state && config.resend_on_start && node_settings.last_message != null)
|
|
109
|
+
{
|
|
110
|
+
setTimeout(() =>
|
|
111
|
+
{
|
|
112
|
+
node.send(node_settings.last_message);
|
|
113
|
+
}, 10000);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setStatus();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
RED.nodes.registerType("smart_counter", CounterNode);
|
|
120
|
+
}
|