smart-nodes 0.6.6 → 0.7.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 CHANGED
@@ -247,3 +247,13 @@
247
247
 
248
248
  - Counter can be configured in 0.1 steps. For smaller steps, use msg.payload value.
249
249
  - Counter has a new topic refresh, to resend the last saved value.
250
+
251
+ ## Version 0.6.7:
252
+
253
+ - Added crit temp change options to mixing-valve node.
254
+ - Added UTC time usage in scheduler node.
255
+
256
+ ## Version 0.7.0:
257
+
258
+ - Added support of home assistant boolean values "on" and "off"
259
+ - BREAKING CHANGE: The forwarder node is only using set_forwarder and set_forwarder_state topics.
package/README.md CHANGED
@@ -23,10 +23,10 @@ This smart nodes are still in development and can have breaking changes. So plea
23
23
 
24
24
  I spent a lots of hours in this project. If you like it, you can support my work in different ways:
25
25
 
26
- - Report bugs.
27
- - Create pull requests.
28
- - Name tipps and tricks if you see any improvement.
29
- - Donate via [Paypal](https://paypal.me/BergenSoft).
26
+ - Report bugs.
27
+ - Create pull requests.
28
+ - Name tipps and tricks if you see any improvement.
29
+ - Donate via [Paypal](https://paypal.me/BergenSoft).
30
30
 
31
31
  # Nodes
32
32
 
@@ -36,12 +36,12 @@ This node is able to control a light or a power outlet.
36
36
 
37
37
  ### **Features:**
38
38
 
39
- - Auto turn off the light after a fixed time.
40
- - Auto turn off the light with a custom time which is part of the message object.
41
- - Toggle light between on and off.
42
- - Can be activated by motion sensors.
43
- - Has an alarm on function to go to a specific state (on or off) when the alarm is activated.
44
- - Has an alarm off function to go to a specific (on, off, the last or the last sended) when the alarm is deactivated.
39
+ - Auto turn off the light after a fixed time.
40
+ - Auto turn off the light with a custom time which is part of the message object.
41
+ - Toggle light between on and off.
42
+ - Can be activated by motion sensors.
43
+ - Has an alarm on function to go to a specific state (on or off) when the alarm is activated.
44
+ - Has an alarm off function to go to a specific (on, off, the last or the last sended) when the alarm is deactivated.
45
45
 
46
46
  ## 2. Shutter control
47
47
 
@@ -50,12 +50,12 @@ There is no support for slats and it is also not planned as I don't need them, b
50
50
 
51
51
  ### **Features:**
52
52
 
53
- - One button control which switch between `up`, `stop`, `down`, `stop`.
54
- - Two button control for each direction `up and stop` and `down and stop`.
55
- - Send direct position the shutter should go to.
56
- - Stop shutter immediately.
57
- - Has an alarm on function to go to a specific state (Open or close) when the alarm is activated.
58
- - Has an alarm off function to restore a specific state when the alarm gets deaktivated.
53
+ - One button control which switch between `up`, `stop`, `down`, `stop`.
54
+ - Two button control for each direction `up and stop` and `down and stop`.
55
+ - Send direct position the shutter should go to.
56
+ - Stop shutter immediately.
57
+ - Has an alarm on function to go to a specific state (Open or close) when the alarm is activated.
58
+ - Has an alarm off function to restore a specific state when the alarm gets deaktivated.
59
59
 
60
60
  ## 3. Scene control
61
61
 
@@ -63,7 +63,7 @@ This node is tracking the state of multiple outputs and controls them by switchi
63
63
 
64
64
  ### **Features:**
65
65
 
66
- - An input message could name multiple scenes that should be iterated one by one by receiving the same message multiple times
66
+ - An input message could name multiple scenes that should be iterated one by one by receiving the same message multiple times
67
67
 
68
68
  ## 4. Central control
69
69
 
@@ -71,8 +71,8 @@ This node can control multiple light/scene controls or shutter controls at the s
71
71
 
72
72
  ### **Features:**
73
73
 
74
- - Take care that multiple light/scene controls are turned of if any of them is one before executing a toggle command. This is helpful to avoid turning one light on and the other off.
75
- - The same is used for shutters with the up_stop or down_stop commands. A toggle is not supported, it will only be forwarded.
74
+ - Take care that multiple light/scene controls are turned of if any of them is one before executing a toggle command. This is helpful to avoid turning one light on and the other off.
75
+ - The same is used for shutters with the up_stop or down_stop commands. A toggle is not supported, it will only be forwarded.
76
76
 
77
77
  ## 5. Long press control
78
78
 
@@ -81,7 +81,7 @@ The time can be configured in this node.
81
81
 
82
82
  ### **Features:**
83
83
 
84
- - Imediately send a message to the long press output when the time is reached. It is not waiting until the button is released.
84
+ - Imediately send a message to the long press output when the time is reached. It is not waiting until the button is released.
85
85
 
86
86
  ## 6. Multi press control
87
87
 
@@ -91,7 +91,7 @@ You can also choose 2-4 press detection.
91
91
 
92
92
  ### **Features:**
93
93
 
94
- - Imediately send a message to the last output when the max presses are reached. It is not waiting until the button is released.
94
+ - Imediately send a message to the last output when the max presses are reached. It is not waiting until the button is released.
95
95
 
96
96
  ## 7. Hysteresis
97
97
 
@@ -99,8 +99,8 @@ This node is checking if the input value reachs a defined value until the upper
99
99
 
100
100
  ### **Features:**
101
101
 
102
- - The state can be saved between NodeRed restarts.
103
- - The last message can automatically be sent 10 seconds after a deployment.
102
+ - The state can be saved between NodeRed restarts.
103
+ - The last message can automatically be sent 10 seconds after a deployment.
104
104
 
105
105
  ## 8. Logic
106
106
 
@@ -108,11 +108,11 @@ This node can be used for AND, OR and XOR logics. Inputs and outputs can be indi
108
108
 
109
109
  ### **Features:**
110
110
 
111
- - All input values could be individual converted as well as the output messsage.
112
- - A NOT logic is possible when selecting 1 input and convert the output.
113
- - The setpoint and hysteresis value can be changed in runtime.
114
- - The state can be saved between NodeRed restarts.
115
- - The last message can automatically be sent 10 seconds after a deployment.
111
+ - All input values could be individual converted as well as the output messsage.
112
+ - A NOT logic is possible when selecting 1 input and convert the output.
113
+ - The setpoint and hysteresis value can be changed in runtime.
114
+ - The state can be saved between NodeRed restarts.
115
+ - The last message can automatically be sent 10 seconds after a deployment.
116
116
 
117
117
  ## 9. Statistic
118
118
 
@@ -120,10 +120,10 @@ This node can be used for getting the following types of values: Minimum, Maximu
120
120
 
121
121
  ### **Features:**
122
122
 
123
- - All input values could be individual converted as well as the output messsage.
124
- - A NOT logic is possible when selecting 1 input and convert the output.
125
- - The state can be saved between NodeRed restarts.
126
- - The last message can automatically be sent 10 seconds after a deployment.
123
+ - All input values could be individual converted as well as the output messsage.
124
+ - A NOT logic is possible when selecting 1 input and convert the output.
125
+ - The state can be saved between NodeRed restarts.
126
+ - The last message can automatically be sent 10 seconds after a deployment.
127
127
 
128
128
  ## 10. Compare
129
129
 
@@ -131,9 +131,9 @@ This node can compare 2 values with the following operators: < <= == >= > !=
131
131
 
132
132
  ### **Features:**
133
133
 
134
- - Can compare also texts like js would do.
135
- - The state can be saved between NodeRed restarts.
136
- - The last message can automatically be sent 10 seconds after a deployment.
134
+ - Can compare also texts like js would do.
135
+ - The state can be saved between NodeRed restarts.
136
+ - The last message can automatically be sent 10 seconds after a deployment.
137
137
 
138
138
  ## 11. Delay
139
139
 
@@ -141,10 +141,10 @@ This node can delay incomming messages.
141
141
 
142
142
  ### **Features:**
143
143
 
144
- - Different times for `msg.payload = true` and `msg.payload = false`.
145
- - Delays can be changed in runtime.
146
- - The state can be saved between NodeRed restarts.
147
- - The last message can automatically be sent 10 seconds after a deployment.
144
+ - Different times for `msg.payload = true` and `msg.payload = false`.
145
+ - Delays can be changed in runtime.
146
+ - The state can be saved between NodeRed restarts.
147
+ - The last message can automatically be sent 10 seconds after a deployment.
148
148
 
149
149
  ## 12. Forwarder
150
150
 
@@ -152,9 +152,9 @@ This node can control if an incomming message should be forwarded or not.
152
152
 
153
153
  ### **Features:**
154
154
 
155
- - The forwarding can be enabled or disabled in runtime.
156
- - The state can be saved between NodeRed restarts.
157
- - The last message can automatically be sent 10 seconds after a deployment.
155
+ - The forwarding can be enabled or disabled in runtime.
156
+ - The state can be saved between NodeRed restarts.
157
+ - The last message can automatically be sent 10 seconds after a deployment.
158
158
 
159
159
  ## 13. Scheduler
160
160
 
@@ -162,11 +162,11 @@ This node can send a defined message on defined times.
162
162
 
163
163
  ### **Features:**
164
164
 
165
- - The weekdays and the time can be selected as a trigger.
166
- - Multiple trigger and messages can be defined in one node.
167
- - The scheduler can be activated or deactivated in runtime.
168
- - The state can be saved between NodeRed restarts.
169
- - The last message can automatically be sent 10 seconds after a deployment.
165
+ - The weekdays and the time can be selected as a trigger.
166
+ - Multiple trigger and messages can be defined in one node.
167
+ - The scheduler can be activated or deactivated in runtime.
168
+ - The state can be saved between NodeRed restarts.
169
+ - The last message can automatically be sent 10 seconds after a deployment.
170
170
 
171
171
  ## 14. Text execution
172
172
 
@@ -174,23 +174,23 @@ This node parses a text and performs actions to the selected and matching smart
174
174
 
175
175
  ### **Features:**
176
176
 
177
- - Control light and scenes (only on or off) in german and english.
178
- - Control shutter in german and english.
177
+ - Control light and scenes (only on or off) in german and english.
178
+ - Control shutter in german and english.
179
179
 
180
180
  ### Examples
181
181
 
182
182
  Text in [ Braces ] is optional<br/>
183
183
  Room names that has to be entered in the nodes are in `code` style.
184
184
 
185
- - Turn on [the light in] the `living room` and the `kitchen` off.
186
- - Open [the shutter in] the `kitchen` and turn `studio` off.
187
- - Close [the shutter in the] `sleeping` room.
188
- - `Living room` to 10 %.
189
- - Turn on `living room` except the `couch` light.
185
+ - Turn on [the light in] the `living room` and the `kitchen` off.
186
+ - Open [the shutter in] the `kitchen` and turn `studio` off.
187
+ - Close [the shutter in the] `sleeping` room.
188
+ - `Living room` to 10 %.
189
+ - Turn on `living room` except the `couch` light.
190
190
 
191
191
  ### **Features:**
192
192
 
193
- - Sends debug message to output.
193
+ - Sends debug message to output.
194
194
 
195
195
  ## 15. Mixing valve
196
196
 
@@ -198,9 +198,10 @@ This node can control a mixing valve to get the required tput temperature. It is
198
198
 
199
199
  ### **Features:**
200
200
 
201
- - Define target temperature.
202
- - Support heating and cooling.
203
- - Define off mode to do nothing, open or close the valve.
201
+ - Define target temperature.
202
+ - Support heating and cooling.
203
+ - Define off mode to do nothing, open or close the valve.
204
+ - Fast change if crit temp is exeeded.
204
205
 
205
206
  ## 16. Heating curve
206
207
 
@@ -208,9 +209,9 @@ This node calculates the needed flow temperature regarding to the target room te
208
209
 
209
210
  ### **Features:**
210
211
 
211
- - Define slope and offset values.
212
- - Limit flow temperature with min and max values.
213
- - Preview of different curves in the node editor.
212
+ - Define slope and offset values.
213
+ - Limit flow temperature with min and max values.
214
+ - Preview of different curves in the node editor.
214
215
 
215
216
  ## 17. Counter
216
217
 
@@ -218,9 +219,9 @@ This node countes up and down within a specified range.
218
219
 
219
220
  ### **Features:**
220
221
 
221
- - Define min, max and step values.
222
- - Set to a specific value.
223
- - Increment and decrement by default or by a given value.
222
+ - Define min, max and step values.
223
+ - Set to a specific value.
224
+ - Increment and decrement by default or by a given value.
224
225
 
225
226
  ## 18. Mode Selector
226
227
 
@@ -228,6 +229,6 @@ This node can define multiple modes which stores the last selected mode persiten
228
229
 
229
230
  ### **Features:**
230
231
 
231
- - Define custom modes.
232
- - The first mode is the default on first start.
233
- - Save last mode persistent and resend it at start, if wanted.
232
+ - Define custom modes.
233
+ - The first mode is the default on first start.
234
+ - Save last mode persistent and resend it at start, if wanted.
@@ -1 +1 @@
1
- {"idn1":{"last_value":false,"last_value_before_alarm":false,"last_value_sended":false,"alarm_active":false}}6666666666667,"alarm_active":false}}
1
+ {"idn1":{"last_position":0,"last_direction_up":false}}
@@ -8,6 +8,7 @@
8
8
  enabled: { value: true },
9
9
  always_forward_true: { value: false },
10
10
  always_forward_false: { value: false },
11
+ always_forward_other: { value: true },
11
12
  forward_last_on_enable: { value: false },
12
13
  save_state: { value: false },
13
14
  resend_on_start: { value: false },
@@ -62,6 +63,10 @@
62
63
  <input type="checkbox" id="node-input-always_forward_false" style="width: 20px;" />
63
64
  <label for="node-input-always_forward_false" style="width: calc(100% - 30px);" data-i18n="[html]forwarder.ui.always_forward_false"></label>
64
65
  </div>
66
+ <div class="form-row">
67
+ <input type="checkbox" id="node-input-always_forward_other" style="width: 20px;" />
68
+ <label for="node-input-always_forward_other" style="width: calc(100% - 30px);" data-i18n="[html]forwarder.ui.always_forward_other"></label>
69
+ </div>
65
70
  <hr/>
66
71
  <h4 style="margin: 0.5rem 0;" data-i18n="forwarder.ui.system_start"></h4>
67
72
  <div class="form-row">
@@ -42,6 +42,7 @@ module.exports = function (RED)
42
42
  // ##################
43
43
  let forward_true = config.always_forward_true;
44
44
  let forward_false = config.always_forward_false;
45
+ let forward_other = config.always_forward_other;
45
46
  let forward_last_on_enable = config.forward_last_on_enable;
46
47
 
47
48
 
@@ -85,18 +86,21 @@ module.exports = function (RED)
85
86
 
86
87
  if (real_topic != null)
87
88
  {
88
- if (real_topic.startsWith("set_state"))
89
- real_topic = real_topic.replace("set_state", "set");
89
+ if (real_topic == "set_forwarder_state")
90
+ real_topic = "set_forwarder";
90
91
 
91
- if (real_topic == "set_inverted")
92
+ if (real_topic == "set_forwarder_state_inverted")
93
+ real_topic = "set_forwarder_inverted";
94
+
95
+ if (real_topic == "set_forwarder_inverted")
92
96
  {
93
- real_topic = "set";
94
- msg.payload = !msg.payload;
97
+ real_topic = "set_forwarder";
98
+ msg.payload = !helper.toBool(msg.payload);
95
99
  }
96
100
 
97
- if (real_topic == "enable" || (real_topic == "set" && msg.payload))
101
+ if (real_topic == "enable" || (real_topic == "set_forwarder" && helper.toBool(msg.payload)))
98
102
  new_state = true;
99
- else if (real_topic == "disable" || (real_topic == "set" && !msg.payload))
103
+ else if (real_topic == "disable" || (real_topic == "set_forwarder" && !helper.toBool(msg.payload)))
100
104
  new_state = false;
101
105
  }
102
106
 
@@ -133,7 +137,7 @@ module.exports = function (RED)
133
137
 
134
138
  default:
135
139
  // Forward if enabled or forced
136
- if (node_settings.enabled || (forward_true && msg.payload) || (forward_false && !msg.payload))
140
+ if (node_settings.enabled || (forward_true && helper.toBool(msg.payload) === true) || (forward_false && helper.toBool(msg.payload) === false) || (forward_other && typeof msg.payload !== "boolean"))
137
141
  {
138
142
  node.send(helper.cloneObject(msg));
139
143
  node_settings.last_msg_was_sended = true;
@@ -27,11 +27,11 @@
27
27
  <td>Deaktiviert das Weiterleiten.</td>
28
28
  </tr>
29
29
  <tr>
30
- <td><code>set</code> oder <code>set_state</code></td>
30
+ <td><code>set_forwarder</code> oder <code>set_forwarder_state</code></td>
31
31
  <td>Aktiviert das Weiterleiten, wenn <code>msg.payload = true</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = false</code>.</td>
32
32
  </tr>
33
33
  <tr>
34
- <td><code>set_inverted</code> oder <code>set_state_inverted</code></td>
34
+ <td><code>set_forwarder_inverted</code> oder <code>set_forwarder_state_inverted</code></td>
35
35
  <td>Aktiviert das Weiterleiten, wenn <code>msg.payload = false</code> oder deaktiviert das Weiterleiten, wenn <code>msg.payload = true</code>.</td>
36
36
  </tr>
37
37
  </tbody>
@@ -6,6 +6,7 @@
6
6
  "forward_last_on_enable": "Letzte nicht gesendete Nachricht senden,<br>wenn die Node aktiviert wird.",
7
7
  "always_forward_true": "Leite <code>msg.payload = true</code> immer weiter.",
8
8
  "always_forward_false": "Leite <code>msg.payload = false</code> immer weiter.",
9
+ "always_forward_other": "Leite alle anderen <code>msg.payload</code> Werte immer weiter.",
9
10
 
10
11
  "system_start": "Systemstart",
11
12
  "save_state": "Zustand speichern",
@@ -27,11 +27,11 @@
27
27
  <td>Disables forwarding.</td>
28
28
  </tr>
29
29
  <tr>
30
- <td><code>set</code> or <code>set_state</code></td>
30
+ <td><code>set_forwarder</code> or <code>set_forwarder_state</code></td>
31
31
  <td>Enables forwarding if <code>msg.payload = true</code> or disables forwarding if <code>msg.payload = false</code>.</td>
32
32
  </tr>
33
33
  <tr>
34
- <td><code>set_inverted</code> or <code>set_state_inverted</code></td>
34
+ <td><code>set_forwarder_inverted</code> or <code>set_forwarder_state_inverted</code></td>
35
35
  <td>Enables forwarding if <code>msg.payload = false</code> or disables forwarding if <code>msg.payload = true</code>.</td>
36
36
  </tr>
37
37
  </tbody>
@@ -4,8 +4,9 @@
4
4
  "name": "Name",
5
5
  "enabled": "Enabled",
6
6
  "forward_last_on_enable": "Send last unsent message when node is activated.",
7
- "always_forward_true": "Always forward <code>msg.payload = true</code> .",
8
- "always_forward_false": "Always forward <code>msg.payload = false</code> .",
7
+ "always_forward_true": "Always forward <code>msg.payload = true</code>.",
8
+ "always_forward_false": "Always forward <code>msg.payload = false</code>.",
9
+ "always_forward_other": "Always forward all other <code>msg.payload</code> values.",
9
10
 
10
11
  "system_start": "System start",
11
12
  "save_state": "Save state",
package/light/light.js CHANGED
@@ -117,7 +117,7 @@ module.exports = function (RED)
117
117
  real_topic = "set";
118
118
 
119
119
  if (mode == "BOOL")
120
- msg.payload = !msg.payload;
120
+ msg.payload = !helper.toBool(msg.payload);
121
121
  else if (mode == "PERCENTAGE")
122
122
  msg.payload = 100 - boolToInt(msg.payload);
123
123
  }
@@ -141,22 +141,22 @@ module.exports = function (RED)
141
141
 
142
142
  // Make sure it is bool or int
143
143
  if (mode == "BOOL")
144
- msg.payload = !!msg.payload;
144
+ msg.payload = helper.toBool(msg.payload);
145
145
  else if (mode == "PERCENTAGE")
146
146
  msg.payload = boolToInt(msg.payload);
147
147
 
148
148
  // Output is already in the state of the status value and the timeout is running.
149
149
  // No need to restart the timeout.
150
- if (node_settings.last_value == msg.payload && timeout != null)
150
+ if (node_settings.last_value == helper.toBool(msg.payload) && timeout != null)
151
151
  doRestartTimer = false;
152
152
 
153
- node_settings.last_value = msg.payload;
153
+ node_settings.last_value = helper.toBool(msg.payload);
154
154
  current_output_state = node_settings.last_value;
155
155
  break;
156
156
 
157
157
  case "off":
158
158
  // If button is released, don't handle this message
159
- if (msg.payload === false)
159
+ if (helper.toBool(msg.payload) === false)
160
160
  return;
161
161
 
162
162
  isBlinking = false;
@@ -171,7 +171,7 @@ module.exports = function (RED)
171
171
 
172
172
  case "on":
173
173
  // If button is released, don't handle this message
174
- if (msg.payload === false)
174
+ if (helper.toBool(msg.payload) === false)
175
175
  return;
176
176
 
177
177
  isBlinking = false;
@@ -187,33 +187,33 @@ module.exports = function (RED)
187
187
  case "set":
188
188
  // Make sure it is bool or int
189
189
  if (mode == "BOOL")
190
- msg.payload = !!msg.payload;
190
+ msg.payload = helper.toBool(msg.payload);
191
191
  else if (mode == "PERCENTAGE")
192
192
  msg.payload = boolToInt(msg.payload);
193
193
 
194
194
  isBlinking = false;
195
195
 
196
- node_settings.last_value = msg.payload;
196
+ node_settings.last_value = helper.toBool(msg.payload);
197
197
  node_settings.last_value_sended = node_settings.last_value;
198
198
  break;
199
199
 
200
200
  case "set_permanent":
201
201
  // Make sure it is bool or int
202
202
  if (mode == "BOOL")
203
- msg.payload = !!msg.payload;
203
+ msg.payload = helper.toBool(msg.payload);
204
204
  else if (mode == "PERCENTAGE")
205
205
  msg.payload = boolToInt(msg.payload);
206
206
 
207
207
  isBlinking = false;
208
- isPermanent = !!msg.payload; // false or 0 => not permanent
208
+ isPermanent = helper.toBool(msg.payload);
209
209
 
210
- node_settings.last_value = msg.payload;
210
+ node_settings.last_value = helper.toBool(msg.payload);
211
211
  node_settings.last_value_sended = node_settings.last_value;
212
212
  break;
213
213
 
214
214
  case "motion":
215
215
  // Make sure it is bool
216
- msg.payload = !!msg.payload;
216
+ msg.payload = helper.toBool(msg.payload);
217
217
  isMotion = msg.payload;
218
218
  isBlinking = false;
219
219
 
@@ -248,7 +248,7 @@ module.exports = function (RED)
248
248
  isBlinking = false;
249
249
 
250
250
  // Make sure it is bool
251
- msg.payload = !!msg.payload;
251
+ msg.payload = helper.toBool(msg.payload);
252
252
 
253
253
  // No alarm change, do nothing
254
254
  if (node_settings.alarm_active == msg.payload)
@@ -321,7 +321,7 @@ module.exports = function (RED)
321
321
  case "toggle":
322
322
  default:
323
323
  // If button is released, don't handle this message
324
- if (msg.payload === false)
324
+ if (helper.toBool(msg.payload) === false)
325
325
  return;
326
326
 
327
327
  if (mode == "BOOL")
package/logic/logic.js CHANGED
@@ -91,9 +91,9 @@ module.exports = function (RED)
91
91
  }
92
92
 
93
93
  if (inverts.includes(real_topic_number))
94
- node_settings.input_states[real_topic_number - 1] = !msg.payload;
94
+ node_settings.input_states[real_topic_number - 1] = !helper.toBool(msg.payload);
95
95
  else
96
- node_settings.input_states[real_topic_number - 1] = !!msg.payload; // !! => Convert to boolean
96
+ node_settings.input_states[real_topic_number - 1] = helper.toBool(msg.payload);
97
97
 
98
98
  let result = getResult();
99
99
 
@@ -70,7 +70,7 @@ module.exports = function (RED)
70
70
  */
71
71
  let handleTopic = msg =>
72
72
  {
73
- if (msg.payload)
73
+ if (helper.toBool(msg.payload))
74
74
  {
75
75
  on_time = Date.now();
76
76
  startAutoLongPress();
@@ -61,7 +61,10 @@
61
61
  </tr>
62
62
  <tr>
63
63
  <td><code>current_temperature</code></td>
64
- <td>Setzt die aktuelle Temperatur auf <code>msg.payload</code> °C.</td>
64
+ <td>
65
+ Setzt die aktuelle Temperatur auf <code>msg.payload</code> °C.<br/>
66
+ Wird die eingestellte kritische Temperatur überschritten, schließt sich der Mischer sofort um den eingestellten prozentualen Wert.
67
+ </td>
65
68
  </tr>
66
69
  <tr>
67
70
  <td><code>alarm</code></td>
@@ -25,6 +25,8 @@
25
25
  "max_change_percent": "Max Änderung",
26
26
  "max_change_temp_difference": "Temperatur für max Änderung",
27
27
  "min_change_time": "Min Änderungsdauer",
28
+ "critical_temp_max": "Max kritische Temperatur",
29
+ "crit_temp_change_percent": "Änderung krit. Temperatur (%)",
28
30
  "controlled_by_central": "Dieser Baustein wird von folgenden Zentralbausteinen gesteuert:"
29
31
  }
30
32
  }
@@ -43,7 +43,10 @@
43
43
  </tr>
44
44
  <tr>
45
45
  <td><code>setpoint</code></td>
46
- <td>Overrides the setpoint with <code>msg.payload</code> °C.</td>
46
+ <td>
47
+ Overrides the setpoint with <code>msg.payload</code> °C.<br/>
48
+ If the configured critical temperature is exceeded, the mixer immediately moves toward the closed position by the configured percentage.
49
+ </td>
47
50
  </tr>
48
51
  <tr>
49
52
  <td><code>off_mode</code></td>
@@ -24,6 +24,8 @@
24
24
  "precision": "Precision",
25
25
  "max_change_percent": "Max change",
26
26
  "max_change_temp_difference": "Temp at max change",
27
+ "critical_temp_max": "Critical temperature (max)",
28
+ "crit_temp_change_percent": "Critical temp change (%)",
27
29
  "min_change_time": "Min change duration",
28
30
  "controlled_by_central": "This block is controlled by the following central blocks:"
29
31
  }
@@ -152,6 +152,8 @@
152
152
  min_change_time: { value: 100 },
153
153
  links: { value: [], type: "smart_central-control[]" },
154
154
  alarm_action: { value: "NOTHING" }, // NOTHING | OPEN | CLOSE
155
+ critical_temp_max: { value: 100 },
156
+ crit_temp_change_percent: { value: 0 },
155
157
  config_change_date: { value: "" },
156
158
  },
157
159
  inputs: 1,
@@ -344,6 +346,43 @@
344
346
  if (value !== this.value) $(this).spinner("value", value);
345
347
  },
346
348
  });
349
+
350
+ // New fields for critical temperature settings
351
+ $("#node-input-critical_temp_max")
352
+ .css("max-width", "4rem")
353
+ .spinner({
354
+ min: 0,
355
+ max: 100,
356
+ step: 1,
357
+ change: function (event, ui)
358
+ {
359
+ var value = parseFloat(this.value);
360
+ value = isNaN(value) ? 0.0 : value;
361
+ value = Math.max(value, parseFloat($(this).attr("aria-valuemin")));
362
+ value = Math.min(value, parseFloat($(this).attr("aria-valuemax")));
363
+ if (value !== this.value) $(this).spinner("value", value);
364
+ },
365
+ });
366
+ if ($("#node-input-critical_temp_max").val() === "")
367
+ $("#node-input-critical_temp_max").val("100");
368
+
369
+ $("#node-input-crit_temp_change_percent")
370
+ .css("max-width", "4rem")
371
+ .spinner({
372
+ min: 0,
373
+ max: 100,
374
+ step: 1,
375
+ change: function (event, ui)
376
+ {
377
+ var value = parseFloat(this.value);
378
+ value = isNaN(value) ? 0.0 : value;
379
+ value = Math.max(value, parseFloat($(this).attr("aria-valuemin")));
380
+ value = Math.min(value, parseFloat($(this).attr("aria-valuemax")));
381
+ if (value !== this.value) $(this).spinner("value", value);
382
+ },
383
+ });
384
+ if ($("#node-input-crit_temp_change_percent").val() === "")
385
+ $("#node-input-crit_temp_change_percent").val("0");
347
386
  },
348
387
  onadd: function ()
349
388
  {
@@ -412,6 +451,14 @@
412
451
  <label for="node-input-min_change_time" style="width: 250px;"><i class="fa fa-sliders"></i> <span data-i18n="mixing-valve.ui.min_change_time"></span></label>
413
452
  <input id="node-input-min_change_time" value="0" /> ms
414
453
  </div>
454
+ <div class="form-row">
455
+ <label for="node-input-critical_temp_max" style="width: 250px;"><i class="fa fa-thermometer-half"></i> <span data-i18n="mixing-valve.ui.critical_temp_max"></span></label>
456
+ <input id="node-input-critical_temp_max" value="100"/> °C
457
+ </div>
458
+ <div class="form-row">
459
+ <label for="node-input-crit_temp_change_percent" style="width: 250px;"><i class="fa fa-percent"></i> <span data-i18n="mixing-valve.ui.crit_temp_change_percent"></span></label>
460
+ <input id="node-input-crit_temp_change_percent" value="0"/> %
461
+ </div>
415
462
  <hr/>
416
463
  <span><i class="fa fa-link"></i> <span data-i18n="mixing-valve.ui.controlled_by_central"></span></span>
417
464
  <div class="form-row node-input-link-row node-input-link-rows"></div>
@@ -38,6 +38,8 @@ module.exports = function (RED)
38
38
  last_enabled_sended: null,
39
39
  alarm_active: false,
40
40
  config_change_date: config.config_change_date,
41
+ critical_temp_max: config.critical_temp_max,
42
+ crit_temp_change_percent: config.crit_temp_change_percent,
41
43
  }, smart_context.get(node.id, config.config_change_date));
42
44
 
43
45
  // Ensure correct types
@@ -46,6 +48,22 @@ module.exports = function (RED)
46
48
  if (isNaN(node_settings.setpoint) || !isFinite(node_settings.setpoint))
47
49
  node_settings.setpoint = 20;
48
50
 
51
+ if (!node_settings.critical_temp_max)
52
+ {
53
+ node_settings.critical_temp_max = null;
54
+ }
55
+ else
56
+ {
57
+ node_settings.critical_temp_max = parseFloat(node_settings.critical_temp_max);
58
+ if (isNaN(node_settings.critical_temp_max) || !isFinite(node_settings.critical_temp_max))
59
+ node_settings.critical_temp_max = null;
60
+ }
61
+
62
+ node_settings.crit_temp_change_percent = parseFloat(node_settings.crit_temp_change_percent);
63
+ if (isNaN(node_settings.crit_temp_change_percent) || !isFinite(node_settings.crit_temp_change_percent))
64
+ node_settings.crit_temp_change_percent = 0;
65
+
66
+
49
67
  // Remove old settings
50
68
  delete node_settings.precision;
51
69
  delete node_settings.max_change_percent;
@@ -157,11 +175,11 @@ module.exports = function (RED)
157
175
  if (real_topic == "set_inverted")
158
176
  {
159
177
  real_topic = "set";
160
- msg.payload = !msg.payload;
178
+ msg.payload = !helper.toBool(msg.payload);
161
179
  }
162
180
 
163
181
  if (real_topic == "set")
164
- real_topic = (!!msg.payload) ? "enable" : "disable";
182
+ real_topic = helper.toBool(msg.payload) ? "enable" : "disable";
165
183
 
166
184
  switch (real_topic)
167
185
  {
@@ -271,6 +289,16 @@ module.exports = function (RED)
271
289
  return;
272
290
  }
273
291
  node_settings.current_temperature = new_temp;
292
+
293
+ // check if critical temperature is set and exceeded
294
+ if (node_settings.critical_temp_max !== null && node_settings.crit_temp_change_percent > 0 && node_settings.current_temperature > node_settings.critical_temp_max)
295
+ {
296
+ force_position = node_settings.last_position - node_settings.crit_temp_change_percent;
297
+ force_position = Math.max(force_position, 0);
298
+
299
+ stopSampling();
300
+ startSampling();
301
+ }
274
302
  break;
275
303
 
276
304
  case "calibrate":
@@ -279,7 +307,7 @@ module.exports = function (RED)
279
307
 
280
308
  case "alarm":
281
309
  // Make sure it is bool
282
- msg.payload = !!msg.payload;
310
+ msg.payload = helper.toBool(msg.payload);
283
311
 
284
312
  // No alarm change -> nothing to do
285
313
  if (node_settings.alarm_active == msg.payload)
@@ -84,8 +84,10 @@ module.exports = function (RED)
84
84
  if (msg.payload == null)
85
85
  return;
86
86
 
87
- if (typeof msg.payload === "boolean")
87
+ if (typeof msg.payload === "boolean" || msg.payload === "on" || msg.payload === "off")
88
88
  {
89
+ msg.payload = msg.payloadhelper.toBool(msg.payload);
90
+
89
91
  // Syntax: set_mode#MODE_NAME and payload is boolean
90
92
  // If payload is true, set to MODE_NAME
91
93
  // if payload is false, set to default mode
@@ -55,7 +55,7 @@ module.exports = function (RED)
55
55
  // ###############
56
56
  node.on("input", function (msg)
57
57
  {
58
- if (msg.payload)
58
+ if (helper.toBool(msg.payload))
59
59
  startTimeout();
60
60
  });
61
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-nodes",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "Controls light, shutters and more. Includes common used logic and statistic nodes to control your home.",
5
5
  "keywords": [
6
6
  "node-red",
package/scene/scene.js CHANGED
@@ -123,7 +123,7 @@ module.exports = function (RED)
123
123
 
124
124
  case "status":
125
125
  // Make sure it is bool
126
- msg.payload = !!msg.payload;
126
+ msg.payload = helper.toBool(msg.payload);
127
127
  node_settings.last_values[number] = msg.payload;
128
128
 
129
129
  notifyCentral();
@@ -145,7 +145,7 @@ module.exports = function (RED)
145
145
 
146
146
  case "set":
147
147
  // Make sure it is bool
148
- msg.payload = !!msg.payload;
148
+ msg.payload = helper.toBool(msg.payload);
149
149
  node_settings.last_values = new Array(config.outputs).fill(msg.payload);
150
150
 
151
151
  // This happens because of splitting by _ for scenes
@@ -155,7 +155,7 @@ module.exports = function (RED)
155
155
 
156
156
  case "scene":
157
157
  // Skip if button is released;
158
- if (msg.payload === false)
158
+ if (helper.toBool(msg.payload) === false)
159
159
  return;
160
160
 
161
161
  if (typeof scenes === "undefined")
@@ -204,7 +204,7 @@ module.exports = function (RED)
204
204
 
205
205
  case "toggle":
206
206
  // Skip if button is released;
207
- if (msg.payload === false)
207
+ if (helper.toBool(msg.payload) === false)
208
208
  return;
209
209
 
210
210
  node_settings.last_values = new Array(config.outputs).fill(currentScene == 0);
@@ -35,4 +35,7 @@
35
35
  </tbody>
36
36
  </table>
37
37
  </p>
38
+ <p>
39
+ Hinweis: Sie können in der Node-Konfiguration "UTC Zeit verwenden" aktivieren, um UTC statt Lokalzeit zu nutzen. Der Status zeigt an, wenn UTC aktiv ist.
40
+ </p>
38
41
  </script>
@@ -15,7 +15,8 @@
15
15
  "thursday": "Donnerstag",
16
16
  "friday": "Freitag",
17
17
  "saturday": "Samstag",
18
- "sunday": "Sonntag"
18
+ "sunday": "Sonntag",
19
+ "use_utc": "UTC Zeit verwenden"
19
20
  }
20
21
  }
21
22
  }
@@ -35,4 +35,7 @@
35
35
  </tbody>
36
36
  </table>
37
37
  </p>
38
+ <p>
39
+ Note: You can choose to use UTC instead of local time by enabling "Use UTC time" in the node config. Status texts will indicate when UTC is active.
40
+ </p>
38
41
  </script>
@@ -15,7 +15,8 @@
15
15
  "thursday": "Thursday",
16
16
  "friday": "Friday",
17
17
  "saturday": "Saturday",
18
- "sunday": "Sunday"
18
+ "sunday": "Sunday",
19
+ "use_utc": "Use UTC time"
19
20
  }
20
21
  }
21
22
  }
@@ -37,6 +37,7 @@
37
37
  defaults: {
38
38
  name: { value: "" },
39
39
  enabled: { value: false },
40
+ use_utc: { value: false },
40
41
  schedules: {
41
42
  value: [],
42
43
  validate: function (v)
@@ -122,6 +123,7 @@
122
123
  $("#resend_on_start_row").hide();
123
124
  });
124
125
  $("#node-input-save_state").trigger("change");
126
+ // no special behaviour required for use_utc checkbox
125
127
 
126
128
  /**
127
129
  * prepare schedule
@@ -270,6 +272,7 @@
270
272
  oneditsave: function ()
271
273
  {
272
274
  let node = this;
275
+ node.use_utc = !!$("#node-input-use_utc").prop("checked");
273
276
 
274
277
  node.schedules = [];
275
278
 
@@ -314,6 +317,10 @@
314
317
  <input type="checkbox" id="node-input-enabled" style="width: 20px;" />
315
318
  <label for="node-input-enabled" style="width: 200px;" data-i18n="scheduler.ui.scheduler_enabled"></label>
316
319
  </div>
320
+ <div class="form-row">
321
+ <input type="checkbox" id="node-input-use_utc" style="width: 20px;" />
322
+ <label for="node-input-use_utc" style="width: calc(100% - 30px);" data-i18n="scheduler.ui.use_utc"></label>
323
+ </div>
317
324
  <div class="form-row" style="margin-bottom: 2px;">
318
325
  <p class="text-center"><i class="fa fa-list"></i> <strong data-i18n="scheduler.ui.schedules"></strong></p>
319
326
  </div>
@@ -6,6 +6,8 @@ module.exports = function (RED)
6
6
  {
7
7
  const node = this;
8
8
  RED.nodes.createNode(node, config);
9
+ // support use of UTC time (global option)
10
+ const useUtc = !!config.use_utc;
9
11
 
10
12
 
11
13
  // ###################
@@ -109,11 +111,11 @@ module.exports = function (RED)
109
111
  if (real_topic == "set_inverted")
110
112
  {
111
113
  real_topic = "set";
112
- msg.payload = !msg.payload;
114
+ msg.payload = !helper.toBool(msg.payload);
113
115
  }
114
116
 
115
117
  if (real_topic == "set")
116
- real_topic = (!!msg.payload) ? "enable" : "disable";
118
+ real_topic = helper.toBool(msg.payload) ? "enable" : "disable";
117
119
 
118
120
  switch (real_topic)
119
121
  {
@@ -181,46 +183,70 @@ module.exports = function (RED)
181
183
  return null;
182
184
 
183
185
  let now = new Date();
186
+
187
+ // use UTC or local values
188
+ const nowHour = useUtc ? now.getUTCHours() : now.getHours();
189
+ const nowMinute = useUtc ? now.getUTCMinutes() : now.getMinutes();
190
+ const nowSecond = useUtc ? now.getUTCSeconds() : now.getSeconds();
191
+ const nowDay = useUtc ? now.getUTCDay() : now.getDay();
192
+
184
193
  let findNextDay = false;
185
194
 
186
195
  // check if the time has already passed today
187
- if (now.getHours() > schedule.hour)
196
+ if (nowHour > schedule.hour)
188
197
  {
189
198
  findNextDay = true;
190
199
  }
191
- else if (now.getHours() == schedule.hour)
200
+ else if (nowHour == schedule.hour)
192
201
  {
193
- if (now.getMinutes() > schedule.minute)
202
+ if (nowMinute > schedule.minute)
194
203
  {
195
204
  findNextDay = true;
196
205
  }
197
- else if (now.getMinutes() == schedule.minute)
206
+ else if (nowMinute == schedule.minute)
198
207
  {
199
- findNextDay = now.getSeconds() >= schedule.second;
208
+ findNextDay = nowSecond >= schedule.second;
200
209
  }
201
210
  }
202
211
 
203
212
  // find next day when the event should be raised
204
- let possibleDay = schedule.days.filter(d => findNextDay ? d > now.getDay() : d >= now.getDay());
213
+ let possibleDay = schedule.days.filter(d => findNextDay ? d > nowDay : d >= nowDay);
205
214
  if (possibleDay.length == 0)
206
215
  possibleDay = Math.min(...schedule.days);
207
216
  else
208
217
  possibleDay = Math.min(...possibleDay);
209
218
 
210
- let nextEvent = new Date(
211
- now.getFullYear(),
212
- now.getMonth(),
213
- now.getDate() + (
214
- findNextDay ?
215
- possibleDay <= now.getDay() ? 7 - now.getDay() + possibleDay : possibleDay - now.getDay()
216
- :
217
- possibleDay < now.getDay() ? 7 - now.getDay() + possibleDay : possibleDay - now.getDay()
218
- ),
219
- schedule.hour,
220
- schedule.minute,
221
- schedule.second
219
+ const dayOffset = (
220
+ findNextDay ?
221
+ (possibleDay <= nowDay ? 7 - nowDay + possibleDay : possibleDay - nowDay)
222
+ :
223
+ (possibleDay < nowDay ? 7 - nowDay + possibleDay : possibleDay - nowDay)
222
224
  );
223
225
 
226
+ let nextEvent;
227
+ if (useUtc)
228
+ {
229
+ nextEvent = new Date(Date.UTC(
230
+ now.getUTCFullYear(),
231
+ now.getUTCMonth(),
232
+ now.getUTCDate() + dayOffset,
233
+ schedule.hour,
234
+ schedule.minute,
235
+ schedule.second
236
+ ));
237
+ }
238
+ else
239
+ {
240
+ nextEvent = new Date(
241
+ now.getFullYear(),
242
+ now.getMonth(),
243
+ now.getDate() + dayOffset,
244
+ schedule.hour,
245
+ schedule.minute,
246
+ schedule.second
247
+ );
248
+ }
249
+
224
250
  // helper.log(node, {
225
251
  // i,
226
252
  // findNextDay,
@@ -256,7 +282,7 @@ module.exports = function (RED)
256
282
  node.status({
257
283
  fill: "red",
258
284
  shape: "dot",
259
- text: helper.getCurrentTimeForStatus() + ": Scheduler disabled"
285
+ text: helper.getCurrentTimeForStatus() + (useUtc ? " (UTC): " : ": ") + "Scheduler disabled"
260
286
  });
261
287
  }
262
288
  else if (nextEvent == null)
@@ -264,7 +290,7 @@ module.exports = function (RED)
264
290
  node.status({
265
291
  fill: "red",
266
292
  shape: "dot",
267
- text: helper.getCurrentTimeForStatus() + ": No events planned"
293
+ text: helper.getCurrentTimeForStatus() + (useUtc ? " (UTC): " : ": ") + "No events planned"
268
294
  });
269
295
  }
270
296
  else
@@ -276,7 +302,7 @@ module.exports = function (RED)
276
302
  node.status({
277
303
  fill: "yellow",
278
304
  shape: "dot",
279
- text: helper.getCurrentTimeForStatus() + ": Wait " + helper.formatMsToStatus(time, "until") + " to raise next event"
305
+ text: helper.getCurrentTimeForStatus() + (useUtc ? " (UTC): " : ": ") + "Wait " + helper.formatMsToStatus(time, "until") + " to raise next event"
280
306
  });
281
307
  }
282
308
  }
@@ -107,7 +107,7 @@ module.exports = function (RED)
107
107
  // Correct next topic to avoid handling up_stop, down_stop or toggle separately.
108
108
  if (real_topic == "short_up_down")
109
109
  {
110
- real_topic = msg.payload ? "down" : "up";
110
+ real_topic = helper.toBool(msg.payload) ? "down" : "up";
111
111
  if (msg.time_on == null)
112
112
  msg.time_on = short_time_on_ms;
113
113
  }
@@ -150,7 +150,7 @@ module.exports = function (RED)
150
150
 
151
151
  case "up_down":
152
152
  // This is only used to track starting of the shutter
153
- node_settings.last_direction_up = !msg.payload;
153
+ node_settings.last_direction_up = !helper.toBool(msg.payload);
154
154
  is_running = true;
155
155
  return;
156
156
 
@@ -125,13 +125,13 @@ module.exports = function (RED)
125
125
  let real_topic = helper.getRealTopic(msg.topic, "toggle", ["up", "up_stop", "down", "down_stop", "stop", "toggle", "up_down", "position", "alarm"]);
126
126
 
127
127
  // skip if button is released
128
- if (msg.payload === false && ["up", "up_stop", "down", "down_stop", "stop", "toggle"].includes(real_topic))
128
+ if (helper.toBool(msg.payload) === false && ["up", "up_stop", "down", "down_stop", "stop", "toggle"].includes(real_topic))
129
129
  return;
130
130
 
131
131
  // Convert up_down from HA UI to next command
132
132
  if (real_topic == "up_down")
133
133
  {
134
- if (msg.payload)
134
+ if (helper.toBool(msg.payload))
135
135
  real_topic = "down";
136
136
  else
137
137
  real_topic = "up";
@@ -192,13 +192,13 @@ module.exports = function (RED)
192
192
  case "position":
193
193
  startAction(ACTION_POSITION, msg.payload ?? null);
194
194
  break;
195
-
195
+
196
196
  case "status_up":
197
197
  // TODO: if status changed from false to true, start time measurement to calculate position
198
198
  // if status changed from true to false, stop time measurement and save position
199
199
  // if alarm is active, check if current action is allowed and stop if not
200
200
  break;
201
-
201
+
202
202
  case "status_down":
203
203
  // TODO: if status changed from false to true, start time measurement to calculate position
204
204
  // if status changed from true to false, stop time measurement and save position
@@ -207,7 +207,7 @@ module.exports = function (RED)
207
207
 
208
208
  case "alarm":
209
209
  // Make sure it is bool
210
- msg.payload = !!msg.payload;
210
+ msg.payload = helper.toBool(msg.payload);
211
211
 
212
212
  // No alarm change, do nothing
213
213
  if (node_settings.alarm_active == msg.payload)
package/smart_helper.js CHANGED
@@ -1,5 +1,23 @@
1
1
  module.exports = {
2
2
 
3
+ toBool(value)
4
+ {
5
+ if (typeof value == "boolean")
6
+ return value;
7
+
8
+ if (typeof value == "string")
9
+ {
10
+ value = value.toLowerCase();
11
+ if (value == "true" || value == "yes" || value == "on")
12
+ return true;
13
+
14
+ if (value == "false" || value == "no" || value == "off")
15
+ return false;
16
+ }
17
+
18
+ return null;
19
+ },
20
+
3
21
  /**
4
22
  * This functions converts a value into the given type.
5
23
  * If a type is unknown the NodeRed function is used for conversation.