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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +127 -0
  4. package/central/central.html +328 -0
  5. package/central/central.js +95 -0
  6. package/compare/compare.html +137 -0
  7. package/compare/compare.js +151 -0
  8. package/delay/delay.html +192 -0
  9. package/delay/delay.js +175 -0
  10. package/examples/central.json +804 -0
  11. package/examples/central.png +0 -0
  12. package/examples/compare.json +916 -0
  13. package/examples/compare.png +0 -0
  14. package/examples/delay.json +198 -0
  15. package/examples/delay.png +0 -0
  16. package/examples/forwarder.json +152 -0
  17. package/examples/forwarder.png +0 -0
  18. package/examples/hysteresis.json +358 -0
  19. package/examples/hysteresis.png +0 -0
  20. package/examples/light-control.json +499 -0
  21. package/examples/light-control.png +0 -0
  22. package/examples/logic.json +562 -0
  23. package/examples/logic.png +0 -0
  24. package/examples/long-press-control.json +113 -0
  25. package/examples/long-press-control.png +0 -0
  26. package/examples/multi-press-control.json +136 -0
  27. package/examples/multi-press-control.png +0 -0
  28. package/examples/scene-control.json +535 -0
  29. package/examples/scene-control.png +0 -0
  30. package/examples/scheduler.json +164 -0
  31. package/examples/scheduler.png +0 -0
  32. package/examples/shutter-complex-control.json +489 -0
  33. package/examples/shutter-complex-control.png +0 -0
  34. package/examples/shutter-control.json +457 -0
  35. package/examples/shutter-control.png +0 -0
  36. package/examples/statistic.json +1112 -0
  37. package/examples/statistic.png +0 -0
  38. package/forwarder/forwarder.html +100 -0
  39. package/forwarder/forwarder.js +95 -0
  40. package/hysteresis/hysteresis.html +152 -0
  41. package/hysteresis/hysteresis.js +146 -0
  42. package/light-control/light-control.html +358 -0
  43. package/light-control/light-control.js +231 -0
  44. package/logic/logic.html +168 -0
  45. package/logic/logic.js +171 -0
  46. package/long-press-control/long-press-control.html +74 -0
  47. package/long-press-control/long-press-control.js +75 -0
  48. package/multi-press-control/multi-press-control.html +135 -0
  49. package/multi-press-control/multi-press-control.js +68 -0
  50. package/package.json +59 -0
  51. package/persistence.js +74 -0
  52. package/scene-control/scene-control.html +575 -0
  53. package/scene-control/scene-control.js +265 -0
  54. package/scheduler/scheduler.html +338 -0
  55. package/scheduler/scheduler.js +209 -0
  56. package/shutter-complex-control/shutter-complex-control.html +330 -0
  57. package/shutter-complex-control/shutter-complex-control.js +399 -0
  58. package/shutter-control/shutter-control.html +283 -0
  59. package/shutter-control/shutter-control.js +208 -0
  60. package/smart_helper.js +156 -0
  61. package/statistic/statistic.html +107 -0
  62. package/statistic/statistic.js +196 -0
Binary file
@@ -0,0 +1,100 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("smart_forwarder", {
3
+ category: "Smart Nodes",
4
+ paletteLabel: "Forwarder",
5
+ color: "#E2D96E",
6
+ defaults: {
7
+ name: { value: "" },
8
+ enabled: { value: true },
9
+ always_forward_true: { value: false },
10
+ always_forward_false: { value: false },
11
+ forward_last_on_enable: { value: false },
12
+ save_state: { value: true },
13
+ resend_on_start: { value: true }
14
+ },
15
+ inputs: 1,
16
+ outputs: 1,
17
+ icon: "font-awesome/fa-forward",
18
+ label: function ()
19
+ {
20
+ return this.name || "Forwarder";
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_forwarder">
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
+ <input type="checkbox" id="node-input-enabled" style="width: 20px;" />
43
+ <span for="node-input-enabled" style="width: 200px;"> Aktiviert</span>
44
+ </div>
45
+ <div class="form-row">
46
+ <input type="checkbox" id="node-input-forward_last_on_enable" style="width: 20px;" />
47
+ <span for="node-input-forward_last_on_enable">Letzte nicht gesendete Nachricht senden, wenn die Node aktiviert wird.</span>
48
+ </div>
49
+ <div class="form-row">
50
+ <input type="checkbox" id="node-input-always_forward_true" style="width: 20px;" />
51
+ <span for="node-input-always_forward_true"><code>msg.payload = true</code> immer weiterleiten</span>
52
+ </div>
53
+ <div class="form-row">
54
+ <input type="checkbox" id="node-input-always_forward_false" style="width: 20px;" />
55
+ <span for="node-input-always_forward_false"><code>msg.payload = false</code> immer weiterleiten</span>
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_forwarder">
70
+ <p>Diese Node leitet eine Nachricht weiter, wenn er enabled wurde oder das dauerhafte Weiterleiten für die Nachricht aktiviert ist.</p>
71
+ <p>
72
+ <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/>
73
+ Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
74
+ </p>
75
+ <p>
76
+ Folgende topics werden akzeptiert:
77
+ <table>
78
+ <thead>
79
+ <tr>
80
+ <th>Topic</th>
81
+ <th>Beschreibung</th>
82
+ </tr>
83
+ </thead>
84
+ <tbody>
85
+ <tr>
86
+ <td><code>enable</code></td>
87
+ <td>Aktiviert das Weiterleiten.</td>
88
+ </tr>
89
+ <tr>
90
+ <td><code>disable</code></td>
91
+ <td>Deaktiviert das Weiterleiten.</td>
92
+ </tr>
93
+ <tr>
94
+ <td><code>set_state</code></td>
95
+ <td>Aktiviert das Weiterleiten, wenn <code>msg.payload = true</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = false</code>.</td>
96
+ </tr>
97
+ </tbody>
98
+ </table>
99
+ </p>
100
+ </script>
@@ -0,0 +1,95 @@
1
+ module.exports = function (RED)
2
+ {
3
+ function ForwarderNode(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
+ enabled: config.enabled,
13
+ lastMessage: null,
14
+ last_msg_was_sended: true
15
+ };
16
+
17
+ if (config.save_state)
18
+ {
19
+ // load old saved values
20
+ nodeSettings = Object.assign(nodeSettings, smartContext.get(node.id));
21
+ }
22
+ else
23
+ {
24
+ // delete old saved values
25
+ smartContext.del(node.id);
26
+ }
27
+
28
+ // dynamic config
29
+ let forwardTrue = config.always_forward_true;
30
+ let forwardFalse = config.always_forward_false;
31
+ let forward_last_on_enable = config.forward_last_on_enable;
32
+
33
+ // runtime values
34
+
35
+
36
+ node.on("input", function (msg)
37
+ {
38
+ let newState = null;
39
+ if (msg.topic == "enable" || (msg.topic == "set_state" && msg.payload))
40
+ newState = true;
41
+ else if (msg.topic == "disable" || (msg.topic == "set_state" && !msg.payload))
42
+ newState = false;
43
+
44
+ // Already the correct state
45
+ if (newState != null && nodeSettings.enabled == newState)
46
+ return;
47
+
48
+ switch (newState)
49
+ {
50
+ case true:
51
+ case false:
52
+ nodeSettings.enabled = newState;
53
+
54
+ if (config.save_state)
55
+ smartContext.set(node.id, nodeSettings);
56
+
57
+ if (nodeSettings.enabled && forward_last_on_enable && nodeSettings.lastMessage != null && !nodeSettings.last_msg_was_sended)
58
+ {
59
+ node.send(nodeSettings.lastMessage);
60
+ nodeSettings.last_msg_was_sended = true;
61
+ }
62
+
63
+ setStatus();
64
+ break;
65
+
66
+ default:
67
+ // Forward if enabled or forced
68
+ if (nodeSettings.enabled || (forwardTrue && msg.payload) || (forwardFalse && !msg.payload))
69
+ {
70
+ node.send(msg);
71
+ nodeSettings.last_msg_was_sended = true;
72
+ }
73
+ else
74
+ {
75
+ nodeSettings.last_msg_was_sended = false;
76
+ }
77
+
78
+ nodeSettings.lastMessage = msg;
79
+ break;
80
+ }
81
+ });
82
+
83
+ let setStatus = () =>
84
+ {
85
+ if (nodeSettings.enabled)
86
+ node.status({ fill: "green", shape: "dot", text: "Forwarding enabled" });
87
+ else
88
+ node.status({ fill: "red", shape: "dot", text: "Forwarding disabled" });
89
+ }
90
+
91
+ setStatus();
92
+ }
93
+
94
+ RED.nodes.registerType("smart_forwarder", ForwarderNode);
95
+ }
@@ -0,0 +1,152 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("smart_hysteresis", {
3
+ category: "Smart Nodes",
4
+ paletteLabel: "Hysteresis",
5
+ color: "#E2D96E",
6
+ defaults: {
7
+ name: { value: "" },
8
+ setpoint: { value: 10 },
9
+ hysteresis: { value: 1 },
10
+ out_higher: { value: '{"topic": ""}' },
11
+ out_higher_type: { value: 'json' },
12
+ out_lower: { value: '{"topic": ""}' },
13
+ out_lower_type: { value: 'json' },
14
+ save_state: { value: true },
15
+ resend_on_start: { value: true }
16
+ },
17
+ inputs: 1,
18
+ outputs: 2,
19
+ outputLabels: ["Über", "Unter"],
20
+ icon: "font-awesome/fa-arrows-v",
21
+ label: function ()
22
+ {
23
+ return this.name || this.comparator || "Hysteresis";
24
+ },
25
+ oneditprepare: function ()
26
+ {
27
+ $("#node-input-setpoint").typedInput({
28
+ type: "num",
29
+ types: ["num"]
30
+ });
31
+
32
+ $("#node-input-hysteresis").typedInput({
33
+ type: "num",
34
+ types: ["num"]
35
+ });
36
+
37
+ $("#node-input-out_higher").typedInput({
38
+ type: "json",
39
+ types: ["json", {
40
+ value: "null",
41
+ label: "Originalnachricht",
42
+ icon: "fa fa-times",
43
+ hasValue: false,
44
+ }],
45
+ typeField: "#node-input-out_higher_type"
46
+ });
47
+
48
+ $("#node-input-out_lower").typedInput({
49
+ type: "json",
50
+ types: ["json", {
51
+ value: "null",
52
+ label: "Originalnachricht",
53
+ icon: "fa fa-times",
54
+ hasValue: false,
55
+ }],
56
+ typeField: "#node-input-out_lower_type"
57
+ });
58
+
59
+ $("#node-input-save_state").on("change", ev =>
60
+ {
61
+ if (ev.target.checked)
62
+ $("#resend_on_start_row").show();
63
+ else
64
+ $("#resend_on_start_row").hide();
65
+ });
66
+ $("#node-input-save_state").trigger("change");
67
+ },
68
+ oneditsave: function ()
69
+ {
70
+ this.setpoint = parseFloat(this.setpoint);
71
+ this.hysteresis = parseFloat(this.hysteresis);
72
+ }
73
+ });
74
+ </script>
75
+
76
+ <script type="text/html" data-template-name="smart_hysteresis">
77
+ <div class="form-row">
78
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
79
+ <input type="text" id="node-input-name" placeholder="Name" />
80
+ </div>
81
+ <div class="form-row">
82
+ <label for="node-input-setpoint"><i class="fa fa-sign-out"></i> Sollwert</label>
83
+ <input id="node-input-setpoint" />
84
+ </div>
85
+ <div class="form-row">
86
+ <label for="node-input-hysteresis"><i class="fa fa-sort"></i> Hysterese</label>
87
+ <input id="node-input-hysteresis" />
88
+ </div>
89
+ <hr/>
90
+ <h4 style="margin: 0.5rem 0;">Ausgangsnachrichten</h4>
91
+ <div class="form-row">
92
+ <label for="node-input-out_higher"><i class="fa fa-arrow-up"></i> Über</label>
93
+ <input type="text" id="node-input-out_higher"/>
94
+ <input type="hidden" id="node-input-out_higher_type">
95
+ </div>
96
+ <div class="form-row">
97
+ <label for="node-input-out_lower"><i class="fa fa-arrow-down"></i> Unter</label>
98
+ <input type="text" id="node-input-out_lower" />
99
+ <input type="hidden" id="node-input-out_lower_type">
100
+ </div>
101
+ <hr/>
102
+ <h4 style="margin: 0.5rem 0;">Systemstart</h4>
103
+ <div class="form-row">
104
+ <input type="checkbox" id="node-input-save_state" style="width: 20px;" />
105
+ <label for="node-input-save_state" style="width: calc(100% - 30px);">Zustand speichern</label>
106
+ </div>
107
+ <div class="form-row" id="resend_on_start_row">
108
+ <input type="checkbox" id="node-input-resend_on_start" style="width: 20px;" />
109
+ <label for="node-input-resend_on_start" style="width: calc(100% - 30px);">Letze Nachricht 10 Sekunden nach dem Start senden</label>
110
+ </div>
111
+ </script>
112
+
113
+ <script type="text/html" data-help-name="smart_hysteresis">
114
+ <p>
115
+ 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
116
+ die einkommende Nachricht an den Ausgang <code>Unter</code>, wenn der einkommende Wert kleiner gleich dem Sollwert minus Hysterese ist.<br/>
117
+ Die Nachricht wird dabei nur einmalig bei über- bzw. unterschreiten des Grenzwertes weitergeleitet.<br/>
118
+ Die Note behält den aktuellen Zustand auch nach dem Deploy bei.
119
+ </p>
120
+ <p>
121
+ <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/>
122
+ Diese Node verwendet nur den Teil <code>name</code>. <code>#</code> und <code>nummer</code> sind dabei optional.
123
+ </p>
124
+ <p>
125
+ Folgende topics werden akzeptiert:
126
+ <table>
127
+ <thead>
128
+ <tr>
129
+ <th>Topic</th>
130
+ <th>Beschreibung</th>
131
+ </tr>
132
+ </thead>
133
+ <tbody>
134
+ <tr>
135
+ <td><code>setpoint</code></td>
136
+ <td>Überschreibt den Sollwert mit <code>msg.payload</code>.</td>
137
+ </tr>
138
+ <tr>
139
+ <td><code>hysteresis</code></td>
140
+ <td>Überschreibt die Hysterese mit <code>msg.payload</code>.</td>
141
+ </tr>
142
+ <tr>
143
+ <td><code>resend</code></td>
144
+ <td>Sendet den aktuellen Zustand erneut.</td>
145
+ </tr>
146
+ </tbody>
147
+ </table>
148
+ </p>
149
+ <p>
150
+ Die einkommende Nachricht wird mit den angegebenen Ausgangsnachrichten ergänzt, sofern nicht Originalnachricht ausgewählt wurde.
151
+ </p>
152
+ </script>
@@ -0,0 +1,146 @@
1
+
2
+ module.exports = function (RED)
3
+ {
4
+ function HysteresisNode(config)
5
+ {
6
+ const node = this;
7
+ RED.nodes.createNode(node, config);
8
+
9
+ const smartContext = require("../persistence.js")(RED);
10
+ const helper = require("../smart_helper.js");
11
+
12
+ var nodeSettings = {
13
+ active: null,
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.active)
23
+ {
24
+ case true:
25
+ node.status({ fill: "yellow", shape: "ring", text: "Load last state: Higher" });
26
+ break;
27
+
28
+ case false:
29
+ node.status({ fill: "yellow", shape: "ring", text: "Load last state: Lower" });
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 setpoint = parseFloat(config.setpoint);
46
+ let hysteresis = parseFloat(config.hysteresis);
47
+ let out_higher = helper.evaluateNodeProperty(RED, config.out_higher, config.out_higher_type);
48
+ let out_lower = helper.evaluateNodeProperty(RED, config.out_lower, config.out_lower_type);
49
+
50
+ // runtime values
51
+
52
+ node.on("input", function (msg)
53
+ {
54
+ let value = parseFloat(msg.payload);
55
+ let realTopic = helper.getTopicName(msg.topic);
56
+
57
+ if (isNaN(value) && realTopic !== "resend")
58
+ {
59
+ // node.error("Invalid payload: " + msg.payload);
60
+ return;
61
+ }
62
+
63
+ switch (realTopic)
64
+ {
65
+ case "setpoint":
66
+ setpoint = value;
67
+ node.status({ fill: "yellow", shape: "ring", text: "New setpoint: " + value });
68
+
69
+ if (config.save_state)
70
+ smartContext.set(node.id, nodeSettings);
71
+ break;
72
+
73
+ case "hysteresis":
74
+ hysteresis = value;
75
+ node.status({ fill: "yellow", shape: "ring", text: "New hysteresis: " + value });
76
+
77
+ if (config.save_state)
78
+ smartContext.set(node.id, nodeSettings);
79
+ break;
80
+
81
+ case "resend":
82
+ if (nodeSettings.active === true && nodeSettings.lastMessage != null)
83
+ {
84
+ node.status({ fill: "green", shape: "dot", text: "Resend higher value " });
85
+ node.send([nodeSettings.lastMessage, null]);
86
+ }
87
+ else if (nodeSettings.active === false && nodeSettings.lastMessage != null)
88
+ {
89
+ node.status({ fill: "green", shape: "dot", text: "Resend lower value " });
90
+ node.send([null, nodeSettings.lastMessage]);
91
+ }
92
+ else
93
+ {
94
+ node.status({ fill: "green", shape: "dot", text: "No resend, state is unknown" });
95
+ }
96
+ break;
97
+
98
+ default:
99
+ if (value >= setpoint + hysteresis && nodeSettings.active !== true)
100
+ {
101
+ node.status({ fill: "green", shape: "dot", text: "Turned higher by value " + value });
102
+ nodeSettings.active = true;
103
+ nodeSettings.lastMessage = out_higher ?? msg;
104
+
105
+ if (config.save_state)
106
+ smartContext.set(node.id, nodeSettings);
107
+
108
+ node.send([nodeSettings.lastMessage, null]);
109
+ }
110
+ else if (value <= setpoint - hysteresis && nodeSettings.active !== false)
111
+ {
112
+ node.status({ fill: "green", shape: "dot", text: "Turned lower by value " + value });
113
+ nodeSettings.active = false;
114
+ nodeSettings.lastMessage = out_lower ?? msg;
115
+
116
+ if (config.save_state)
117
+ smartContext.set(node.id, nodeSettings);
118
+
119
+ node.send([null, nodeSettings.lastMessage]);
120
+ }
121
+ else
122
+ {
123
+ node.status({ fill: "yellow", shape: "ring", text: "No change by value " + value });
124
+ }
125
+ break;
126
+ }
127
+ });
128
+
129
+ node.on("close", function ()
130
+ {
131
+ });
132
+
133
+ if (config.save_state && config.resend_on_start && nodeSettings.active && nodeSettings.lastMessage != null)
134
+ {
135
+ setTimeout(() =>
136
+ {
137
+ if (nodeSettings.active)
138
+ node.send([nodeSettings.lastMessage, null]);
139
+ else
140
+ node.send([null, nodeSettings.lastMessage]);
141
+ }, 10000);
142
+ }
143
+ }
144
+
145
+ RED.nodes.registerType("smart_hysteresis", HysteresisNode);
146
+ }