node-red-contrib-stoptimer-varidelay-plus 0.5.4

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.
@@ -0,0 +1,230 @@
1
+ <!--
2
+ Modifications copyright (C) 2020 hamsando
3
+ Copyright jbardi
4
+
5
+ Modifications copyright (C) 2025 mchristegh
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ -->
19
+
20
+ <script type="text/javascript">
21
+ RED.nodes.registerType('stoptimer-varidelay',{
22
+ category: 'function',
23
+ color:"#869869",
24
+ defaults: {
25
+ duration: {value:"5",required:true,validate:RED.validators.typedInput("durationType")},
26
+ durationType: {value:'num'},
27
+ units: {value:"Second"},
28
+ payloadtype: {value:'num'},
29
+ payloadval: {value:"0"},
30
+ name: {value:""},
31
+ reporting: {value:"none"},
32
+ reportingformat: {value:"human"},
33
+ persist: {value:false},
34
+ ignoretimerpass: {value:false},
35
+ donotresettimer: {value:false}
36
+ },
37
+ inputs:1,
38
+ outputs:4,
39
+ icon: "stoptimer.png",
40
+ label: function() {
41
+ return this.name || this.duration + " " + this.units + " Timer";
42
+ },
43
+ labelStyle: function() {
44
+ return this.name?"node_label_italic":"";
45
+ },
46
+ outputLabels: ["Original Payload","Additional Payload","Time Remaining","Ignored Message"],
47
+ oneditprepare: function() {
48
+ $("#node-input-payloadval").typedInput({
49
+ default: 'num',
50
+ typeField: $("#node-input-payloadtype"),
51
+ types:['str','num','bool']
52
+ });
53
+
54
+ if (this.durationType == null) {
55
+ this.durationType = "num";
56
+ }
57
+
58
+ $("#node-input-durationType").val(this.durationType);
59
+
60
+ $("#node-input-duration").typedInput({
61
+ default: 'num',
62
+ typeField: $("#node-input-durationType"),
63
+ types:['num','env']
64
+ });
65
+
66
+ $("#node-input-duration").typedInput('type', this.durationType);
67
+
68
+ $("#node-input-reporting").val(this.reporting);
69
+ $("#node-input-reportingformat").val(this.reportingformat);
70
+ }
71
+ });
72
+ </script>
73
+
74
+ <script type="text/x-red" data-template-name="stoptimer-varidelay">
75
+ <div class="form-row">
76
+ <i class="fa fa-clock-o"></i>
77
+ <label for="node-input-duration"> Timer</label>
78
+ <input type="hidden" id="node-input-durationType">
79
+ <input type="text" id="node-input-duration" style="text-align:end; width:270px !important">
80
+ </div>
81
+ <div class="form-row">
82
+ <i class="fa fa-bars"></i>
83
+ <label for="node-input-units"> Units</label>
84
+ <select id="node-input-units">
85
+ <option value="Millisecond">Milliseconds</option>
86
+ <option value="Second">Seconds</option>
87
+ <option value="Minute">Minutes</option>
88
+ <option value="Hour">Hours</option>
89
+ </select>
90
+ </div>
91
+ <div class="form-row">
92
+ <i class="fa fa-heartbeat"></i>
93
+ <label for="node-input-reporting"> Reporting</label>
94
+ <select id="node-input-reporting">
95
+ <option value="none">Never</option>
96
+ <option value="every_second">Every Second</option>
97
+ <option value="last_minute_seconds">Every Minute, Last minute by seconds</option>
98
+ </select>
99
+ </div>
100
+ <div class="form-row">
101
+ <label for="node-input-reportingformat"> Reporting format</label>
102
+ <select id="node-input-reportingformat">
103
+ <option value="human">HH:MM:SS</option>
104
+ <option value="seconds">Seconds</option>
105
+ <option value="minutes">Minutes</option>
106
+ <option value="hours">Hours</option>
107
+ </select>
108
+ </div>
109
+ <div class="form-row">
110
+ <i class="fa fa-floppy-o"></i>
111
+ <label style="width:auto" for="node-input-persist"> Resume timer on deploy/restart</label>
112
+ <input type="checkbox" id="node-input-persist" style="display:inline-block; width:auto; vertical-align:top;">
113
+ </div>
114
+ <div class="form-row">
115
+ <i class="fa fa-recycle"></i>
116
+ <label style="width:auto" for="node-input-ignoretimerpass"> Ignore incoming _timerpass</label>
117
+ <input type="checkbox" id="node-input-ignoretimerpass" style="display:inline-block; width:auto; vertical-align:top;">
118
+ </div>
119
+ <div class="form-row">
120
+ <i class="fa fa-lock"></i>
121
+ <label style="width:auto" for="node-input-donotresettimer"> Do Not Reset Timer on Subsequent Incoming Message</label>
122
+ <input type="checkbox" id="node-input-donotresettimer" style="display:inline-block; width:auto; vertical-align:top;">
123
+ </div>
124
+ <div class="form-row">
125
+ <i class="fa fa-envelope"></i>
126
+ <label for="node-input-payloadtype"> 2nd Payload</label>
127
+ <input type="hidden" id="node-input-payloadtype">
128
+ <input style="width: 70%" type="text" id="node-input-payloadval">
129
+ </div>
130
+ <div class="form-row">
131
+ <i class="fa fa-tag"></i>
132
+ <label for="node-input-name"> Name</label>
133
+ <input type="text" id="node-input-name" placeholder="Name"></input>
134
+ </div>
135
+ </script>
136
+
137
+ <script type="text/x-red" data-help-name="stoptimer-varidelay">
138
+ <p><B>General usage</B><br>
139
+ Sends the <code>msg</code> through the first output after the set timer duration. If a new <code>msg</code> is received before the timer has ended, it will replace the existing <code>msg</code> and the timer will be restarted, unless the new <code>msg</code> has a <code>payload</code> of <code>stop</code>, <code>STOP</code>, <code>pause</code>, <code>PAUSE</code>, <code>resume</code>, or <code>RESUME</code>. The second output allows you to send an additional payload of a number, string or boolean. If the timer is stopped, the second and third outputs will automatically send a payload of <code>stopped</code>. The third output will send the time remaining as time ticks away.
140
+ <br><br>The status below the node as well as the third output can be configured via the <I>Reporting</I> field in the node to update at a frequency of:
141
+ <ul>
142
+ <li>Never (default)</li>
143
+ <li>Every Second</li>
144
+ <li>Every Minute, Last minute by seconds</li>
145
+ </ul>
146
+ The last option works as follows:
147
+ <ul>
148
+ <li>While there is more than 1 minute remaining, the timer will decrement every minute. At the 1 minute point, it will switch to reporting every second.</li>
149
+ <li>The exception to this rule is if your duration is not a minute increment. In that case, the first update will be for the partial minute, after which it will operate as noted above. (for example: 2.5 minutes will decrement to 2 minutes, then 1 minute, then every second down to zero)</li>
150
+ </ul>
151
+ </p>
152
+ <p>
153
+ The format of the reporting (and status) are defined by the "Reporting Format" option. The default is hh:mm:ss (string), but it can be configured to present that as the total number of seconds, minutes or hours (number) instead.
154
+ </p>
155
+ <p><B>Message properties on all outputs</B><br>
156
+ All output messages include the following additional properties:
157
+ <ul>
158
+ <li><code>msg.timerState</code> — the current state of the timer: <code>running</code>, <code>paused</code>, <code>stopped</code>, or <code>expired</code></li>
159
+ <li><code>msg.timerDuration</code> — the original timer duration in milliseconds</li>
160
+ <li><code>msg.elapsedTime</code> — the elapsed time in milliseconds (outputs 1, 2, and 3)</li>
161
+ <li><code>msg.remainingTime</code> — the remaining time in milliseconds (outputs 3 and 4)</li>
162
+ </ul>
163
+ </p>
164
+ <p><B>Overriding the node via incoming messages</B><br>
165
+ If the input contains <code>msg.delay</code>, then the delay will be <code>msg.delay</code> units of time, where the units are whatever the units are defaulted to in the node itself. In the absence of a <code>msg.delay</code>, or a value in <code>msg.delay</code> that cannot be converted to an int, the value configured within the node will be used. If the value of <code>msg.delay</code> is less than 0, then 0 is used.
166
+ </p>
167
+ <p>
168
+ If the input contains <code>msg.units</code>, with a value of "Milliseconds", "Seconds", "Minutes" or "Hours" then that will override what is defaulted in the node. In the absence of a <code>msg.units</code>, or an unknown string in <code>msg.units</code>, the units configured within the node will be used. In the case of an unknown string, a warning message will appear in the Debug logs.
169
+ </p>
170
+ <p><b>Pausing and Resuming</b><br>
171
+ The timer can be paused by sending a message with <code>msg.payload</code> of <code>pause</code> or <code>PAUSE</code>. While paused:
172
+ <ul>
173
+ <li>The countdown is frozen at the remaining time</li>
174
+ <li>The node status shows <code>Paused: HH:MM:SS</code> with a yellow indicator</li>
175
+ <li>Outputs 2 and 3 will send a payload of <code>paused</code></li>
176
+ <li>Any incoming message other than <code>resume</code>, <code>RESUME</code>, <code>stop</code>, or <code>STOP</code> will be routed to output 4</li>
177
+ <li>Sending another <code>pause</code> message while already paused will also be routed to output 4</li>
178
+ </ul>
179
+ The timer can be resumed by sending a message with <code>msg.payload</code> of <code>resume</code> or <code>RESUME</code>. On resume:
180
+ <ul>
181
+ <li>The countdown restarts from where it was frozen</li>
182
+ <li>Outputs 2 and 3 will send a payload of <code>resumed</code></li>
183
+ </ul>
184
+ If the <i>Resume timer on deploy/restart</i> option is enabled and the timer is paused when Node-RED restarts or the flow is redeployed, the timer will restore as paused at the same remaining time.
185
+ </p>
186
+ <p><b>Special Note on Milliseconds</b><br>
187
+ While you can set Milliseconds, I would not rely on the accuracy for anything critical. For the purposes of the node status and output 3, except in the case where Reporting is set to None, the milliseconds are not displayed or provided on the 3rd output as it wouldn't make sense based on the available reporting rates.
188
+ </p>
189
+ <p><b>Resume timer on deploy/restart</b><br>
190
+ This option is <b>DISABLED</b> by default. If you <i>ENABLE</i> it (check the checkbox) then if the stoptimer is running and you re-Deploy the flow, or restart Node-RED, then the timer will automatically restart itself where it should be. What does that mean? A couple of examples will help here.
191
+ <ul>
192
+ <li>If you had a 10 minute stoptimer running, with 6 minutes elapsed (ie: 4 minutes left) and you hit Deploy, normally the stoptimer would no longer be running, but if you have this feature enabled, the timer will continue running from the 6 minute mark (ie: counting down 4 more minutes and then trigger).</li>
193
+ <li>If you had a 10 minute stoptimer running, with 6 minutes elapsed (ie: 4 minutes left) and you <i>stopped</i> Node-RED for 2 minutes and then restarted it, normally the stoptimer would no longer be running, but if you have this feature enabled, the timer will continue running from the 8 minute mark (6 minutes from the original run + 2 minutes of Node-RED downtime) -- counting down 2 more minutes and then trigger.</li>
194
+ <li><b>Special Case</b> If on restart or re-Deploy, there is less than 3 seconds remaining on the stoptimer (or if the stoptimer should have elapsed already) then the stoptimer is set to a random amount between 3 and 8 seconds. This helps to ensure that anything else that needs to initialize before the stoptimer triggers has a chance to do so. It also helps so that if you happen to have a lot of timers, they don't all trigger at once and flood unsuspecting nodes/devices.</li>
195
+ </ul>
196
+ <br>
197
+ This persistence is <b>not</b> related to "Persistent Context" (the contextStorage option in <code>settings.js</code>). When the "Resume timer" option is enabled in the node, the node will store timer related information in a <code>stvd-timers</code> subdirectory of <i>userDir</i> (where <i>userDir</i> is defined in <code>settings.js</code>). If <i>userDir</i> is not explicitly defined, it defaults to a directory called <code>.node-red</code> in your home user directory. The files in this directory will be created/destroyed as needed by the node.
198
+ </p>
199
+ <p><b>What is <code>_timerpass</code></b><br>
200
+ <code>_timerpass</code> is a property added to messages exiting the 1st/top and 2nd/middle outputs of stoptimer.
201
+ <code>_timerpass</code> is set to <code>true</code> when the timer expires.
202
+ <br><br>
203
+ <b>What does <code>_timerpass</code> do?</b><br>
204
+ If stoptimer has at any point been stopped using message.payload=stop (or STOP) AND <br>
205
+ If stoptimer has not received a message with _timerpass not set since that time THEN <br>
206
+ any incoming message that has the _timerpass=true property will die within stoptimer with no output.
207
+ <br><br>
208
+ This can be problematic if you want to chain multiple stoptimers together. It is not insurmountable, but it can be irritating.
209
+ <br><br>
210
+ <b>Why does this behavior exist?</b><br>
211
+ It is a legacy thing, it was part of the original stoptimer whose code I forked. Not sure what exactly the original intent was, but I'm sure there is some rationale.
212
+ <br><br>
213
+ <b>How does Stoptimer-Varidelay handle this?</b><br>
214
+ <i>Ignore Timerpass</i> in the node config dialog. If enabled in a given stoptimer-varidelay node, it will ignore the presence of the <code>_timerpass</code> property on an incoming message and will process the incoming message as it does every other message.
215
+ By default, this option is not enabled in order to preserve compatibility with any existing flows. Note that you may need to refresh the web UI after updating the node in order to see the new "ignore timerpass" option.
216
+ </p>
217
+ <p><b>Do Not Reset Timer on Subsequent Incoming Message</b><br>
218
+ This option is <b>DISABLED</b> by default. If you <i>ENABLE</i> it (check the checkbox) then while the timer is running, any subsequent incoming messages (other than <code>stop</code>, <code>STOP</code>, <code>pause</code>, <code>PAUSE</code>, <code>resume</code>, or <code>RESUME</code>) will be ignored and the timer will continue running undisturbed. The first message always starts the timer normally.
219
+ <br><br>
220
+ When a message is ignored, it will be sent to the 4th output with the original message content intact plus:
221
+ <ul>
222
+ <li><code>msg.remainingTime</code> — remaining time in milliseconds</li>
223
+ <li><code>msg.timerState</code> — current timer state</li>
224
+ </ul>
225
+ When this option is enabled, the node status will show:<br>
226
+ <code>Remaining: 00:04:32 | Ignored: 3, Last: Jun 27 14:22:05</code>
227
+ <br><br>
228
+ The ignored count and last ignored datetime will reset each time a new message starts the timer. They will remain visible on the status after the timer expires or is stopped, so you can see how many messages were ignored during that timer run.
229
+ </p>
230
+ </script>