node-red-contrib-eztimer
Advanced tools
Comparing version 1.0.14 to 1.1.0
118
index.js
@@ -90,2 +90,3 @@ const util = require('util'); | ||
if (msg.payload === 'on') { | ||
// Sends the on event without impacting the scheduled event | ||
handled = true; | ||
@@ -98,8 +99,17 @@ send(events.on, true); | ||
handled = true; | ||
send(events.off); | ||
schedule(events.off); | ||
send(events.off, true); | ||
if (!isSuspended()) schedule(events.off); | ||
status(events.off, true); | ||
} else if (msg.payload === 'trigger') { | ||
// Sends the trigger/on event without impact the scheduled event | ||
handled = true; | ||
send(events.on); | ||
} else if (msg.payload === 'cancel' && config.timerType == '1') { | ||
// Cancels the current timer without sending the off event | ||
handled = true; | ||
if (!isSuspended()) { | ||
schedule(events.on); | ||
schedule(events.off); | ||
} | ||
status(events.off); | ||
} else if (msg.payload === 'info') { | ||
@@ -152,3 +162,3 @@ handled = true; | ||
obj[prop] = typeConverter(match[1]); | ||
requiresBootstrap = requiresBootstrap || previous !== obj[prop]; | ||
requiresBootstrap = requiresBootstrap || (previous !== obj[prop] && dynamicDuration(prop, obj[prop])); | ||
} | ||
@@ -169,3 +179,3 @@ }); | ||
obj[prop] = typeConverter(msg.payload[payloadName]); | ||
requiresBootstrap = requiresBootstrap || previous !== obj[prop]; | ||
requiresBootstrap = requiresBootstrap || (previous !== obj[prop] && dynamicDuration(prop, obj[prop])); | ||
} | ||
@@ -185,2 +195,56 @@ }); | ||
function dynamicDuration(property, duration) { | ||
// Return false if not a duration request | ||
if (!property == "duration") return true; | ||
if (state) { | ||
// Timer currently 'on' - parse time | ||
var secs = getSeconds(duration); | ||
var offTime = moment(events.on.last.moment).add(secs, 'seconds'); | ||
if (offTime.isBefore(node.now())) { | ||
// New time is before now - need to turn off and schedule | ||
node.log("Live duration change (" + duration + " => " + secs + "s) causes an off-time in the past, sending 'off' event."); | ||
send(events.off); | ||
schedule(events.off); | ||
status(events.off); | ||
} else { | ||
// New time is after now - just update the scheduled off event (and status) | ||
node.log("Live duration change (" + duration + " => " + secs + "s), rescheduling 'off' time to " + offTime.toString()); | ||
schedule(events.off, null, true); | ||
status(events.on); | ||
} | ||
} else { | ||
// Timer currently 'off', just re-schedule of off event (if an on event is scheduled) | ||
if (!isSuspended()) { | ||
schedule(events.off); | ||
status(events.off); | ||
} | ||
} | ||
// Return false to indicate no bootstrap required | ||
return false; | ||
} | ||
function getSeconds(val) { | ||
var secs = 0; | ||
// accept 00:00, 00:00:00, 45s, 45h,4m etc. | ||
var matches = new RegExp(/(?:(\d+)[h:\s,]+)?(?:(\d+)[m:\s,]+)?(?:(\d+)[s\s]*)?$/).exec(val); | ||
if (matches.length > 1) { | ||
if (matches[1] != null) { | ||
secs += parseInt(matches[1]) * 3600; // hours | ||
} | ||
if (matches[2] != null) { | ||
secs += parseInt(matches[2]) * 60; // minutes | ||
} | ||
if (matches[3] != null) { | ||
secs += parseInt(matches[3]) * 1; // seconds | ||
} | ||
} else { | ||
return 0; | ||
} | ||
return secs; | ||
} | ||
node.on('close', suspend); | ||
@@ -198,9 +262,13 @@ | ||
event.state = (eventName == 'on'); | ||
event.last = { moment: null }; | ||
event.callback = function() { | ||
// Send the event | ||
send(event); | ||
// Schedule the next event, if it's not a 'manual' timer | ||
if (events.on.type != '9') schedule(event, null, null); | ||
// Clear the event if it IS a 'manual' timer (as we don't know when the off event will be) | ||
if (events.on.type == '9') event.moment = undefined; | ||
if (events.on.type != '9' && !isSuspended()) { | ||
// Schedule the next event, if it's not a 'manual' timer | ||
schedule(event, null, null); | ||
} else { | ||
// Else just clear the event - we don't know when the off event will be | ||
event.moment = undefined; | ||
} | ||
// Update the status/icon | ||
@@ -215,2 +283,3 @@ status(event); | ||
var msg = {}; | ||
msg.tag = config.tag || 'eztimer'; | ||
var currPart = msg; | ||
@@ -232,3 +301,4 @@ var spl = event.property.split('.'); | ||
} | ||
node.send(msg); | ||
event.last.moment = node.now(); | ||
if (!event.suppressrepeats || state != event.state) node.send(msg); | ||
state = event.state; | ||
@@ -280,3 +350,3 @@ } | ||
// If a standard run, add a day | ||
if (!init) event.moment = event.moment.add(1, 'days'); | ||
if (!init && !m.isAfter(now)) event.moment = event.moment.add(1, 'days'); | ||
} else { | ||
@@ -286,13 +356,12 @@ event.moment = null; | ||
//node.warn('event \'' + event.name + '\' scheduled for ' + event.moment.format('DD/MM HH:mm:ss.SSS') + ' raw'); | ||
break; | ||
case '3': //Duration | ||
var matches = new RegExp(/(\d+):(\d+):(\d+)/).exec(event.duration); | ||
var secs = (matches[3] * 1) + (matches[2] * 60) + (matches[1] * 3600); | ||
var secs = getSeconds(event.duration); | ||
if (manual) { | ||
//event is manual - schedule assuming 'now' | ||
event.moment = moment(node.now()).add(secs, 'seconds'); | ||
//event is manual - schedule based on last 'on' event | ||
event.moment = moment(event.inverse.last.moment).add(secs, 'seconds'); | ||
} else { | ||
// event is auto - schedule based on current 'on' event | ||
event.moment = moment(event.inverse.moment).add(secs, 'seconds'); | ||
//node.warn('event \'' + event.name + '\' scheduled for ' + event.moment.format('DD/MM HH:mm:ss.SSS') + ' raw'); | ||
} | ||
@@ -303,3 +372,2 @@ | ||
event.moment.add(-1, 'day'); | ||
//node.warn('event \'' + event.name + '\' scheduled for ' + event.moment.format('DD/MM HH:mm:ss.SSS') + ' rollback'); | ||
} | ||
@@ -327,18 +395,13 @@ break; | ||
// Add a day if the moment is in the past | ||
//if (!isInitial || (isInitial && now.isAfter(event.moment))) { | ||
if (now.isAfter(event.moment)) { | ||
//node.warn('event \'' + event.name + '\' - \'' + now.format('DD/MM HH:mm:ss.SSS') + '\' is after \'' + event.moment.format('DD/MM HH:mm:ss.SSS') + '\', adding a day.'); | ||
event.moment.add(1, 'day'); | ||
//node.warn('event \'' + event.name + '\' scheduled for ' + event.moment.format('DD/MM HH:mm:ss.SSS') + ' past'); | ||
} | ||
// Adjust weekday if not selected | ||
while (!weekdays()[event.moment.isoWeekday() - 1]) { | ||
// Adjust weekday if not selected (and not manual) | ||
while (!manual && !weekdays()[event.moment.isoWeekday() - 1]) { | ||
event.moment.add(1, 'day'); | ||
//node.warn('event \'' + event.name + '\' scheduled for ' + event.moment.format('DD/MM HH:mm:ss.SSS') + ' skip'); | ||
} | ||
if (event.timeout) { | ||
clearTimeout(event.timeout); | ||
} | ||
// Clear any pending event | ||
if (event.timeout) clearTimeout(event.timeout); | ||
@@ -360,3 +423,4 @@ //console.log('schedule: ' + event.name + ' => ' + event.moment.toString()); | ||
if (event.inverse.moment && event.inverse.moment.isAfter(node.now())) { | ||
data.text = event.name + (manual ? ' manual' : ' auto') + (isSuspended() ? ' - scheduling suspended' : ` until ${event.inverse.moment.format(fmt)}`); | ||
//data.text = event.name + (manual ? ' manual' : ' auto') + (isSuspended() ? ' - scheduling suspended' : ` until ${event.inverse.moment.format(fmt)}`); | ||
data.text = event.name + (manual ? ' manual' : ' auto') + ` until ${event.inverse.moment.format(fmt)}`; | ||
} else { | ||
@@ -363,0 +427,0 @@ data.text = event.name + (manual ? ' manual' : ' auto') + (isSuspended() ? ' - scheduling suspended' : ``); |
{ | ||
"name": "node-red-contrib-eztimer", | ||
"version": "1.0.14", | ||
"version": "1.1.0", | ||
"description": "A simple-yet-flexible timer/scheduler for node-red", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -38,2 +38,16 @@ # eztimer | ||
As of `1.1.1` it is supported to specify timespans in numerous timespan formats: | ||
| input | interpretation | ||
|-------|----------------------------- | ||
| `"12:14"` | 12 hours and 14 minutes | ||
| `"12:14:24"` | 12 hours, 14 minutes and 24 seconds | ||
| `"23h 5m"` | 23 hours and 5 minutes | ||
| `"5m"` | 5 minutes | ||
| `"90s"` | 1 minute and 30 seconds (90 seconds) | ||
| `300` | 5 minutes (300 seconds) - integers are interpreted as seconds | ||
These are valid both at UI/config-time and at runtime using the `duration` payload. | ||
## Offsets | ||
@@ -53,2 +67,6 @@ | ||
## Suppression of repeating events | ||
In some circumstances it may be required to re-start the timer _without_ re-sending the `on` event - this setting achieves this. It's available for both `on` and `off` events, but disabled in `trigger` mode. | ||
## Inputs | ||
@@ -58,8 +76,9 @@ | ||
| msg.payload | Description | | ||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| `trigger` | Causes eztimer to emit the configured trigger event. | | ||
| `on` | Triggers manual on mode and causes eztimer to emit the configured `on` event. Manual mode is reset when the next `on` or `off` time is reached | | ||
| msg.payload | Description | | ||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| `trigger` | Causes eztimer to emit the configured trigger event. | | ||
| `on` | Triggers manual on mode and causes eztimer to emit the configured `on` event. Manual mode is reset when the next `on` or `off` time is reached | | ||
| `off` | Triggers manual off mode and causes eztimer to emit the configured `off` event. Manual mode is reset when the next `on` or `off` time is reached | | ||
| `info` | eztimer emits an object containing the `on` and `off` times in UTC format. It also contains the state which is either `on` or `off`. | | ||
| `info` | Eztimer emits an object containing the `on` and `off` times in UTC format. It also contains the state which is either `on` or `off`. | | ||
| `cancel` | Cancels the current run (if any) of the timer (_without_ emitting an `off` event). | | ||
@@ -88,3 +107,3 @@ # Programmatic Control | ||
| `msg.payload.offtime` | String value representing time of day (HH:mm[:ss]) | | ||
| `msg.payload.duration` | String value representing a timespan (HH:mm[:ss]) | | ||
| `msg.payload.duration` | String value representing a timespan (see [Times](##Times)) | | ||
| `msg.payload.offtopic` | String value emitted as the topic for the off event | | ||
@@ -104,2 +123,11 @@ | `msg.payload.offvalue` | Update output value for off event (must be same as configured type) | | ||
## 1.1.0 | ||
A few changes in this one - have had it running in test for a month or two and believe it to be stable - as always, log any bugs at [github issues](https://github.com/mrgadget/node-red-contrib-eztimer/issues) and I'll tend to them as soon as possible. | ||
* Added ability to change duration without resetting timer (enabling duration change while timer is on) | ||
* Made duration more 'friendly' - 00:00, 00:00:00, 34m, 23s, 34m 4s, and even a plain integer (interpreted as seconds) are all valid for duration now. | ||
* Added ability to _Suppress Repeated Events_. Meaning once an `on` event has been sent, repeatedly sending `on` inputs won't resend the `on` event (but it will restart the timer) | ||
* Added `cancel` as input command (to cancel any existing timer run without emitting `off` event). | ||
* Added `tag` config parameter. This value is emitted with all events to allow easy identification of specific eztimer node the message was emitted from. Defaults to `eztimer`. | ||
* Fixed issue where, when 1) using a duration event type _with_ 2) some days disabled _and_ 3) the timer is run manually on a disabled day, the `off` event would schedule on the next _available_ day (causing an abnormally long runtime). | ||
## 1.0.14 | ||
@@ -106,0 +134,0 @@ * Added `nadir` to ordered sun event array |
Sorry, the diff of this file is not supported yet
79389
849
169