smart-nodes 0.1.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/LICENSE +21 -0
- package/LICENSE.md +21 -0
- package/README.md +127 -0
- package/central/central.html +328 -0
- package/central/central.js +95 -0
- package/compare/compare.html +137 -0
- package/compare/compare.js +151 -0
- package/delay/delay.html +192 -0
- package/delay/delay.js +175 -0
- package/examples/central.json +804 -0
- package/examples/central.png +0 -0
- package/examples/compare.json +916 -0
- package/examples/compare.png +0 -0
- package/examples/delay.json +198 -0
- package/examples/delay.png +0 -0
- package/examples/forwarder.json +152 -0
- package/examples/forwarder.png +0 -0
- package/examples/hysteresis.json +358 -0
- package/examples/hysteresis.png +0 -0
- package/examples/light-control.json +499 -0
- package/examples/light-control.png +0 -0
- package/examples/logic.json +562 -0
- package/examples/logic.png +0 -0
- package/examples/long-press-control.json +113 -0
- package/examples/long-press-control.png +0 -0
- package/examples/multi-press-control.json +136 -0
- package/examples/multi-press-control.png +0 -0
- package/examples/scene-control.json +535 -0
- package/examples/scene-control.png +0 -0
- package/examples/scheduler.json +164 -0
- package/examples/scheduler.png +0 -0
- package/examples/shutter-complex-control.json +489 -0
- package/examples/shutter-complex-control.png +0 -0
- package/examples/shutter-control.json +457 -0
- package/examples/shutter-control.png +0 -0
- package/examples/statistic.json +1112 -0
- package/examples/statistic.png +0 -0
- package/forwarder/forwarder.html +100 -0
- package/forwarder/forwarder.js +95 -0
- package/hysteresis/hysteresis.html +152 -0
- package/hysteresis/hysteresis.js +146 -0
- package/light-control/light-control.html +358 -0
- package/light-control/light-control.js +231 -0
- package/logic/logic.html +168 -0
- package/logic/logic.js +171 -0
- package/long-press-control/long-press-control.html +74 -0
- package/long-press-control/long-press-control.js +75 -0
- package/multi-press-control/multi-press-control.html +135 -0
- package/multi-press-control/multi-press-control.js +68 -0
- package/package.json +59 -0
- package/persistence.js +74 -0
- package/scene-control/scene-control.html +575 -0
- package/scene-control/scene-control.js +265 -0
- package/scheduler/scheduler.html +338 -0
- package/scheduler/scheduler.js +209 -0
- package/shutter-complex-control/shutter-complex-control.html +330 -0
- package/shutter-complex-control/shutter-complex-control.js +399 -0
- package/shutter-control/shutter-control.html +283 -0
- package/shutter-control/shutter-control.js +208 -0
- package/smart_helper.js +156 -0
- package/statistic/statistic.html +107 -0
- package/statistic/statistic.js +196 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("smart_compare", {
|
|
3
|
+
category: "Smart Nodes",
|
|
4
|
+
paletteLabel: "Compare",
|
|
5
|
+
color: "#E2D96E",
|
|
6
|
+
defaults: {
|
|
7
|
+
name: { value: "" },
|
|
8
|
+
comparator: { value: "EQUAL" }, // EQUAL, GREATER, GREATER_EQUAL, SMALLER, SMALLER_EQUAL, UNEQUAL
|
|
9
|
+
value1: { value: "" },
|
|
10
|
+
value1_type: { value: "null" },
|
|
11
|
+
value2: { value: "" },
|
|
12
|
+
value2_type: { value: "null" },
|
|
13
|
+
save_state: { value: true },
|
|
14
|
+
resend_on_start: { value: true }
|
|
15
|
+
},
|
|
16
|
+
inputs: 1,
|
|
17
|
+
outputs: 1,
|
|
18
|
+
icon: "font-awesome/fa-sort",
|
|
19
|
+
label: function ()
|
|
20
|
+
{
|
|
21
|
+
return this.name || this.comparator || "Compare";
|
|
22
|
+
},
|
|
23
|
+
oneditprepare: function ()
|
|
24
|
+
{
|
|
25
|
+
$("#node-input-comparator").typedInput({
|
|
26
|
+
types: [
|
|
27
|
+
{
|
|
28
|
+
default: "EQUAL",
|
|
29
|
+
options: [
|
|
30
|
+
{ value: "SMALLER", label: "< Kleiner" },
|
|
31
|
+
{ value: "SMALLER_EQUAL", label: "<= Kleiner oder gleich" },
|
|
32
|
+
{ value: "EQUAL", label: "== Gleich" },
|
|
33
|
+
{ value: "UNEQUAL", label: "!= Ungleich" },
|
|
34
|
+
{ value: "GREATER_EQUAL", label: ">= Größer oder gleich" },
|
|
35
|
+
{ value: "GREATER", label: "> Größer" }
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
$("#node-input-value1").typedInput({
|
|
42
|
+
type: "num",
|
|
43
|
+
types: ["str", "num", "bool", {
|
|
44
|
+
value: "null",
|
|
45
|
+
label: "Kein Wert",
|
|
46
|
+
icon: "fa fa-times",
|
|
47
|
+
hasValue: false,
|
|
48
|
+
}],
|
|
49
|
+
typeField: "#node-input-value1_type"
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
$("#node-input-value2").typedInput({
|
|
53
|
+
type: "num",
|
|
54
|
+
types: ["str", "num", "bool", {
|
|
55
|
+
value: "null",
|
|
56
|
+
label: "Kein Wert",
|
|
57
|
+
icon: "fa fa-times",
|
|
58
|
+
hasValue: false,
|
|
59
|
+
}],
|
|
60
|
+
typeField: "#node-input-value2_type"
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
$("#node-input-save_state").on("change", ev =>
|
|
64
|
+
{
|
|
65
|
+
if (ev.target.checked)
|
|
66
|
+
$("#resend_on_start_row").show();
|
|
67
|
+
else
|
|
68
|
+
$("#resend_on_start_row").hide();
|
|
69
|
+
});
|
|
70
|
+
$("#node-input-save_state").trigger("change");
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<script type="text/html" data-template-name="smart_compare">
|
|
76
|
+
<div class="form-row">
|
|
77
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
78
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
79
|
+
</div>
|
|
80
|
+
<div class="form-row">
|
|
81
|
+
<label for="node-input-comparator"><i class="fa fa-sort"></i> Vergleich</label>
|
|
82
|
+
<input id="node-input-comparator" />
|
|
83
|
+
</div>
|
|
84
|
+
<div class="form-row">
|
|
85
|
+
<div>Defaultwerte werden hier gesetzt und können durch die einkommenden Nachrichten überschrieben werden.</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="form-row">
|
|
88
|
+
<label for="node-input-value1"><i class="fa fa-hashtag"></i> Wert 1</label>
|
|
89
|
+
<input type="text" id="node-input-value1">
|
|
90
|
+
<input type="hidden" id="node-input-value1_type">
|
|
91
|
+
</div>
|
|
92
|
+
<div class="form-row">
|
|
93
|
+
<label for="node-input-value2"><i class="fa fa-hashtag"></i> Wert 2</label>
|
|
94
|
+
<input type="text" id="node-input-value2"/>
|
|
95
|
+
<input type="hidden" id="node-input-value2_type">
|
|
96
|
+
</div>
|
|
97
|
+
<hr/>
|
|
98
|
+
<h4 style="margin: 0.5rem 0;">Systemstart</h4>
|
|
99
|
+
<div class="form-row">
|
|
100
|
+
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
|
101
|
+
<label for="node-input-save_state" style="width: calc(100% - 30px);">Zustand speichern</label>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="form-row" id="resend_on_start_row">
|
|
104
|
+
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
105
|
+
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);">Letze Nachricht 10 Sekunden nach dem Start senden</label>
|
|
106
|
+
</div>
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<script type="text/html" data-help-name="smart_compare">
|
|
110
|
+
<p>Diese Node führt verschiedene Vergleiche aus. Dabei können nicht nur Zahlen, sondern auch Texte verglichen werden.</p>
|
|
111
|
+
<p>
|
|
112
|
+
<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/>
|
|
113
|
+
Diese Node verwendet nur den Teil <code>nummer</code>. <code>name</code> und <code>#</code> sind dabei optional.
|
|
114
|
+
</p>
|
|
115
|
+
<p>
|
|
116
|
+
Folgende topics werden akzeptiert:
|
|
117
|
+
<table>
|
|
118
|
+
<thead>
|
|
119
|
+
<tr>
|
|
120
|
+
<th>Topic</th>
|
|
121
|
+
<th>Beschreibung</th>
|
|
122
|
+
</tr>
|
|
123
|
+
</thead>
|
|
124
|
+
<tbody>
|
|
125
|
+
<tr>
|
|
126
|
+
<td><code>1</code></td>
|
|
127
|
+
<td>Setzt den linken zu vergleichenden Wert</td>
|
|
128
|
+
</tr>
|
|
129
|
+
<tr>
|
|
130
|
+
<td><code>2</code></td>
|
|
131
|
+
<td>Setzt den rechten zu vergleichenden Wert</td>
|
|
132
|
+
</tr>
|
|
133
|
+
</tbody>
|
|
134
|
+
</table>
|
|
135
|
+
</p>
|
|
136
|
+
<p>Texte werden anhand ihres Ascii-Codes verglichen.<code>"c" < "b"</code> ergibt <code>false</code><code>"C" < "b"</code> ergibt <code>true</code>.</p>
|
|
137
|
+
</script>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
module.exports = function (RED)
|
|
2
|
+
{
|
|
3
|
+
function CompareNode(config)
|
|
4
|
+
{
|
|
5
|
+
const node = this;
|
|
6
|
+
RED.nodes.createNode(node, config);
|
|
7
|
+
|
|
8
|
+
const smartContext = require("../persistence.js")(RED);
|
|
9
|
+
const helper = require("../smart_helper.js");
|
|
10
|
+
|
|
11
|
+
var nodeSettings = {
|
|
12
|
+
values: [
|
|
13
|
+
helper.evaluateNodeProperty(RED, config.value1, config.value1_type),
|
|
14
|
+
helper.evaluateNodeProperty(RED, config.value2, config.value2_type)
|
|
15
|
+
],
|
|
16
|
+
lastResult: null,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (config.save_state)
|
|
20
|
+
{
|
|
21
|
+
// load old saved values
|
|
22
|
+
nodeSettings = Object.assign(nodeSettings, smartContext.get(node.id));
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
{
|
|
26
|
+
// delete old saved values
|
|
27
|
+
smartContext.del(node.id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// dynamic config
|
|
31
|
+
let comparator = config.comparator;
|
|
32
|
+
|
|
33
|
+
// runtime values
|
|
34
|
+
|
|
35
|
+
node.on("input", function (msg)
|
|
36
|
+
{
|
|
37
|
+
// Check if topic has a valid value
|
|
38
|
+
let input = helper.getTopicNumber(msg.topic);
|
|
39
|
+
if (input == null || input < 1 || input > 2)
|
|
40
|
+
{
|
|
41
|
+
node.error("Topic has to be 1 or 2, send: " + msg.topic);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// check if payload has a valid value
|
|
46
|
+
let num = parseFloat(msg.payload);
|
|
47
|
+
if (Number.isNaN(num))
|
|
48
|
+
{
|
|
49
|
+
node.error("Payload has to be numeric");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Save new value
|
|
54
|
+
nodeSettings.values[input - 1] = num;
|
|
55
|
+
|
|
56
|
+
let result = getResult();
|
|
57
|
+
setStatus(result);
|
|
58
|
+
|
|
59
|
+
if (result != null)
|
|
60
|
+
{
|
|
61
|
+
nodeSettings.lastResult = result;
|
|
62
|
+
|
|
63
|
+
if (config.save_state)
|
|
64
|
+
smartContext.set(node.id, nodeSettings);
|
|
65
|
+
|
|
66
|
+
node.send({ payload: result, comparator: comparator });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let getResult = () =>
|
|
71
|
+
{
|
|
72
|
+
// Wait until both values are set
|
|
73
|
+
if (nodeSettings.values[0] == null || nodeSettings.values[1] == null)
|
|
74
|
+
return null;
|
|
75
|
+
|
|
76
|
+
switch (comparator)
|
|
77
|
+
{
|
|
78
|
+
case "SMALLER":
|
|
79
|
+
return nodeSettings.values[0] < nodeSettings.values[1];
|
|
80
|
+
|
|
81
|
+
case "SMALLER_EQUAL":
|
|
82
|
+
return nodeSettings.values[0] <= nodeSettings.values[1];
|
|
83
|
+
|
|
84
|
+
case "EQUAL":
|
|
85
|
+
return nodeSettings.values[0] === nodeSettings.values[1];
|
|
86
|
+
|
|
87
|
+
case "UNEQUAL":
|
|
88
|
+
return nodeSettings.values[0] !== nodeSettings.values[1];
|
|
89
|
+
|
|
90
|
+
case "GREATER_EQUAL":
|
|
91
|
+
return nodeSettings.values[0] >= nodeSettings.values[1];
|
|
92
|
+
|
|
93
|
+
case "GREATER":
|
|
94
|
+
return nodeSettings.values[0] > nodeSettings.values[1];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// This is only used for status message
|
|
101
|
+
let getComparatorSign = () =>
|
|
102
|
+
{
|
|
103
|
+
switch (comparator)
|
|
104
|
+
{
|
|
105
|
+
case "SMALLER":
|
|
106
|
+
return "<";
|
|
107
|
+
|
|
108
|
+
case "SMALLER_EQUAL":
|
|
109
|
+
return "<=";
|
|
110
|
+
|
|
111
|
+
case "EQUAL":
|
|
112
|
+
return "==";
|
|
113
|
+
|
|
114
|
+
case "UNEQUAL":
|
|
115
|
+
return "!=";
|
|
116
|
+
|
|
117
|
+
case "GREATER_EQUAL":
|
|
118
|
+
return ">=";
|
|
119
|
+
|
|
120
|
+
case "GREATER":
|
|
121
|
+
return ">";
|
|
122
|
+
}
|
|
123
|
+
return "???";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// updates the status
|
|
127
|
+
let setStatus = (result) =>
|
|
128
|
+
{
|
|
129
|
+
if (result == null)
|
|
130
|
+
node.status({});
|
|
131
|
+
else
|
|
132
|
+
node.status({ fill: "yellow", shape: "ring", text: nodeSettings.values.join(" " + getComparatorSign() + " ") + " => " + result });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
node.on("close", function ()
|
|
136
|
+
{
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (config.save_state && config.resend_on_start && nodeSettings.lastResult != null)
|
|
140
|
+
{
|
|
141
|
+
setTimeout(() =>
|
|
142
|
+
{
|
|
143
|
+
node.send({ payload: nodeSettings.lastResult, comparator: comparator });
|
|
144
|
+
}, 10000);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setStatus(nodeSettings.lastResult);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
RED.nodes.registerType("smart_compare", CompareNode);
|
|
151
|
+
}
|
package/delay/delay.html
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("smart_delay", {
|
|
3
|
+
category: "Smart Nodes",
|
|
4
|
+
paletteLabel: "delay",
|
|
5
|
+
color: "#6EE2D9",
|
|
6
|
+
defaults: {
|
|
7
|
+
name: { value: "" },
|
|
8
|
+
on_delay: { value: 10 },
|
|
9
|
+
on_delay_unit: { value: "s" },
|
|
10
|
+
off_delay: { value: 10 },
|
|
11
|
+
off_delay_unit: { value: "s" },
|
|
12
|
+
delay_only_on_change: { value: true },
|
|
13
|
+
save_state: { value: true },
|
|
14
|
+
resend_on_start: { value: true }
|
|
15
|
+
},
|
|
16
|
+
inputs: 1,
|
|
17
|
+
outputs: 1,
|
|
18
|
+
icon: "timer.png",
|
|
19
|
+
label: function ()
|
|
20
|
+
{
|
|
21
|
+
return this.name || "Delay";
|
|
22
|
+
},
|
|
23
|
+
oneditprepare: function ()
|
|
24
|
+
{
|
|
25
|
+
$("#node-input-on_delay").spinner({
|
|
26
|
+
min: 0,
|
|
27
|
+
change: function (event, ui)
|
|
28
|
+
{
|
|
29
|
+
var value = parseInt(this.value);
|
|
30
|
+
value = isNaN(value) ? 0 : value;
|
|
31
|
+
value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
|
|
32
|
+
// value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
|
|
33
|
+
if (value !== this.value)
|
|
34
|
+
$(this).spinner("value", value);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
$("#node-input-on_delay_unit").css("max-width", "10rem").typedInput({
|
|
39
|
+
types: [
|
|
40
|
+
{
|
|
41
|
+
default: "s",
|
|
42
|
+
options: [
|
|
43
|
+
{ value: "ms", label: "Millisekunden" },
|
|
44
|
+
{ value: "s", label: "Sekunden" },
|
|
45
|
+
{ value: "min", label: "Minuten" },
|
|
46
|
+
{ value: "h", label: "Stunden" },
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
$("#node-input-off_delay").spinner({
|
|
53
|
+
min: 0,
|
|
54
|
+
change: function (event, ui)
|
|
55
|
+
{
|
|
56
|
+
var value = parseInt(this.value);
|
|
57
|
+
value = isNaN(value) ? 0 : value;
|
|
58
|
+
value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
|
|
59
|
+
// value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
|
|
60
|
+
if (value !== this.value)
|
|
61
|
+
$(this).spinner("value", value);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
$("#node-input-off_delay_unit").css("max-width", "10rem").typedInput({
|
|
66
|
+
types: [
|
|
67
|
+
{
|
|
68
|
+
default: "s",
|
|
69
|
+
options: [
|
|
70
|
+
{ value: "ms", label: "Millisekunden" },
|
|
71
|
+
{ value: "s", label: "Sekunden" },
|
|
72
|
+
{ value: "min", label: "Minuten" },
|
|
73
|
+
{ value: "h", label: "Stunden" },
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
$("#node-input-save_state").on("change", ev =>
|
|
80
|
+
{
|
|
81
|
+
if (ev.target.checked)
|
|
82
|
+
$("#resend_on_start_row").show();
|
|
83
|
+
else
|
|
84
|
+
$("#resend_on_start_row").hide();
|
|
85
|
+
});
|
|
86
|
+
$("#node-input-save_state").trigger("change");
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<script type="text/html" data-template-name="smart_delay">
|
|
92
|
+
<div class="form-row">
|
|
93
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
94
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-row">
|
|
97
|
+
<label for="node-input-on_delay"><i class="fa fa-clock-o"></i> Einschalt- verzögerung</label>
|
|
98
|
+
<input id="node-input-on_delay" value="0" />
|
|
99
|
+
<input id="node-input-on_delay_unit" />
|
|
100
|
+
</div>
|
|
101
|
+
<div class="form-row">
|
|
102
|
+
<label for="node-input-off_delay"><i class="fa fa-clock-o"></i> Ausschalt- verzögerung</label>
|
|
103
|
+
<input id="node-input-off_delay" value="0" />
|
|
104
|
+
<input id="node-input-off_delay_unit" />
|
|
105
|
+
</div>
|
|
106
|
+
<div class="form-row">
|
|
107
|
+
<input id="node-input-delay_only_on_change" type="checkbox" style="display: inline-block; width: auto; vertical-align: top;"/>
|
|
108
|
+
<span>Nur verzögern, wenn sich <code>msg.payload</code> verändert hat.</span>
|
|
109
|
+
</div>
|
|
110
|
+
<hr/>
|
|
111
|
+
<h4 style="margin: 0.5rem 0;">Systemstart</h4>
|
|
112
|
+
<div class="form-row">
|
|
113
|
+
<input type="checkbox" id="node-input-save_state" style="width: 20px;" />
|
|
114
|
+
<label for="node-input-save_state" style="width: calc(100% - 30px);">Zustand speichern</label>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="form-row" id="resend_on_start_row">
|
|
117
|
+
<input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
|
|
118
|
+
<label for="node-input-resend_on_start" style="width: calc(100% - 30px);">Letze Nachricht 10 Sekunden nach dem Start senden</label>
|
|
119
|
+
</div>
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<script type="text/html" data-help-name="smart_delay">
|
|
123
|
+
<p>Diese Node kann dazu verwendet werden ein Ein- oder Ausschaltsignal zu verzögern.</p>
|
|
124
|
+
<p>Die Zeit 0 bedeutet keine Verögerung.</p>
|
|
125
|
+
<p>
|
|
126
|
+
<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/>
|
|
127
|
+
Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
|
|
128
|
+
</p>
|
|
129
|
+
<p>
|
|
130
|
+
Folgende topics werden akzeptiert:
|
|
131
|
+
<table>
|
|
132
|
+
<thead>
|
|
133
|
+
<tr>
|
|
134
|
+
<th>Topic</th>
|
|
135
|
+
<th>Beschreibung</th>
|
|
136
|
+
</tr>
|
|
137
|
+
</thead>
|
|
138
|
+
<tbody>
|
|
139
|
+
<tr>
|
|
140
|
+
<td><code>set_delay_on</code></td>
|
|
141
|
+
<td>Setzt die Einschaltverzögerung auf die in <code>msg.payload</code> angegebene Zeit.</td>
|
|
142
|
+
</tr>
|
|
143
|
+
<tr>
|
|
144
|
+
<td><code>set_delay_off</code></td>
|
|
145
|
+
<td>Setzt die Ausschaltverzögerung auf die in <code>msg.payload</code> angegebene Zeit.</td>
|
|
146
|
+
</tr>
|
|
147
|
+
<tr>
|
|
148
|
+
<td><code>set_delays</code></td>
|
|
149
|
+
<td>Setzt die Ein- und Ausschaltverzögerung auf die in <code>msg.payload</code> angegebene Zeit.</td>
|
|
150
|
+
</tr>
|
|
151
|
+
</tbody>
|
|
152
|
+
</table>
|
|
153
|
+
</p>
|
|
154
|
+
<p>
|
|
155
|
+
Beispiel: <code>msg = { "topic": "set_delay_on", "payload": 5000 }</code> oder <code>msg = { "topic": "set_delay_on", "payload": "5s" }</code><br/>
|
|
156
|
+
Diese Nachricht setzt die Einschaltverzögerung auf 5000 Millisekunden / 5 Sekunden.
|
|
157
|
+
Ist die Zeit auf 0 eingestellt, wird die Nachricht sofort weitergeleitet.<br/>
|
|
158
|
+
Als Einheit für die Zeit können folgende Werte verwendet werden:
|
|
159
|
+
<table>
|
|
160
|
+
<thead>
|
|
161
|
+
<tr>
|
|
162
|
+
<th>Einheit</th>
|
|
163
|
+
<th>Beschreibung</th>
|
|
164
|
+
</tr>
|
|
165
|
+
</thead>
|
|
166
|
+
<tbody>
|
|
167
|
+
<tr>
|
|
168
|
+
<td><code>ms</code> (default)</td>
|
|
169
|
+
<td>Millisekunden</td>
|
|
170
|
+
</tr>
|
|
171
|
+
<tr>
|
|
172
|
+
<td><code>s</code> oder <code>sec</code></td>
|
|
173
|
+
<td>Sekunden</td>
|
|
174
|
+
</tr>
|
|
175
|
+
<tr>
|
|
176
|
+
<td><code>m</code> oder <code>min</code></td>
|
|
177
|
+
<td>Mintun.</td>
|
|
178
|
+
</tr>
|
|
179
|
+
<tr>
|
|
180
|
+
<td><code>h</code></td>
|
|
181
|
+
<td>Stunden</td>
|
|
182
|
+
</tr>
|
|
183
|
+
</tbody>
|
|
184
|
+
</table>
|
|
185
|
+
</p>
|
|
186
|
+
<p>
|
|
187
|
+
Wenn die Checkbox <b>Nur verzögern, wenn sich <code>msg.payload</code> verändert hat.</b> aktiviert ist
|
|
188
|
+
und während dem Warten um ein Einschaltsignal weiterzuleiten ein Ausschaltsignal empfangen wird,
|
|
189
|
+
so wird das Senden des Einschaltsignals abgebrochen. Dies gilt auch für den umgekehrten Fall.<br/>
|
|
190
|
+
Ist die Checkbox nicht aktiviert, wird jede Nachricht verzögert, sofern nicht bereits eine Verzögerung für den gleichen <code>payload</code> aktiv ist.
|
|
191
|
+
</p>
|
|
192
|
+
</script>
|
package/delay/delay.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
module.exports = function (RED)
|
|
2
|
+
{
|
|
3
|
+
function DelayNode(config)
|
|
4
|
+
{
|
|
5
|
+
const node = this;
|
|
6
|
+
RED.nodes.createNode(node, config);
|
|
7
|
+
|
|
8
|
+
const smartContext = require("../persistence.js")(RED);
|
|
9
|
+
const helper = require("../smart_helper.js");
|
|
10
|
+
|
|
11
|
+
var nodeSettings = {
|
|
12
|
+
on_delay_ms: helper.getTimeInMs(config.on_delay, config.on_delay_unit),
|
|
13
|
+
off_delay_ms: helper.getTimeInMs(config.off_delay, config.off_delay_unit),
|
|
14
|
+
lastMessage: null,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (config.save_state)
|
|
18
|
+
{
|
|
19
|
+
// load old saved values
|
|
20
|
+
nodeSettings = Object.assign(nodeSettings, smartContext.get(node.id));
|
|
21
|
+
|
|
22
|
+
switch (nodeSettings.lastMessage?.payload)
|
|
23
|
+
{
|
|
24
|
+
case true:
|
|
25
|
+
node.status({ fill: "yellow", shape: "ring", text: "Load last state: On" });
|
|
26
|
+
break;
|
|
27
|
+
|
|
28
|
+
case false:
|
|
29
|
+
node.status({ fill: "yellow", shape: "ring", text: "Load last state: Off" });
|
|
30
|
+
break;
|
|
31
|
+
|
|
32
|
+
default:
|
|
33
|
+
node.status({ fill: "yellow", shape: "ring", text: "No last state available" });
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
{
|
|
39
|
+
// delete old saved values
|
|
40
|
+
node.status({});
|
|
41
|
+
smartContext.del(node.id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// dynamic config
|
|
45
|
+
let delay_only_on_change = config.delay_only_on_change;
|
|
46
|
+
|
|
47
|
+
// runtime values
|
|
48
|
+
let timeout = null;
|
|
49
|
+
let next_payload = null;
|
|
50
|
+
|
|
51
|
+
node.on("input", function (msg)
|
|
52
|
+
{
|
|
53
|
+
switch (msg.topic)
|
|
54
|
+
{
|
|
55
|
+
case "set_delay_on":
|
|
56
|
+
nodeSettings.on_delay_ms = helper.getTimeInMsFromString(msg.payload);
|
|
57
|
+
node.status({ fill: "yellow", shape: "ring", text: "New on delay: " + helper.formatMsToStatus(nodeSettings.on_delay_ms) });
|
|
58
|
+
|
|
59
|
+
if (config.save_state)
|
|
60
|
+
smartContext.set(node.id, nodeSettings);
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case "set_delay_off":
|
|
64
|
+
nodeSettings.off_delay_ms = helper.getTimeInMsFromString(msg.payload);
|
|
65
|
+
node.status({ fill: "yellow", shape: "ring", text: "New off delay: " + helper.formatMsToStatus(nodeSettings.on_delay_ms) });
|
|
66
|
+
|
|
67
|
+
if (config.save_state)
|
|
68
|
+
smartContext.set(node.id, nodeSettings);
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case "set_delays":
|
|
72
|
+
nodeSettings.on_delay_ms = helper.getTimeInMsFromString(msg.payload);
|
|
73
|
+
nodeSettings.off_delay_ms = nodeSettings.on_delay_ms;
|
|
74
|
+
node.status({ fill: "yellow", shape: "ring", text: "New delays: " + helper.formatMsToStatus(nodeSettings.on_delay_ms) });
|
|
75
|
+
|
|
76
|
+
if (config.save_state)
|
|
77
|
+
smartContext.set(node.id, nodeSettings);
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
default:
|
|
81
|
+
send(msg);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
node.on("close", function ()
|
|
87
|
+
{
|
|
88
|
+
if (timeout != null)
|
|
89
|
+
{
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
timeout = null;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let send = (msg) =>
|
|
96
|
+
{
|
|
97
|
+
let delayMs = 0;
|
|
98
|
+
|
|
99
|
+
if (msg.payload)
|
|
100
|
+
delayMs = nodeSettings.on_delay_ms;
|
|
101
|
+
else
|
|
102
|
+
delayMs = nodeSettings.off_delay_ms;
|
|
103
|
+
|
|
104
|
+
if (delay_only_on_change)
|
|
105
|
+
{
|
|
106
|
+
if (timeout)
|
|
107
|
+
{
|
|
108
|
+
// current delay runs already for the same payload, so don't start a new one.
|
|
109
|
+
if (next_payload == msg.payload)
|
|
110
|
+
return;
|
|
111
|
+
|
|
112
|
+
// payload changed back to last value => stop current delay
|
|
113
|
+
if (nodeSettings.lastMessage?.payload == msg.payload)
|
|
114
|
+
{
|
|
115
|
+
node.status({});
|
|
116
|
+
clearTimeout(timeout);
|
|
117
|
+
timeout = null;
|
|
118
|
+
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// payload hasn't changed
|
|
123
|
+
else if (nodeSettings.lastMessage?.payload == msg.payload)
|
|
124
|
+
{
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// stop timeout if any
|
|
130
|
+
if (timeout != null)
|
|
131
|
+
{
|
|
132
|
+
node.status({});
|
|
133
|
+
clearTimeout(timeout);
|
|
134
|
+
timeout = null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// No delay if 0 or smaller
|
|
138
|
+
if (delayMs <= 0)
|
|
139
|
+
{
|
|
140
|
+
node.status({});
|
|
141
|
+
nodeSettings.lastMessage = msg
|
|
142
|
+
|
|
143
|
+
if (config.save_state)
|
|
144
|
+
smartContext.set(node.id, nodeSettings);
|
|
145
|
+
|
|
146
|
+
node.send(msg);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// start new timeout
|
|
151
|
+
node.status({ fill: "yellow", shape: "ring", text: "Forward msg.topic = '" + (msg.topic ?? "null") + "' msg.payload = '" + (msg.payload ?? "null") + "' in " + helper.formatMsToStatus(delayMs, "at") });
|
|
152
|
+
timeout = setTimeout(() =>
|
|
153
|
+
{
|
|
154
|
+
node.status({});
|
|
155
|
+
nodeSettings.lastMessage = msg
|
|
156
|
+
|
|
157
|
+
if (config.save_state)
|
|
158
|
+
smartContext.set(node.id, nodeSettings);
|
|
159
|
+
|
|
160
|
+
node.send(msg);
|
|
161
|
+
}, delayMs);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (config.save_state && config.resend_on_start && nodeSettings.lastMessage != null)
|
|
165
|
+
{
|
|
166
|
+
setTimeout(() =>
|
|
167
|
+
{
|
|
168
|
+
node.status({});
|
|
169
|
+
node.send(nodeSettings.lastMessage);
|
|
170
|
+
}, 10000);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
RED.nodes.registerType("smart_delay", DelayNode);
|
|
175
|
+
}
|