node-red-contrib-eztimer
Advanced tools
Comparing version 1.2.4 to 1.2.5
193
index.js
@@ -55,3 +55,22 @@ const util = require('util'); | ||
var state = false; | ||
var resendInterval; | ||
var resendObj; | ||
RED.httpAdmin.get("/eztimer/getHaZones", RED.auth.needsPermission('serial.read'), function(req,res) { | ||
var ha = node.context().global.get('homeassistant'); | ||
var zones = []; | ||
for(element in ha.homeAssistant.states) { | ||
var zone = ha.homeAssistant.states[element]; | ||
if (element.substring(0,4) == 'zone') { | ||
var z = {"entity_id": zone.entity_id, "name": zone.attributes.friendly_name, "latitude": zone.attributes.latitude, "longitude": zone.attributes.longitude} | ||
if(z.entity_id.substring(0,9) == "zone.home") | ||
zones.unshift(z); | ||
else | ||
zones.push(z); | ||
} | ||
}; | ||
res.json(zones); | ||
}); | ||
switch (config.timerType) { | ||
@@ -73,2 +92,20 @@ case '1': | ||
// Init resend | ||
if (config.resend) { | ||
resendInterval = getSeconds(config.resendInterval); | ||
if (resendInterval > 0) { | ||
log(1, 'Re-send interval = ' + resendInterval + ' seconds.'); | ||
resend() | ||
} else { | ||
log(1, 'Re-send disabled.'); | ||
} | ||
} | ||
function resend() { | ||
resendObj = setTimeout(function() { | ||
if (events.last) send(events.last, true); | ||
resend() | ||
}, (resendInterval) * 1000); | ||
} | ||
function weekdays() { | ||
@@ -90,65 +127,6 @@ return [ | ||
if (msg.payload == null) { | ||
// Unsuppored input | ||
node.error("Null or undefined (msg.payload) input.") | ||
// Unsuppored input | ||
} else if (_.isString(msg.payload)) { | ||
if (msg.payload === 'on') { | ||
// Sends the on event without impacting the scheduled event | ||
handled = true; | ||
send(events.on, true); | ||
if (events.off && events.off.type == '3') schedule(events.off, null, true); // If 'off' is of type duration, schedule 'off' event. | ||
updateStatus(); | ||
} else if (msg.payload === 'off' && config.timerType == '1') { | ||
// Sends the off event, then re-schedules it | ||
handled = true; | ||
clearTimeout(events.off.timeout); | ||
events.off.moment = null; | ||
send(events.off, true); | ||
if (!isSuspended() && events.off.type != '3') schedule(events.off); | ||
updateStatus(); | ||
} else if (msg.payload === 'trigger') { | ||
// Sends the trigger/on event without impact the scheduled event | ||
handled = true; | ||
send(events.on); | ||
updateStatus(); | ||
} else if (msg.payload === 'cancel' && config.timerType == '1') { | ||
// Cancels the current timer without sending the off event | ||
handled = true; | ||
if (!isSuspended()) { | ||
schedule(events.on); | ||
clearTimeout(events.off.timeout); | ||
events.off.moment = null; | ||
if (!isSuspended() && events.off.type != '3') schedule(events.off); | ||
} | ||
updateStatus(); | ||
} else if (msg.payload === 'info') { | ||
handled = true; | ||
var info = getInfo(); | ||
// Info is now sent with every output - continue to send as payload for backward compatibiliy. | ||
node.send({ | ||
topic: 'info', | ||
info: info, | ||
tag: config.tag || 'eztimer', | ||
payload: info | ||
}); | ||
} else { | ||
if (msg.payload.indexOf('suspended') !== -1) { | ||
handled = true; | ||
const match = /.*suspended\s+(\S+)/.exec(msg.payload); | ||
const previous = config.suspended; | ||
config.suspended = toBoolean(match[1]); | ||
requiresBootstrap = requiresBootstrap || (previous !== config.suspended && config.sendEventsOnSuspend); | ||
} | ||
enumerateProgrammables(function(obj, prop, payloadName, typeConverter) { | ||
const match = new RegExp(`.*${payloadName}\\s+(\\S+)`).exec( | ||
msg.payload | ||
); | ||
if (match) { | ||
handled = true; | ||
const previous = obj[prop]; | ||
obj[prop] = typeConverter(match[1]); | ||
requiresBootstrap = requiresBootstrap || (previous !== obj[prop] && dynamicDuration(prop, obj[prop])); | ||
} | ||
}); | ||
node.error("Invalid string (msg.payload) input.") | ||
} | ||
handled = action('payload string', msg.payload) | ||
} else { | ||
@@ -161,2 +139,11 @@ if (msg.payload.hasOwnProperty('suspended')) { | ||
} | ||
if (msg.payload.hasOwnProperty('manual')) { | ||
handled = true; | ||
const previous = config.suspended; | ||
config.suspended = !!msg.payload.manual; | ||
requiresBootstrap = requiresBootstrap || previous !== config.suspended; | ||
} | ||
if (msg.payload.hasOwnProperty('action')) { | ||
handled = action('action', msg.payload.action); | ||
} | ||
enumerateProgrammables(function(obj, prop, payloadName, typeConverter) { | ||
@@ -170,3 +157,3 @@ if (msg.payload.hasOwnProperty(payloadName)) { | ||
}); | ||
node.error("Invalid object (msg.payload) input.") | ||
if (!handled) node.error("Invalid object (msg.payload) input.") | ||
} | ||
@@ -184,2 +171,76 @@ if (!handled) { | ||
function action(source, data) { | ||
var handled = false; | ||
if (data === 'on') { | ||
// Sends the on event without impacting the scheduled event | ||
handled = true; | ||
send(events.on, true); | ||
if (events.off && events.off.type == '3') schedule(events.off, null, true); // If 'off' is of type duration, schedule 'off' event. | ||
updateStatus(); | ||
} else if (data === 'off' && config.timerType == '1') { | ||
// Sends the off event, then re-schedules it | ||
handled = true; | ||
clearTimeout(events.off.timeout); | ||
events.off.moment = null; | ||
send(events.off, true); | ||
if (!isSuspended() && events.off.type != '3') schedule(events.off); | ||
updateStatus(); | ||
} else if (data === 'trigger') { | ||
// Sends the trigger/on event without impact the scheduled event | ||
handled = true; | ||
send(events.on); | ||
updateStatus(); | ||
} else if (data === 'cancel' && config.timerType == '1') { | ||
// Cancels the current timer without sending the off event | ||
handled = true; | ||
if (!isSuspended()) { | ||
schedule(events.on); | ||
clearTimeout(events.off.timeout); | ||
events.off.moment = null; | ||
if (!isSuspended() && events.off.type != '3') schedule(events.off); | ||
} | ||
updateStatus(); | ||
} else if (data === 'info') { | ||
handled = true; | ||
var info = getInfo(); | ||
// Info is now sent with every output - continue to send as payload for backward compatibiliy. | ||
node.send({ | ||
topic: 'info', | ||
info: info, | ||
tag: config.tag || 'eztimer', | ||
payload: info | ||
}); | ||
} else if (data === 'sync') { | ||
handled = true; | ||
sync(); | ||
} else { | ||
// if (data.indexOf('suspended') !== -1) { | ||
// handled = true; | ||
// const match = /.*suspended\s+(\S+)/.exec(data); | ||
// const previous = config.suspended; | ||
// config.suspended = toBoolean(match[1]); | ||
// requiresBootstrap = requiresBootstrap || (previous !== config.suspended && config.sendEventsOnSuspend); | ||
// } | ||
// enumerateProgrammables(function(obj, prop, payloadName, typeConverter) { | ||
// const match = new RegExp(`.*${payloadName}\\s+(\\S+)`).exec(data); | ||
// if (match) { | ||
// handled = true; | ||
// const previous = obj[prop]; | ||
// obj[prop] = typeConverter(match[1]); | ||
// requiresBootstrap = requiresBootstrap || (previous !== obj[prop] && dynamicDuration(prop, obj[prop])); | ||
// } | ||
// }); | ||
switch (source) { | ||
case 'payload string': | ||
node.error("Invalid action input (via msg.payload string)"); | ||
break; | ||
case 'action': | ||
node.error("Invalid action input (via msg.payload object action property)"); | ||
break; | ||
} | ||
} | ||
return handled; | ||
} | ||
function getInfo() { | ||
@@ -361,5 +422,10 @@ var ret = { | ||
function sync() { | ||
if (events.last) send(events.last); | ||
} | ||
function send(event, manual) { | ||
log(1, 'emitting \'' + event.name + '\' event'); | ||
event.last.moment = node.now(); | ||
events.last = event; | ||
@@ -571,2 +637,3 @@ if (!event.suppressrepeats || state != event.state) { | ||
clearTimeout(events.on.timeout); | ||
clearTimeout(resendObj); | ||
events.on.moment = null; | ||
@@ -622,2 +689,3 @@ | ||
function enumerateProgrammables(callback) { | ||
callback(events.on, 'type', 'ontype', String); | ||
callback(events.on, 'timetod', 'triggertime', String); | ||
@@ -630,2 +698,3 @@ callback(events.on, 'timetod', 'ontime', String); | ||
callback(events.on, 'randomoffset', 'onrandomoffset', toBoolean); | ||
callback(events.off, 'type', 'offtype', String); | ||
callback(events.off, 'timetod', 'offtime', String); | ||
@@ -632,0 +701,0 @@ callback(events.off, 'duration', 'duration', String); |
{ | ||
"name": "node-red-contrib-eztimer", | ||
"version": "1.2.4", | ||
"version": "1.2.5", | ||
"description": "A simple-yet-flexible timer/scheduler for node-red", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
139
README.md
# eztimer | ||
Timer/scheduler for node-red which allows you to enter on/off times as 24hr clock (e.g. 01:10) or suncalc events (e.g. | ||
Timer/scheduler for Node-RED which allows you to enter on/off times as 24hr clock (e.g. 01:10) or suncalc events (e.g. | ||
goldenHour). It also allows you to offset times and randomise the time within the offset. | ||
@@ -9,14 +9,31 @@ | ||
Emphasis has been put on creating a simple interface and utilising built-in node-red formatting helpers (such as creating | ||
Emphasis has been put on creating a simple interface and utilising built-in Node-RED formatting helpers (such as creating | ||
a JSON payload). | ||
# Installation | ||
## via Node-RED GUI | ||
Use the built-in Node-Red [Palette Manager](https://nodered.org/docs/user-guide/editor/palette/manager) to find and install. | ||
This node requires node 4.x. It's tested against 4.6.1. | ||
## via NPM | ||
In the CLI on your Node-Red box; | ||
```sh | ||
cd ~/.node-red | ||
npm install node-red-contrib-eztimer | ||
``` | ||
$ cd ~/.node-red | ||
$ npm install node-red-contrib-eztimer | ||
## Development Builds | ||
This isn't for most people - but I've thrown this in so I don't need to keep explaining it in GitHub issues. This assumes you have a default install of Node-RED. | ||
```sh | ||
cd ~ | ||
wget https://raw.githubusercontent.com/mrgadget/node-red-contrib-eztimer/develop/index.js | ||
wget https://raw.githubusercontent.com/mrgadget/node-red-contrib-eztimer/develop/index.html | ||
cd ~/.node-red/node_modules/node-red-contrib-eztimer | ||
mv index.js index.js.bak | ||
mv index.html index.html.bak | ||
cp ~/index.js . | ||
cp ~/index.html . | ||
``` | ||
You will need to restart Node-RED for the change to take effect. You can put back your old version at any time simply by copying the backup back over top. | ||
# Configuration | ||
## Schedule | ||
@@ -27,3 +44,3 @@ | ||
## Suspending scheduling | ||
## Suspending Scheduling (aka Manual Mode) | ||
@@ -38,18 +55,20 @@ The **Suspend Scheduling** checkbox allows you to disable time scheduling. If scheduling is suspended, eztimer will only | ||
Select the type of trigger from the dropdown and this will provide either a fruther dropdown (for suncalc events), or a textbox to enter either a 24hr time (HH:mm[:ss]) or, for the `off` event, a duration (hh:mm:ss). | ||
Select the type of trigger from the dropdown and this will provide either; | ||
* a dropdown for Sun Events, or, | ||
* a textbox to enter either; | ||
* a 24hr time, or, | ||
* a duration (for the `off` event). | ||
As of `1.1.1` it is supported to specify timespans in numerous timespan formats: | ||
The below table denotes the permitted formats for Times/durations: | ||
| input | interpretation | ||
|--------------|----------------------------- | ||
| `"12:14"` | 12 hours and 14 minutes | ||
| `"12:14:24"` | Time of day, or 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 | ||
| 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 payload input. | ||
These are valid both at UI/config-time and at runtime using the `duration` payload. | ||
## Offsets | ||
@@ -59,9 +78,9 @@ | ||
* -ve number brings the time forward. E.g. if the time is dusk and offset is -60, a message will be generated 60 minutes | ||
before dusk. | ||
* +ve number delays the time by the specified number of minutes | ||
* -ve number brings the time forward. E.g. if the time is dusk and offset is `-60`, a message will be generated 60 minutes | ||
_before_ dusk. | ||
* +ve number _delays_ the time by the specified number of minutes. | ||
## Randomisation of times | ||
Both `on` and `off` times can be randomised by ticking "Use random time within offset period". For example, if you specify | ||
Both `on` and `off` times can be randomised by ticking "Use random time within offset period". For example, if you specify | ||
dusk with an offset of -60 minutes, every day a message will be generated at a random time in a 60 minute window before | ||
@@ -72,7 +91,8 @@ dusk. | ||
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. | ||
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 | ||
You can wire inject nodes to the input of this node and send the following in `msg.payload`. | ||
You can wire inject nodes to the input of this node and send the following string values in `msg.payload`. | ||
@@ -86,2 +106,3 @@ | msg.payload | Description | | ||
| `cancel` | Cancels the current run (if any) of the timer (_without_ emitting an `off` event). | | ||
| `sync` | Re-sends the last emitted event | | ||
@@ -92,3 +113,3 @@ # Programmatic Control | ||
**It is very important to note that properties set programmatically in this manner are transient. They will not persist over a NodeRED restart or redeploy!** | ||
**It is very important to note that properties set programmatically in this manner are transient. They will not persist over a Node-RED restart or redeploy!** | ||
@@ -100,28 +121,40 @@ Note that both the property-based and string-based specifications are overrides that violate the usual behavior. | ||
| Property | Type | | ||
| ----------------------------- | --------------------------------------------------------------------- | | ||
| `msg.payload.suspended` | Boolean: true will suspend scheduling, false will resume scheduling | | ||
| `msg.payload.tag` | String value emitted as the tag for all events | | ||
| `msg.payload.ontime` | String value representing time of day (HH:mm[:ss]) | | ||
| `msg.payload.triggertime` | Alias of `ontime` | | ||
| `msg.payload.ontopic` | String value emitted as the topic for the on event | | ||
| `msg.payload.onvalue` | Output value for on event (must be same as configured type) | | ||
| `msg.payload.triggervalue` | Alias of `onvalue` | | ||
| `msg.payload.onoffset` | Number value as specified above for Offset configuration | | ||
| `msg.payload.onrandomoffset` | Boolean value as specified above in Randomisation of Times | | ||
| `msg.payload.offtime` | String value representing time of day (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 | | ||
| `msg.payload.offvalue` | Output value for off event (must be same as configured type) | | ||
| `msg.payload.offoffset` | Number value as specified above for Offset configuration | | ||
| `msg.payload.offrandomoffset` | Boolean value as specified above in Randomisation of Times | | ||
| `msg.payload.mon` | Boolean: true enables the schedule on a Monday, false disables it. | | ||
| `msg.payload.tue` | Boolean: true enables the schedule on a Tuesday, false disables it. | | ||
| `msg.payload.wed` | Boolean: true enables the schedule on a Wednesday, false disables it. | | ||
| `msg.payload.thu` | Boolean: true enables the schedule on a Thursday, false disables it. | | ||
| `msg.payload.fri` | Boolean: true enables the schedule on a Friday, false disables it. | | ||
| `msg.payload.sat` | Boolean: true enables the schedule on a Saturday, false disables it. | | ||
| `msg.payload.sun` | Boolean: true enables the schedule on a Sunday, false disables it. | | ||
| Property | Type | | ||
| ----------------------------- | ----------------------------------------------------------------------------------- | | ||
| `msg.payload.action` | Accepts the standard input payloads of `trigger`, `on`, `off`, etc. | | ||
| `msg.payload.suspended` | Boolean: true will suspend scheduling, false will resume scheduling | | ||
| `msg.payload.manual` | Alias of `suspended` | | ||
| `msg.payload.tag` | String value emitted as the tag for all events | | ||
| `msg.payload.ontype` | Integer value: `Sun Event [1]` & `Time of Day [2]` | | ||
| `msg.payload.ontime` | String value representing time of day (HH:mm[:ss]) | | ||
| `msg.payload.triggertime` | Alias of `ontime` | | ||
| `msg.payload.ontopic` | String value emitted as the topic for the on event | | ||
| `msg.payload.onvalue` | Output value for on event (must be same as configured type) | | ||
| `msg.payload.triggervalue` | Alias of `onvalue` | | ||
| `msg.payload.onoffset` | Number value as specified above for Offset configuration | | ||
| `msg.payload.onrandomoffset` | Boolean value as specified above in Randomisation of Times | | ||
| `msg.payload.offtype` | Integer value: `Sun Event [1]`, `Time of Day [2]` & `Duration [3]` | | ||
| `msg.payload.offtime` | String value representing time of day (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 | | ||
| `msg.payload.offvalue` | Output value for off event (must be same as configured type) | | ||
| `msg.payload.offoffset` | Number value as specified above for Offset configuration | | ||
| `msg.payload.offrandomoffset` | Boolean value as specified above in Randomisation of Times | | ||
| `msg.payload.mon` | Boolean: true enables the schedule on a Monday, false disables it. | | ||
| `msg.payload.tue` | Boolean: true enables the schedule on a Tuesday, false disables it. | | ||
| `msg.payload.wed` | Boolean: true enables the schedule on a Wednesday, false disables it. | | ||
| `msg.payload.thu` | Boolean: true enables the schedule on a Thursday, false disables it. | | ||
| `msg.payload.fri` | Boolean: true enables the schedule on a Friday, false disables it. | | ||
| `msg.payload.sat` | Boolean: true enables the schedule on a Saturday, false disables it. | | ||
| `msg.payload.sun` | Boolean: true enables the schedule on a Sunday, false disables it. | | ||
# Change Log | ||
## 1.2.5 | ||
* Added `resend` feature. Enabling this causese the last scheduled event to be re-emitted at the pre-defined interval. [credit @JasonSwindle](https://github.com/mrgadget/node-red-contrib-eztimer/issues/37) | ||
* Included `action` in programmatic control - this enablings the sending of on/off events from a JSON input. [credit @petter-b](https://github.com/mrgadget/node-red-contrib-eztimer/issues/38) | ||
* Hooked into HomeAssistant (where available) for latitude and longitude. [credit @mingan666](https://github.com/mrgadget/node-red-contrib-eztimer/issues/39) | ||
* Added `offtype` and `ontype` programmables. Used for dynamically changing the event type - these changes are _not_ saved (ie, they won't survive a Node-RED restart), and input is _not_ validated, so use with caution. Required integer values are in the [Programmatic Control](##Programmatic-Control) table. [credit @matt6575](https://github.com/mrgadget/node-red-contrib-eztimer/issues/40) | ||
* Added `manual` as an alias for `suspended` as it makes more sense with how some users use the node. [credit @matt6575](https://github.com/mrgadget/node-red-contrib-eztimer/issues/40) | ||
* Fixed anomolous error upon input message. [credit @marc-gist](https://github.com/mrgadget/node-red-contrib-eztimer/issues/41) | ||
## 1.2.4 | ||
@@ -152,3 +185,3 @@ * Change `info` to be sent with every output (under the `msg.info`). [credit @Fires04](https://github.com/mrgadget/node-red-contrib-eztimer/issues/30). | ||
Fixes driven by issue #21 [credit @jazzgil](https://github.com/mrgadget/node-red-contrib-eztimer/issues/21). | ||
* Fixed the node-red info pane to correctly match the the programmatic options in this file (see [Programmatic Control](#Programmatic-Control)) | ||
* Fixed the Node-RED info pane to correctly match the the programmatic options in this file (see [Programmatic Control](#Programmatic-Control)) | ||
* Updated output function to correctly emit programatically set topic (`ontopic` or `offtopic`) - not previously sent. | ||
@@ -222,2 +255,2 @@ * Added ability to set `tag` value programmatically. | ||
## 1.0.5 | ||
* Fix `ontime` and `offtime` inputs, added `triggertime` as an alias of `ontime`. | ||
* Fix `ontime` and `offtime` inputs, added `triggertime` as an alias of `ontime`. |
Sorry, the diff of this file is not supported yet
97942
1023
247