smart-nodes 0.3.36 → 0.4.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/CHANGELOG.md +62 -0
- package/README.md +35 -17
- package/central/central.html +27 -24
- package/central/central.js +84 -26
- package/central/locales/de-DE/central.html +10 -0
- package/central/locales/de-DE/central.json +14 -0
- package/central/locales/en-US/central.html +10 -0
- package/central/locales/en-US/central.json +14 -0
- package/compare/compare.html +64 -87
- package/compare/compare.js +69 -29
- package/compare/locales/de-DE/compare.html +36 -0
- package/compare/locales/de-DE/compare.json +35 -0
- package/compare/locales/en-US/compare.html +36 -0
- package/compare/locales/en-US/compare.json +35 -0
- package/counter/counter.html +70 -72
- package/counter/counter.js +43 -20
- package/counter/locales/de-DE/counter.html +48 -0
- package/counter/locales/de-DE/counter.json +21 -0
- package/counter/locales/en-US/counter.html +48 -0
- package/counter/locales/en-US/counter.json +21 -0
- package/delay/delay.html +30 -102
- package/delay/delay.js +63 -20
- package/delay/locales/de-DE/delay.html +71 -0
- package/delay/locales/de-DE/delay.json +19 -0
- package/delay/locales/en-US/delay.html +76 -0
- package/delay/locales/en-US/delay.json +19 -0
- package/forwarder/forwarder.html +11 -42
- package/forwarder/forwarder.js +59 -18
- package/forwarder/locales/de-DE/forwarder.html +32 -0
- package/forwarder/locales/de-DE/forwarder.json +15 -0
- package/forwarder/locales/en-US/forwarder.html +32 -0
- package/forwarder/locales/en-US/forwarder.json +15 -0
- package/heating-curve/heating-curve.html +10 -51
- package/heating-curve/heating-curve.js +38 -13
- package/heating-curve/locales/de-DE/heating-curve.html +38 -0
- package/heating-curve/locales/de-DE/heating-curve.json +12 -0
- package/heating-curve/locales/en-US/heating-curve.html +38 -0
- package/heating-curve/locales/en-US/heating-curve.json +12 -0
- package/hysteresis/hysteresis.html +49 -69
- package/hysteresis/hysteresis.js +94 -68
- package/hysteresis/locales/de-DE/hysteresis.html +36 -0
- package/hysteresis/locales/de-DE/hysteresis.json +27 -0
- package/hysteresis/locales/en-US/hysteresis.html +36 -0
- package/hysteresis/locales/en-US/hysteresis.json +27 -0
- package/light/light.html +250 -0
- package/{light-control/light-control.js → light/light.js} +151 -32
- package/light/locales/de-DE/light.html +149 -0
- package/light/locales/de-DE/light.json +24 -0
- package/light/locales/en-US/light.html +148 -0
- package/light/locales/en-US/light.json +24 -0
- package/logic/locales/de-DE/logic.html +12 -0
- package/logic/locales/de-DE/logic.json +26 -0
- package/logic/locales/en-US/logic.html +12 -0
- package/logic/locales/en-US/logic.json +26 -0
- package/logic/logic.html +48 -64
- package/logic/logic.js +63 -29
- package/long-press/locales/de-DE/long-press.html +5 -0
- package/long-press/locales/de-DE/long-press.json +13 -0
- package/long-press/locales/en-US/long-press.html +5 -0
- package/long-press/locales/en-US/long-press.json +13 -0
- package/{long-press-control/long-press-control.html → long-press/long-press.html} +10 -14
- package/long-press/long-press.js +163 -0
- package/mixing-valve/locales/de-DE/mixing-valve.html +65 -0
- package/mixing-valve/locales/de-DE/mixing-valve.json +19 -0
- package/mixing-valve/locales/en-US/mixing-valve.html +66 -0
- package/mixing-valve/locales/en-US/mixing-valve.json +19 -0
- package/mixing-valve/mixing-valve.html +27 -93
- package/mixing-valve/mixing-valve.js +87 -61
- package/multi-press/locales/de-DE/multi-press.html +5 -0
- package/multi-press/locales/de-DE/multi-press.json +12 -0
- package/multi-press/locales/en-US/multi-press.html +5 -0
- package/multi-press/locales/en-US/multi-press.json +12 -0
- package/{multi-press-control/multi-press-control.html → multi-press/multi-press.html} +9 -13
- package/{multi-press-control/multi-press-control.js → multi-press/multi-press.js} +53 -5
- package/package.json +7 -7
- package/persistence.js +1 -0
- package/scene/locales/de-DE/scene.html +105 -0
- package/scene/locales/de-DE/scene.json +21 -0
- package/scene/locales/en-US/scene.html +107 -0
- package/scene/locales/en-US/scene.json +20 -0
- package/{scene-control/scene-control.html → scene/scene.html} +30 -132
- package/{scene-control/scene-control.js → scene/scene.js} +76 -26
- package/scheduler/locales/de-DE/scheduler.html +30 -0
- package/scheduler/locales/de-DE/scheduler.json +21 -0
- package/scheduler/locales/en-US/scheduler.html +30 -0
- package/scheduler/locales/en-US/scheduler.json +21 -0
- package/scheduler/scheduler.html +34 -64
- package/scheduler/scheduler.js +85 -53
- package/shutter/locales/de-DE/shutter.html +127 -0
- package/shutter/locales/de-DE/shutter.json +11 -0
- package/shutter/locales/en-US/shutter.html +133 -0
- package/shutter/locales/en-US/shutter.json +11 -0
- package/{shutter-control/shutter-control.html → shutter/shutter.html} +7 -133
- package/{shutter-control/shutter-control.js → shutter/shutter.js} +116 -56
- package/shutter-complex/locales/de-DE/shutter-complex.html +120 -0
- package/shutter-complex/locales/de-DE/shutter-complex.json +20 -0
- package/shutter-complex/locales/en-US/shutter-complex.html +120 -0
- package/shutter-complex/locales/en-US/shutter-complex.json +20 -0
- package/{shutter-complex-control/shutter-complex-control.html → shutter-complex/shutter-complex.html} +36 -140
- package/shutter-complex/shutter-complex.js +578 -0
- package/smart_helper.js +52 -9
- package/statistic/locales/de-DE/statistic.html +10 -0
- package/statistic/locales/de-DE/statistic.json +29 -0
- package/statistic/locales/en-US/statistic.html +10 -0
- package/statistic/locales/en-US/statistic.json +29 -0
- package/statistic/statistic.html +38 -43
- package/statistic/statistic.js +57 -28
- package/text-exec/locales/de-DE/text-exec.html +18 -0
- package/text-exec/locales/de-DE/text-exec.json +7 -0
- package/text-exec/locales/en-US/text-exec.html +18 -0
- package/text-exec/locales/en-US/text-exec.json +7 -0
- package/text-exec/text-exec.html +9 -25
- package/text-exec/text-exec.js +43 -2
- package/light-control/light-control.html +0 -363
- package/long-press-control/long-press-control.js +0 -76
- package/shutter-complex-control/shutter-complex-control.js +0 -442
package/hysteresis/hysteresis.js
CHANGED
|
@@ -7,10 +7,22 @@ module.exports = function (RED)
|
|
|
7
7
|
const node = this;
|
|
8
8
|
RED.nodes.createNode(node, config);
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
// ###################
|
|
12
|
+
// # Class constants #
|
|
13
|
+
// ###################
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// #######################
|
|
17
|
+
// # Global help objects #
|
|
18
|
+
// #######################
|
|
10
19
|
const smart_context = require("../persistence.js")(RED);
|
|
11
20
|
const helper = require("../smart_helper.js");
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
|
|
23
|
+
// #####################
|
|
24
|
+
// # persistent values #
|
|
25
|
+
// #####################
|
|
14
26
|
var node_settings = {
|
|
15
27
|
last_value: null,
|
|
16
28
|
last_result: null,
|
|
@@ -46,22 +58,52 @@ module.exports = function (RED)
|
|
|
46
58
|
smart_context.del(node.id);
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
|
|
62
|
+
// ##################
|
|
63
|
+
// # Dynamic config #
|
|
64
|
+
// ##################
|
|
50
65
|
let out_higher = helper.evaluateNodeProperty(RED, config.out_higher, config.out_higher_type);
|
|
51
66
|
let out_lower = helper.evaluateNodeProperty(RED, config.out_lower, config.out_lower_type);
|
|
52
67
|
let send_only_change = helper.evaluateNodeProperty(RED, config.send_only_change, "bool");
|
|
53
68
|
let outputs = helper.evaluateNodeProperty(RED, config.outputs, "num");
|
|
54
69
|
|
|
55
|
-
// runtime values
|
|
56
70
|
|
|
71
|
+
// ##################
|
|
72
|
+
// # Runtime values #
|
|
73
|
+
// ##################
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// ###############
|
|
77
|
+
// # Node events #
|
|
78
|
+
// ###############
|
|
57
79
|
node.on("input", function (msg)
|
|
58
80
|
{
|
|
59
|
-
|
|
81
|
+
handleTopic(msg);
|
|
82
|
+
|
|
83
|
+
setStatus();
|
|
84
|
+
|
|
85
|
+
if (config.save_state)
|
|
86
|
+
smart_context.set(node.id, node_settings);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
node.on("close", function ()
|
|
90
|
+
{
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// #####################
|
|
95
|
+
// # Private functions #
|
|
96
|
+
// #####################
|
|
97
|
+
|
|
98
|
+
// This is the main function which handles all topics that was received.
|
|
99
|
+
let handleTopic = msg =>
|
|
100
|
+
{
|
|
60
101
|
let real_topic = helper.getTopicName(msg.topic);
|
|
102
|
+
let value = parseFloat(msg.payload);
|
|
61
103
|
|
|
62
104
|
if (isNaN(value))
|
|
63
105
|
{
|
|
64
|
-
|
|
106
|
+
console.warn("Invalid payload: " + msg.payload);
|
|
65
107
|
return;
|
|
66
108
|
}
|
|
67
109
|
|
|
@@ -69,67 +111,51 @@ module.exports = function (RED)
|
|
|
69
111
|
{
|
|
70
112
|
case "setpoint":
|
|
71
113
|
node_settings.setpoint = value;
|
|
72
|
-
node.status({ fill: node_settings.last_result ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": New setpoint: " + value + "" });
|
|
73
|
-
|
|
74
|
-
if (config.save_state)
|
|
75
|
-
smart_context.set(node.id, node_settings);
|
|
76
114
|
break;
|
|
77
115
|
|
|
78
116
|
case "hysteresis":
|
|
79
117
|
node_settings.hysteresis = value;
|
|
80
|
-
node.status({ fill: node_settings.last_result ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": New hysteresis: " + value + "" });
|
|
81
|
-
|
|
82
|
-
if (config.save_state)
|
|
83
|
-
smart_context.set(node.id, node_settings);
|
|
84
118
|
break;
|
|
85
119
|
|
|
86
120
|
default:
|
|
87
|
-
|
|
88
|
-
let out_msg = null;
|
|
89
|
-
|
|
90
|
-
// No change, nothing to send
|
|
91
|
-
if (result !== null)
|
|
92
|
-
{
|
|
93
|
-
// Get custom output message
|
|
94
|
-
if (result)
|
|
95
|
-
out_msg = createMessage(out_higher, config.out_higher_type, msg, value);
|
|
96
|
-
else
|
|
97
|
-
out_msg = createMessage(out_lower, config.out_lower_type, msg, value);
|
|
98
|
-
|
|
99
|
-
// Overwrite automatic values, if not already defined
|
|
100
|
-
if (typeof out_msg.payload === "undefined")
|
|
101
|
-
out_msg.payload = result ?? node_settings.last_result;
|
|
102
|
-
|
|
103
|
-
// Separate outputs if needed
|
|
104
|
-
if (outputs == 2)
|
|
105
|
-
{
|
|
106
|
-
if (result)
|
|
107
|
-
out_msg = [out_msg, null];
|
|
108
|
-
else
|
|
109
|
-
out_msg = [null, out_msg];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Send only if needed
|
|
113
|
-
if (send_only_change == false || node_settings.last_result != result)
|
|
114
|
-
node.send(out_msg);
|
|
115
|
-
|
|
116
|
-
node_settings.last_result = result;
|
|
117
|
-
node_settings.last_message = out_msg;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
node_settings.last_value = value;
|
|
121
|
-
|
|
122
|
-
setStatus(result === null);
|
|
123
|
-
|
|
124
|
-
if (config.save_state)
|
|
125
|
-
smart_context.set(node.id, node_settings);
|
|
121
|
+
node_settings.last_value = value
|
|
126
122
|
break;
|
|
127
123
|
}
|
|
128
|
-
});
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
// Always calculate new result
|
|
126
|
+
let result = getResult(node_settings.last_value);
|
|
127
|
+
let out_msg = null;
|
|
128
|
+
|
|
129
|
+
// No change, nothing to send
|
|
130
|
+
if (result === null)
|
|
131
|
+
return;
|
|
132
|
+
|
|
133
|
+
// Get custom output message
|
|
134
|
+
if (result)
|
|
135
|
+
out_msg = createMessage(out_higher, config.out_higher_type, msg, node_settings.last_value);
|
|
136
|
+
else
|
|
137
|
+
out_msg = createMessage(out_lower, config.out_lower_type, msg, node_settings.last_value);
|
|
138
|
+
|
|
139
|
+
// Overwrite automatic values, if not already defined
|
|
140
|
+
if (typeof out_msg.payload === "undefined")
|
|
141
|
+
out_msg.payload = result ?? node_settings.last_result;
|
|
142
|
+
|
|
143
|
+
// Separate outputs if needed
|
|
144
|
+
if (outputs == 2)
|
|
145
|
+
{
|
|
146
|
+
if (result)
|
|
147
|
+
out_msg = [out_msg, null];
|
|
148
|
+
else
|
|
149
|
+
out_msg = [null, out_msg];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Send only if needed
|
|
153
|
+
if (send_only_change == false || node_settings.last_result != result)
|
|
154
|
+
node.send(out_msg);
|
|
155
|
+
|
|
156
|
+
node_settings.last_result = result;
|
|
157
|
+
node_settings.last_message = out_msg;
|
|
158
|
+
}
|
|
133
159
|
|
|
134
160
|
let getResult = value =>
|
|
135
161
|
{
|
|
@@ -145,19 +171,17 @@ module.exports = function (RED)
|
|
|
145
171
|
return node_settings.last_result;
|
|
146
172
|
}
|
|
147
173
|
|
|
148
|
-
let setStatus =
|
|
174
|
+
let setStatus = () =>
|
|
149
175
|
{
|
|
150
|
-
if (
|
|
151
|
-
node.status({ fill:
|
|
152
|
-
else
|
|
153
|
-
node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ":
|
|
154
|
-
else if (node_settings.last_result === false)
|
|
155
|
-
node.status({ fill: "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Turned lower by value " + node_settings.last_value + "" });
|
|
176
|
+
if (node_settings.last_result == null)
|
|
177
|
+
node.status({ fill: "yellow", shape: "ring", text: helper.getCurrentTimeForStatus() + ": ❓ S: " + node_settings.setpoint + " - H: " + node_settings.hysteresis + " - V: null" });
|
|
178
|
+
else
|
|
179
|
+
node.status({ fill: node_settings.last_result ? "green" : "red", shape: "dot", text: helper.getCurrentTimeForStatus() + ": " + (node_settings.last_result ? "⬆️" : "⬇️") + " S: " + node_settings.setpoint + " - H: " + node_settings.hysteresis + " - V: " + node_settings.last_value.toFixed(2) });
|
|
156
180
|
}
|
|
157
181
|
|
|
158
182
|
let createMessage = (out_msg, out_type, msg, value) =>
|
|
159
183
|
{
|
|
160
|
-
return
|
|
184
|
+
return helper.cloneObject(out_type == "ORIGINAL" ? msg : out_msg, {
|
|
161
185
|
smart_info: {
|
|
162
186
|
last_result: node_settings.last_result,
|
|
163
187
|
hysteresis: node_settings.hysteresis,
|
|
@@ -165,7 +189,7 @@ module.exports = function (RED)
|
|
|
165
189
|
last_value: node_settings.last_value,
|
|
166
190
|
value: value
|
|
167
191
|
}
|
|
168
|
-
})
|
|
192
|
+
});
|
|
169
193
|
};
|
|
170
194
|
|
|
171
195
|
if (config.save_state && config.resend_on_start && node_settings.last_result !== null && node_settings.last_message !== null)
|
|
@@ -175,16 +199,18 @@ module.exports = function (RED)
|
|
|
175
199
|
if (outputs == 2)
|
|
176
200
|
{
|
|
177
201
|
if (node_settings.last_result)
|
|
178
|
-
node.send([node_settings.last_message, null]);
|
|
202
|
+
node.send([helper.cloneObject(node_settings.last_message), null]);
|
|
179
203
|
else
|
|
180
|
-
node.send([null, node_settings.last_message]);
|
|
204
|
+
node.send([null, helper.cloneObject(node_settings.last_message)]);
|
|
181
205
|
}
|
|
182
206
|
else
|
|
183
207
|
{
|
|
184
|
-
node.send(node_settings.last_message);
|
|
208
|
+
node.send(helper.cloneObject(node_settings.last_message));
|
|
185
209
|
}
|
|
186
210
|
}, 10000);
|
|
187
211
|
}
|
|
212
|
+
|
|
213
|
+
setStatus();
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
RED.nodes.registerType("smart_hysteresis", HysteresisNode);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_hysteresis">
|
|
2
|
+
<p>
|
|
3
|
+
Diese Node sendet die einkommende Nachricht an den Ausgang <code>Über</code>, wenn der einkommende Wert größer gleich dem Sollwert plus Hysterese ist und
|
|
4
|
+
die einkommende Nachricht an den Ausgang <code>Unter</code>, wenn der einkommende Wert kleiner gleich dem Sollwert minus Hysterese ist.<br/>
|
|
5
|
+
Die Nachricht wird dabei nur einmalig bei über- bzw. unterschreiten des Grenzwertes weitergeleitet.<br/>
|
|
6
|
+
Die Node behält den aktuellen Zustand auch nach dem Deploy bei.
|
|
7
|
+
</p>
|
|
8
|
+
<p>
|
|
9
|
+
<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/>
|
|
10
|
+
Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
|
|
11
|
+
</p>
|
|
12
|
+
<p>
|
|
13
|
+
Folgende topics werden akzeptiert:
|
|
14
|
+
<table>
|
|
15
|
+
<thead>
|
|
16
|
+
<tr>
|
|
17
|
+
<th>Topic</th>
|
|
18
|
+
<th>Beschreibung</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
<tr>
|
|
23
|
+
<td><code>setpoint</code></td>
|
|
24
|
+
<td>Überschreibt den Sollwert mit <code>msg.payload</code>.</td>
|
|
25
|
+
</tr>
|
|
26
|
+
<tr>
|
|
27
|
+
<td><code>hysteresis</code></td>
|
|
28
|
+
<td>Überschreibt die Hysterese mit <code>msg.payload</code>.</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
</p>
|
|
33
|
+
<p>
|
|
34
|
+
Die einkommende Nachricht wird mit den angegebenen Ausgangsnachrichten ergänzt, sofern nicht Originalnachricht ausgewählt wurde.
|
|
35
|
+
</p>
|
|
36
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hysteresis": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"setpoint": "Sollwert",
|
|
6
|
+
"hysteresis": "Hysterese",
|
|
7
|
+
"output_messages": "Ausgangsnachrichten",
|
|
8
|
+
"over": "Über",
|
|
9
|
+
"under": "Unter",
|
|
10
|
+
"send": "Senden",
|
|
11
|
+
"outputs": "Ausgänge",
|
|
12
|
+
|
|
13
|
+
"note": "Hinweis:",
|
|
14
|
+
"note_text": "<code>msg.payload</code> wird automatisch auf das Hystere-Ergebnis gesetzt,<br/>sofern es nicht bereits hier in der Konfiguration gesetzt wurde.<br/><code>true</code> wenn höher, <code>false</code> wenn niedriger",
|
|
15
|
+
"system_start": "Systemstart",
|
|
16
|
+
"save_state": "Zustand speichern",
|
|
17
|
+
"send_after_start": "Letze Nachricht 10 Sekunden nach dem Start senden",
|
|
18
|
+
|
|
19
|
+
"send_nothing": "Nichts senden",
|
|
20
|
+
"original_message": "Originalnachricht",
|
|
21
|
+
"send_only_change": "Nur bei Änderung",
|
|
22
|
+
"always": "Immer",
|
|
23
|
+
"common_output": "Gemeinsamer Ausgang",
|
|
24
|
+
"separate_output": "Separate Ausgänge"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script type="text/html" data-help-name="smart_hysteresis">
|
|
2
|
+
<p>
|
|
3
|
+
This node sends the incoming message to the output <code>Over</code> if the incoming value is greater than or equal to the setpoint plus hysteresis and
|
|
4
|
+
the incoming message to the output <code>Under</code> if the incoming value is less than or equal to the setpoint minus hysteresis.<br />
|
|
5
|
+
The message is only forwarded once when the limit is exceeded or not reached.<br />
|
|
6
|
+
The node retains the current state even after deployment.
|
|
7
|
+
</p>
|
|
8
|
+
<p>
|
|
9
|
+
<b>Note:</b> Smart nodes use topics in the format <code>name#number</code>, so that different smart nodes can be controlled with the same topic.<br />
|
|
10
|
+
This node only uses the part <code>name</code>. <code>#</code> and <code>number</code> are optional.
|
|
11
|
+
</p>
|
|
12
|
+
<p>
|
|
13
|
+
The following topics are accepted:
|
|
14
|
+
<table>
|
|
15
|
+
<thead>
|
|
16
|
+
<tr>
|
|
17
|
+
<th>Topic</th>
|
|
18
|
+
<th>Description</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
<tr>
|
|
23
|
+
<td><code>setpoint</code></td>
|
|
24
|
+
<td>Overwrites the setpoint with <code>msg.payload</code>.</td>
|
|
25
|
+
</tr>
|
|
26
|
+
<tr>
|
|
27
|
+
<td><code>hysteresis</code></td>
|
|
28
|
+
<td>Overwrites the hysteresis with <code>msg.payload</code>.</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
</p>
|
|
33
|
+
<p>
|
|
34
|
+
The incoming message is supplemented with the specified outgoing messages, unless the original message was selected.
|
|
35
|
+
</p>
|
|
36
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hysteresis": {
|
|
3
|
+
"ui": {
|
|
4
|
+
"name": "Name",
|
|
5
|
+
"setpoint": "Setpoint",
|
|
6
|
+
"hysteresis": "Hysteresis",
|
|
7
|
+
"output_messages": "Output messages",
|
|
8
|
+
"over": "Over",
|
|
9
|
+
"under": "Under",
|
|
10
|
+
"send": "Send",
|
|
11
|
+
"outputs": "Outputs",
|
|
12
|
+
|
|
13
|
+
"note": "Note:",
|
|
14
|
+
"note_text": "<code>msg.payload</code> is automatically set to the hysteresis result,<br/>if it has not already been set here in the configuration.<br/><code>true</code> if higher, <code>false</code> if lower",
|
|
15
|
+
"system_start": "System start",
|
|
16
|
+
"save_state": "Save state",
|
|
17
|
+
"send_after_start": "Send last message 10 seconds after start",
|
|
18
|
+
|
|
19
|
+
"send_nothing": "Send nothing",
|
|
20
|
+
"original_message": "Original message",
|
|
21
|
+
"send_only_change": "Only when changed",
|
|
22
|
+
"always": "Always",
|
|
23
|
+
"common_output": "Common output",
|
|
24
|
+
"separate_output": "Separate outputs"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/light/light.html
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(function ()
|
|
3
|
+
{
|
|
4
|
+
let treeList;
|
|
5
|
+
let candidateNodesCount = 0;
|
|
6
|
+
let flows = [];
|
|
7
|
+
let flowMap = {};
|
|
8
|
+
|
|
9
|
+
function onEditPrepare(node, targetTypes)
|
|
10
|
+
{
|
|
11
|
+
if (!node.links)
|
|
12
|
+
node.links = [];
|
|
13
|
+
|
|
14
|
+
const activeSubflow = RED.nodes.subflow(node.z);
|
|
15
|
+
|
|
16
|
+
treeList = $("<div>")
|
|
17
|
+
.css({ width: "100%", height: "100%" })
|
|
18
|
+
.appendTo(".node-input-link-row")
|
|
19
|
+
.treeList({ autoSelect: false })
|
|
20
|
+
.on("treelistitemmouseover", function (e, item)
|
|
21
|
+
{
|
|
22
|
+
if (item.node)
|
|
23
|
+
{
|
|
24
|
+
item.node.highlighted = true;
|
|
25
|
+
item.node.dirty = true;
|
|
26
|
+
RED.view.redraw();
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.on("treelistitemmouseout", function (e, item)
|
|
30
|
+
{
|
|
31
|
+
if (item.node)
|
|
32
|
+
{
|
|
33
|
+
item.node.highlighted = false;
|
|
34
|
+
item.node.dirty = true;
|
|
35
|
+
RED.view.redraw();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
flows = [];
|
|
40
|
+
flowMap = {};
|
|
41
|
+
|
|
42
|
+
if (activeSubflow)
|
|
43
|
+
{
|
|
44
|
+
flowMap[activeSubflow.id] = {
|
|
45
|
+
id: activeSubflow.id,
|
|
46
|
+
class: "red-ui-palette-header",
|
|
47
|
+
label: "Subflow : " + (activeSubflow.name || activeSubflow.id),
|
|
48
|
+
expanded: true,
|
|
49
|
+
children: []
|
|
50
|
+
};
|
|
51
|
+
flows.push(flowMap[activeSubflow.id]);
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
{
|
|
55
|
+
RED.nodes.eachWorkspace(function (ws)
|
|
56
|
+
{
|
|
57
|
+
if (!ws.disabled)
|
|
58
|
+
{
|
|
59
|
+
flowMap[ws.id] = {
|
|
60
|
+
id: ws.id,
|
|
61
|
+
class: "red-ui-palette-header",
|
|
62
|
+
label: (ws.label || ws.id) + (node.z === ws.id ? " *" : ""),
|
|
63
|
+
expanded: true,
|
|
64
|
+
children: []
|
|
65
|
+
};
|
|
66
|
+
flows.push(flowMap[ws.id]);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setTimeout(function ()
|
|
72
|
+
{
|
|
73
|
+
treeList.treeList("show", node.z);
|
|
74
|
+
}, 100);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function initTreeList(node, targetTypes)
|
|
78
|
+
{
|
|
79
|
+
candidateNodesCount = 0;
|
|
80
|
+
for (const key in flowMap)
|
|
81
|
+
{
|
|
82
|
+
flowMap[key].children = [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let candidateNodes = [];
|
|
86
|
+
|
|
87
|
+
targetTypes.forEach(function (targetType)
|
|
88
|
+
{
|
|
89
|
+
candidateNodes = candidateNodes.concat(RED.nodes.filterNodes({ type: targetType }));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
candidateNodes.forEach(function (n)
|
|
93
|
+
{
|
|
94
|
+
if (flowMap[n.z])
|
|
95
|
+
{
|
|
96
|
+
const isChecked = (node.links.indexOf(n.id) !== -1) || (n.links || []).indexOf(node.id) !== -1;
|
|
97
|
+
if (isChecked)
|
|
98
|
+
{
|
|
99
|
+
flowMap[n.z].children.push({
|
|
100
|
+
id: n.id,
|
|
101
|
+
node: n,
|
|
102
|
+
label: n.name || n.id,
|
|
103
|
+
selected: false,
|
|
104
|
+
checkbox: false,
|
|
105
|
+
radio: false
|
|
106
|
+
});
|
|
107
|
+
candidateNodesCount++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
for (const key in flowMap)
|
|
113
|
+
{
|
|
114
|
+
flowMap[key].children.sort((a, b) => a.label.localeCompare(b.label));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const flowsFiltered = flows.filter(function (f) { return f.children.length > 0 });
|
|
118
|
+
treeList.treeList("empty");
|
|
119
|
+
treeList.treeList("data", flowsFiltered);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resizeNodeList()
|
|
123
|
+
{
|
|
124
|
+
var rows = $("#dialog-form>div:not(.node-input-link-row)");
|
|
125
|
+
var height = $("#dialog-form").height();
|
|
126
|
+
for (var i = 0; i < rows.length; i++)
|
|
127
|
+
{
|
|
128
|
+
height -= $(rows[i]).outerHeight(true);
|
|
129
|
+
}
|
|
130
|
+
var editorRow = $("#dialog-form>div.node-input-link-row");
|
|
131
|
+
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")));
|
|
132
|
+
$(".node-input-link-row").css("height", height + "px");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
RED.nodes.registerType("smart_light-control", {
|
|
136
|
+
category: "Smart Nodes",
|
|
137
|
+
paletteLabel: "Light control",
|
|
138
|
+
color: "#C882FF",
|
|
139
|
+
defaults: {
|
|
140
|
+
name: { value: "" },
|
|
141
|
+
exec_text_names: { value: "" },
|
|
142
|
+
max_time_on: { value: "0" },
|
|
143
|
+
max_time_on_unit: { value: "s" },
|
|
144
|
+
alarm_action: { value: 'NOTHING' }, // NOTHING | ON | OFF
|
|
145
|
+
alarm_off_action: { value: 'NOTHING' }, // NOTHING | ON | OFF | LAST | LAST_SENDED
|
|
146
|
+
links: { value: [], type: "smart_central-control[]" }
|
|
147
|
+
},
|
|
148
|
+
inputs: 1,
|
|
149
|
+
outputs: 1,
|
|
150
|
+
icon: "font-awesome/fa-lightbulb-o",
|
|
151
|
+
label: function ()
|
|
152
|
+
{
|
|
153
|
+
return this.name || "Light control";
|
|
154
|
+
},
|
|
155
|
+
oneditprepare: function ()
|
|
156
|
+
{
|
|
157
|
+
let node = this;
|
|
158
|
+
|
|
159
|
+
onEditPrepare(this, ["smart_central-control"]);
|
|
160
|
+
initTreeList(node, ["smart_central-control"]);
|
|
161
|
+
|
|
162
|
+
$("#node-input-max_time_on")
|
|
163
|
+
.spinner({
|
|
164
|
+
min: 0,
|
|
165
|
+
change: function (event, ui)
|
|
166
|
+
{
|
|
167
|
+
var value = parseInt(this.value);
|
|
168
|
+
value = isNaN(value) ? 0 : value;
|
|
169
|
+
value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
|
|
170
|
+
// value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
|
|
171
|
+
if (value !== this.value) $(this).spinner("value", value);
|
|
172
|
+
},
|
|
173
|
+
}).css("max-width", "4rem");
|
|
174
|
+
|
|
175
|
+
$("#node-input-max_time_on_unit")
|
|
176
|
+
.css("max-width", "10rem")
|
|
177
|
+
.typedInput({
|
|
178
|
+
types: [{
|
|
179
|
+
value: "max_time_on_unit",
|
|
180
|
+
default: "s",
|
|
181
|
+
options: [
|
|
182
|
+
{ value: "ms", label: node._("light.ui.milliseconds") },
|
|
183
|
+
{ value: "s", label: node._("light.ui.seconds") },
|
|
184
|
+
{ value: "min", label: node._("light.ui.minutes") },
|
|
185
|
+
{ value: "h", label: node._("light.ui.hours") },
|
|
186
|
+
],
|
|
187
|
+
}],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
$("#node-input-alarm_action").typedInput({
|
|
191
|
+
types: [{
|
|
192
|
+
value: "alarm_action",
|
|
193
|
+
default: "NOTHING",
|
|
194
|
+
options: [
|
|
195
|
+
{ value: "NOTHING", label: node._("light.ui.no_action") },
|
|
196
|
+
{ value: "ON", label: node._("light.ui.turn_on") },
|
|
197
|
+
{ value: "OFF", label: node._("light.ui.turn_off") }
|
|
198
|
+
],
|
|
199
|
+
}],
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
$("#node-input-alarm_off_action").typedInput({
|
|
203
|
+
types: [{
|
|
204
|
+
value: "alarm_off_action",
|
|
205
|
+
default: "NOTHING",
|
|
206
|
+
options: [
|
|
207
|
+
{ value: "NOTHING", label: node._("light.ui.no_action") },
|
|
208
|
+
{ value: "ON", label: node._("light.ui.turn_on") },
|
|
209
|
+
{ value: "OFF", label: node._("light.ui.turn_off") },
|
|
210
|
+
{ value: "LAST", label: node._("light.ui.last_state") },
|
|
211
|
+
{ value: "LAST_SENDED", label: node._("light.ui.last_sended_state") }
|
|
212
|
+
],
|
|
213
|
+
}],
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
onadd: function ()
|
|
217
|
+
{
|
|
218
|
+
this.links = [];
|
|
219
|
+
},
|
|
220
|
+
oneditresize: resizeNodeList
|
|
221
|
+
});
|
|
222
|
+
})();
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<script type="text/html" data-template-name="smart_light-control">
|
|
226
|
+
<div class="form-row">
|
|
227
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="light.ui.name"></span></label>
|
|
228
|
+
<input type="text" id="node-input-name" data-i18n="[placeholder]light.ui.name" />
|
|
229
|
+
</div>
|
|
230
|
+
<div class="form-row">
|
|
231
|
+
<label for="node-input-exec_text_names"><i class="fa fa-comments-o"></i> <span data-i18n="light.ui.text"></span></label>
|
|
232
|
+
<input id="node-input-exec_text_names" type="text" />
|
|
233
|
+
<div style="max-width: 450px;" data-i18n="light.ui.controlled_by_words"></div>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="form-row">
|
|
236
|
+
<label for="node-input-max_time_on"><i class="fa fa-clock-o"></i> <span data-i18n="light.ui.time_on"></span></label>
|
|
237
|
+
<input id="node-input-max_time_on" value="0" />
|
|
238
|
+
<input id="node-input-max_time_on_unit" />
|
|
239
|
+
</div>
|
|
240
|
+
<div class="form-row">
|
|
241
|
+
<label for="node-input-alarm_action"><i class="fa fa-exclamation-triangle"></i> <span data-i18n="light.ui.alarm_on"></span></label>
|
|
242
|
+
<input id="node-input-alarm_action"/>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="form-row">
|
|
245
|
+
<label for="node-input-alarm_off_action"><i class="fa fa-exclamation-triangle"></i> <span data-i18n="light.ui.alarm_off"></span></label>
|
|
246
|
+
<input id="node-input-alarm_off_action"/>
|
|
247
|
+
</div>
|
|
248
|
+
<span><i class="fa fa-link"></i> <span data-i18n="light.ui.controlled_by_central"></span></span>
|
|
249
|
+
<div class="form-row node-input-link-row node-input-link-rows"></div>
|
|
250
|
+
</script>
|