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 +10 -0
- package/README.md +68 -67
- package/SmartNodesContext.json +1 -1
- package/forwarder/forwarder.html +5 -0
- package/forwarder/forwarder.js +12 -8
- package/forwarder/locales/de-DE/forwarder.html +2 -2
- package/forwarder/locales/de-DE/forwarder.json +1 -0
- package/forwarder/locales/en-US/forwarder.html +2 -2
- package/forwarder/locales/en-US/forwarder.json +3 -2
- package/light/light.js +14 -14
- package/logic/logic.js +2 -2
- package/long-press/long-press.js +1 -1
- package/mixing-valve/locales/de-DE/mixing-valve.html +4 -1
- package/mixing-valve/locales/de-DE/mixing-valve.json +2 -0
- package/mixing-valve/locales/en-US/mixing-valve.html +4 -1
- package/mixing-valve/locales/en-US/mixing-valve.json +2 -0
- package/mixing-valve/mixing-valve.html +47 -0
- package/mixing-valve/mixing-valve.js +31 -3
- package/mode-selector/mode-selector.js +3 -1
- package/multi-press/multi-press.js +1 -1
- package/package.json +1 -1
- package/scene/scene.js +4 -4
- package/scheduler/locales/de-DE/scheduler.html +3 -0
- package/scheduler/locales/de-DE/scheduler.json +2 -1
- package/scheduler/locales/en-US/scheduler.html +3 -0
- package/scheduler/locales/en-US/scheduler.json +2 -1
- package/scheduler/scheduler.html +7 -0
- package/scheduler/scheduler.js +49 -23
- package/shutter/shutter.js +2 -2
- package/shutter-complex/shutter-complex.js +5 -5
- package/smart_helper.js +18 -0
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
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
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
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
75
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
103
|
-
-
|
|
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
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
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
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
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
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
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
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
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
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
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
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
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
|
-
-
|
|
178
|
-
-
|
|
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
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
202
|
-
-
|
|
203
|
-
-
|
|
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
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
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
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
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
|
-
-
|
|
232
|
-
-
|
|
233
|
-
-
|
|
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.
|
package/SmartNodesContext.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"idn1":{"
|
|
1
|
+
{"idn1":{"last_position":0,"last_direction_up":false}}
|
package/forwarder/forwarder.html
CHANGED
|
@@ -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">
|
package/forwarder/forwarder.js
CHANGED
|
@@ -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
|
|
89
|
-
real_topic =
|
|
89
|
+
if (real_topic == "set_forwarder_state")
|
|
90
|
+
real_topic = "set_forwarder";
|
|
90
91
|
|
|
91
|
-
if (real_topic == "
|
|
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 = "
|
|
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 == "
|
|
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 == "
|
|
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 &&
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
96
|
+
node_settings.input_states[real_topic_number - 1] = helper.toBool(msg.payload);
|
|
97
97
|
|
|
98
98
|
let result = getResult();
|
|
99
99
|
|
package/long-press/long-press.js
CHANGED
|
@@ -61,7 +61,10 @@
|
|
|
61
61
|
</tr>
|
|
62
62
|
<tr>
|
|
63
63
|
<td><code>current_temperature</code></td>
|
|
64
|
-
<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>
|
|
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 = (
|
|
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 =
|
|
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
|
package/package.json
CHANGED
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 =
|
|
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 =
|
|
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);
|
package/scheduler/scheduler.html
CHANGED
|
@@ -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>
|
package/scheduler/scheduler.js
CHANGED
|
@@ -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 = (
|
|
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 (
|
|
196
|
+
if (nowHour > schedule.hour)
|
|
188
197
|
{
|
|
189
198
|
findNextDay = true;
|
|
190
199
|
}
|
|
191
|
-
else if (
|
|
200
|
+
else if (nowHour == schedule.hour)
|
|
192
201
|
{
|
|
193
|
-
if (
|
|
202
|
+
if (nowMinute > schedule.minute)
|
|
194
203
|
{
|
|
195
204
|
findNextDay = true;
|
|
196
205
|
}
|
|
197
|
-
else if (
|
|
206
|
+
else if (nowMinute == schedule.minute)
|
|
198
207
|
{
|
|
199
|
-
findNextDay =
|
|
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 >
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
}
|
package/shutter/shutter.js
CHANGED
|
@@ -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 =
|
|
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.
|