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.
Files changed (116) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +35 -17
  3. package/central/central.html +27 -24
  4. package/central/central.js +84 -26
  5. package/central/locales/de-DE/central.html +10 -0
  6. package/central/locales/de-DE/central.json +14 -0
  7. package/central/locales/en-US/central.html +10 -0
  8. package/central/locales/en-US/central.json +14 -0
  9. package/compare/compare.html +64 -87
  10. package/compare/compare.js +69 -29
  11. package/compare/locales/de-DE/compare.html +36 -0
  12. package/compare/locales/de-DE/compare.json +35 -0
  13. package/compare/locales/en-US/compare.html +36 -0
  14. package/compare/locales/en-US/compare.json +35 -0
  15. package/counter/counter.html +70 -72
  16. package/counter/counter.js +43 -20
  17. package/counter/locales/de-DE/counter.html +48 -0
  18. package/counter/locales/de-DE/counter.json +21 -0
  19. package/counter/locales/en-US/counter.html +48 -0
  20. package/counter/locales/en-US/counter.json +21 -0
  21. package/delay/delay.html +30 -102
  22. package/delay/delay.js +63 -20
  23. package/delay/locales/de-DE/delay.html +71 -0
  24. package/delay/locales/de-DE/delay.json +19 -0
  25. package/delay/locales/en-US/delay.html +76 -0
  26. package/delay/locales/en-US/delay.json +19 -0
  27. package/forwarder/forwarder.html +11 -42
  28. package/forwarder/forwarder.js +59 -18
  29. package/forwarder/locales/de-DE/forwarder.html +32 -0
  30. package/forwarder/locales/de-DE/forwarder.json +15 -0
  31. package/forwarder/locales/en-US/forwarder.html +32 -0
  32. package/forwarder/locales/en-US/forwarder.json +15 -0
  33. package/heating-curve/heating-curve.html +10 -51
  34. package/heating-curve/heating-curve.js +38 -13
  35. package/heating-curve/locales/de-DE/heating-curve.html +38 -0
  36. package/heating-curve/locales/de-DE/heating-curve.json +12 -0
  37. package/heating-curve/locales/en-US/heating-curve.html +38 -0
  38. package/heating-curve/locales/en-US/heating-curve.json +12 -0
  39. package/hysteresis/hysteresis.html +49 -69
  40. package/hysteresis/hysteresis.js +94 -68
  41. package/hysteresis/locales/de-DE/hysteresis.html +36 -0
  42. package/hysteresis/locales/de-DE/hysteresis.json +27 -0
  43. package/hysteresis/locales/en-US/hysteresis.html +36 -0
  44. package/hysteresis/locales/en-US/hysteresis.json +27 -0
  45. package/light/light.html +250 -0
  46. package/{light-control/light-control.js → light/light.js} +151 -32
  47. package/light/locales/de-DE/light.html +149 -0
  48. package/light/locales/de-DE/light.json +24 -0
  49. package/light/locales/en-US/light.html +148 -0
  50. package/light/locales/en-US/light.json +24 -0
  51. package/logic/locales/de-DE/logic.html +12 -0
  52. package/logic/locales/de-DE/logic.json +26 -0
  53. package/logic/locales/en-US/logic.html +12 -0
  54. package/logic/locales/en-US/logic.json +26 -0
  55. package/logic/logic.html +48 -64
  56. package/logic/logic.js +63 -29
  57. package/long-press/locales/de-DE/long-press.html +5 -0
  58. package/long-press/locales/de-DE/long-press.json +13 -0
  59. package/long-press/locales/en-US/long-press.html +5 -0
  60. package/long-press/locales/en-US/long-press.json +13 -0
  61. package/{long-press-control/long-press-control.html → long-press/long-press.html} +10 -14
  62. package/long-press/long-press.js +163 -0
  63. package/mixing-valve/locales/de-DE/mixing-valve.html +65 -0
  64. package/mixing-valve/locales/de-DE/mixing-valve.json +19 -0
  65. package/mixing-valve/locales/en-US/mixing-valve.html +66 -0
  66. package/mixing-valve/locales/en-US/mixing-valve.json +19 -0
  67. package/mixing-valve/mixing-valve.html +27 -93
  68. package/mixing-valve/mixing-valve.js +87 -61
  69. package/multi-press/locales/de-DE/multi-press.html +5 -0
  70. package/multi-press/locales/de-DE/multi-press.json +12 -0
  71. package/multi-press/locales/en-US/multi-press.html +5 -0
  72. package/multi-press/locales/en-US/multi-press.json +12 -0
  73. package/{multi-press-control/multi-press-control.html → multi-press/multi-press.html} +9 -13
  74. package/{multi-press-control/multi-press-control.js → multi-press/multi-press.js} +53 -5
  75. package/package.json +7 -7
  76. package/persistence.js +1 -0
  77. package/scene/locales/de-DE/scene.html +105 -0
  78. package/scene/locales/de-DE/scene.json +21 -0
  79. package/scene/locales/en-US/scene.html +107 -0
  80. package/scene/locales/en-US/scene.json +20 -0
  81. package/{scene-control/scene-control.html → scene/scene.html} +30 -132
  82. package/{scene-control/scene-control.js → scene/scene.js} +76 -26
  83. package/scheduler/locales/de-DE/scheduler.html +30 -0
  84. package/scheduler/locales/de-DE/scheduler.json +21 -0
  85. package/scheduler/locales/en-US/scheduler.html +30 -0
  86. package/scheduler/locales/en-US/scheduler.json +21 -0
  87. package/scheduler/scheduler.html +34 -64
  88. package/scheduler/scheduler.js +85 -53
  89. package/shutter/locales/de-DE/shutter.html +127 -0
  90. package/shutter/locales/de-DE/shutter.json +11 -0
  91. package/shutter/locales/en-US/shutter.html +133 -0
  92. package/shutter/locales/en-US/shutter.json +11 -0
  93. package/{shutter-control/shutter-control.html → shutter/shutter.html} +7 -133
  94. package/{shutter-control/shutter-control.js → shutter/shutter.js} +116 -56
  95. package/shutter-complex/locales/de-DE/shutter-complex.html +120 -0
  96. package/shutter-complex/locales/de-DE/shutter-complex.json +20 -0
  97. package/shutter-complex/locales/en-US/shutter-complex.html +120 -0
  98. package/shutter-complex/locales/en-US/shutter-complex.json +20 -0
  99. package/{shutter-complex-control/shutter-complex-control.html → shutter-complex/shutter-complex.html} +36 -140
  100. package/shutter-complex/shutter-complex.js +578 -0
  101. package/smart_helper.js +52 -9
  102. package/statistic/locales/de-DE/statistic.html +10 -0
  103. package/statistic/locales/de-DE/statistic.json +29 -0
  104. package/statistic/locales/en-US/statistic.html +10 -0
  105. package/statistic/locales/en-US/statistic.json +29 -0
  106. package/statistic/statistic.html +38 -43
  107. package/statistic/statistic.js +57 -28
  108. package/text-exec/locales/de-DE/text-exec.html +18 -0
  109. package/text-exec/locales/de-DE/text-exec.json +7 -0
  110. package/text-exec/locales/en-US/text-exec.html +18 -0
  111. package/text-exec/locales/en-US/text-exec.json +7 -0
  112. package/text-exec/text-exec.html +9 -25
  113. package/text-exec/text-exec.js +43 -2
  114. package/light-control/light-control.html +0 -363
  115. package/long-press-control/long-press-control.js +0 -76
  116. package/shutter-complex-control/shutter-complex-control.js +0 -442
@@ -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
- // persistant values
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
- // dynamic config
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
- let value = parseFloat(msg.payload);
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
- // node.error("Invalid payload: " + msg.payload);
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
- let result = getResult(value);
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
- node.on("close", function ()
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 = noChange =>
174
+ let setStatus = () =>
149
175
  {
150
- if (noChange)
151
- node.status({ fill: node_settings.last_result ? "green" : "red", shape: "ring", text: helper.getCurrentTimeForStatus() + ": No change by value " + node_settings.last_value + "" });
152
- else if (node_settings.last_result === true)
153
- node.status({ fill: "green", shape: "dot", text: helper.getCurrentTimeForStatus() + ": Turned higher by value " + node_settings.last_value + "" });
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 Object.assign({}, out_type == "original" ? msg : out_msg, {
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
+ }
@@ -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>