├── README.md ├── icons └── timer.png ├── package.json ├── scheduler.html └── scheduler.js /README.md: -------------------------------------------------------------------------------- 1 | # Scheduler for Node-Red 2 | A simple scheduler for node-red 3 | 4 | I created this pair of files to provide for my own purposes to offer better timing facilities than the normal inject mode including dusk and dawn settings (handy for lighting), weeks and months. No doubt soon I'll add more. 5 | 6 | Install this in your node-red nodes area - the only files you NEED are the .js, .html files and the icon directory with the image. 7 | 8 | You can find out more about this at http://tech.scargill.net/node-red-scheduler/ 9 | 10 | This should be suitable for general use. It has 2 outputs, one of which triggers when there is a change and presents one of two messages, the other a simple 1 or 0 every minute. 11 | 12 | In my case, this is used in a Raspberry Pi2 and in a folder called pi/node-red/nodes/node-red-nodes/time/scheduler 13 | 14 | -------------------------------------------------------------------------------- /icons/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scargill/scheduler/d959364ba13a31178cc086829fb50668bcdfc411/icons/timer.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "node-red-contrib-scheduler", 3 | "version" : "1.0.0", 4 | "description" : "A Node Red Timer with dusk, dawn and months", 5 | "author": "Peter Scargill ", 6 | "dependencies": { 7 | "suncalc" : "1.*" 8 | }, 9 | "keywords": [ "node-red", "scheduler" ], 10 | "node-red" : { 11 | "nodes": { 12 | "scheduler": "scheduler.js" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scheduler.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 304 | 305 | 310 | 311 | 391 | -------------------------------------------------------------------------------- /scheduler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mostly - apart from the Sunrise module on which this depends this is 3 | * copyright (c) Peter Scargill - but as I've had so many ideas from others - 4 | * consider it free to use for whatever purpose you like. If you redesign it 5 | * please remember to drop my name and link in there somewhere. 6 | * http://tech.scargill.net This software puts out one of two messages on change 7 | * of state which could be sent to the MQTT node and can be triggered by the 8 | * INJECT module (should be triggered once every minute) 9 | */ 10 | 11 | module.exports = function(RED) { 12 | "use strict"; 13 | var SunCalc = require('suncalc'); 14 | 15 | function SchedulerNode(n) { 16 | RED.nodes.createNode(this, n); 17 | var node = this; 18 | 19 | node.lat = n.lat; 20 | node.lon = n.lon; 21 | node.start = n.start; 22 | node.end = n.end; 23 | node.startt = n.starttime; 24 | node.endt = n.endtime; 25 | node.duskOff = n.duskoff; 26 | node.dawnOff = n.dawnoff; 27 | node.outtopic = n.outtopic; 28 | node.outPayload1 = n.outpayload1; 29 | node.outPayload2 = n.outpayload2; 30 | 31 | node.sun = n.sun; 32 | node.mon = n.mon; 33 | node.tue = n.tue; 34 | node.wed = n.wed; 35 | node.thu = n.thu; 36 | node.fri = n.fri; 37 | node.sat = n.sat; 38 | node.jan = n.jan; 39 | node.feb = n.feb; 40 | node.mar = n.mar; 41 | node.apr = n.apr; 42 | node.may = n.may; 43 | node.jun = n.jun; 44 | node.jul = n.jul; 45 | node.aug = n.aug; 46 | node.sep = n.sep; 47 | node.oct = n.oct; 48 | node.nov = n.nov; 49 | node.dec = n.dec; 50 | 51 | var ison = 0; 52 | var newEndTime = 0; 53 | 54 | node 55 | .on( 56 | "input", 57 | function(inmsg) { 58 | var now = new Date(); 59 | var nowOff = -now.getTimezoneOffset() * 60000; 60 | var times = SunCalc.getTimes(now, node.lat, 61 | node.lon); 62 | var nowMillis = Date.UTC(now.getUTCFullYear(), now 63 | .getUTCMonth(), now.getUTCDate(), now 64 | .getUTCHours(), now.getUTCMinutes(), 1); 65 | var midnightMillis = Date.UTC(now.getUTCFullYear(), 66 | now.getUTCMonth(), now.getUTCDate(), 0, 1); 67 | var startMillis = Date.UTC(times[node.start] 68 | .getUTCFullYear(), times[node.start] 69 | .getUTCMonth(), times[node.start] 70 | .getUTCDate(), times[node.start] 71 | .getUTCHours(), times[node.start] 72 | .getUTCMinutes()); 73 | var endMillis = Date.UTC(times[node.end] 74 | .getUTCFullYear(), times[node.end] 75 | .getUTCMonth(), times[node.end] 76 | .getUTCDate(), times[node.end] 77 | .getUTCHours(), times[node.end] 78 | .getUTCMinutes()); 79 | 80 | nowMillis += nowOff; 81 | startMillis += nowOff; 82 | endMillis += nowOff; 83 | 84 | var outmsg = { 85 | payload : "", 86 | topic : "" 87 | }; 88 | var outmsg2 = { 89 | payload : "", 90 | topic : "status" 91 | }; 92 | 93 | var dawn = (((startMillis - midnightMillis) / 60000) + Number(node.dawnOff)) % 1440; 94 | var dusk = (((endMillis - midnightMillis) / 60000) + Number(node.duskOff)) % 1440; 95 | var today = (Math 96 | .round((nowMillis - midnightMillis) / 60000)) % 1440; 97 | var startTime = parseInt(node.startt, 10); 98 | var endTime = parseInt(node.endt, 10); 99 | 100 | if (startTime == 5000) 101 | startTime = dawn; 102 | if (startTime == 6000) 103 | startTime = dusk; 104 | if (endTime == 5000) 105 | endTime = dawn; 106 | if (endTime == 6000) 107 | endTime = dusk; 108 | if (inmsg.payload == "reset") 109 | ison = 0; 110 | 111 | var proceed; 112 | proceed = 0; 113 | 114 | switch (now.getDay()) { 115 | case 0: 116 | if (node.sun) 117 | proceed++; 118 | break; 119 | case 1: 120 | if (node.mon) 121 | proceed++; 122 | break; 123 | case 2: 124 | if (node.tue) 125 | proceed++; 126 | break; 127 | case 3: 128 | if (node.wed) 129 | proceed++; 130 | break; 131 | case 4: 132 | if (node.thu) 133 | proceed++; 134 | break; 135 | case 5: 136 | if (node.fri) 137 | proceed++; 138 | break; 139 | case 6: 140 | if (node.sat) 141 | proceed++; 142 | break; 143 | } 144 | 145 | if (proceed) 146 | switch (now.getMonth()) { 147 | case 0: 148 | if (node.jan) 149 | proceed++; 150 | break; 151 | case 1: 152 | if (node.feb) 153 | proceed++; 154 | break; 155 | case 2: 156 | if (node.mar) 157 | proceed++; 158 | break; 159 | case 3: 160 | if (node.apr) 161 | proceed++; 162 | break; 163 | case 4: 164 | if (node.may) 165 | proceed++; 166 | break; 167 | case 5: 168 | if (node.jun) 169 | proceed++; 170 | break; 171 | case 6: 172 | if (node.jul) 173 | proceed++; 174 | break; 175 | case 7: 176 | if (node.aug) 177 | proceed++; 178 | break; 179 | case 8: 180 | if (node.sep) 181 | proceed++; 182 | break; 183 | case 9: 184 | if (node.oct) 185 | proceed++; 186 | break; 187 | case 10: 188 | if (node.nov) 189 | proceed++; 190 | break; 191 | case 11: 192 | if (node.dec) 193 | proceed++; 194 | break; 195 | } 196 | 197 | if (proceed >= 2) 198 | proceed = 1; 199 | else 200 | proceed = 0; 201 | 202 | newEndTime = endTime; 203 | if (endTime > 10000) 204 | newEndTime = startTime + (endTime - 10000); 205 | 206 | if (proceed) // have to handle midnight wrap 207 | { 208 | if (startTime <= newEndTime) { 209 | if ((today >= startTime) 210 | && (today <= newEndTime)) 211 | proceed++; 212 | } else { 213 | if ((today >= startTime) 214 | || (today <= newEndTime)) 215 | proceed++; 216 | } 217 | } 218 | 219 | var duration = 0; 220 | if (proceed >= 2) { 221 | if (today <= newEndTime) 222 | duration = newEndTime - today; 223 | else 224 | duration = newEndTime + (1440 - today); 225 | node.status({ 226 | fill : "yellow", 227 | shape : "dot", 228 | text : "on for " + duration + " mins" 229 | }); 230 | } else { 231 | node.status({ 232 | fill : "blue", 233 | shape : "dot", 234 | text : "off" 235 | }); 236 | } 237 | 238 | outmsg.topic = node.outtopic; 239 | if (proceed >= 2) { 240 | if (((ison == 0) || (ison == 1))) { 241 | outmsg.payload = node.outPayload1; 242 | ison = 2; 243 | outmsg2.payload = (ison - 1).toString(); 244 | node.send([outmsg, outmsg2]); 245 | } else 246 | { 247 | outmsg2.payload = (ison - 1).toString(); 248 | node.send([null, outmsg2]); 249 | } 250 | 251 | } else { 252 | if ((ison == 0) || (ison == 2)) { 253 | outmsg.payload = node.outPayload2; 254 | ison = 1; 255 | outmsg2.payload = (ison - 1).toString(); 256 | node.send([outmsg, outmsg2]); 257 | } else 258 | { 259 | outmsg2.payload = (ison - 1).toString(); 260 | node.send([null, outmsg2]); 261 | } 262 | } 263 | }); 264 | 265 | var tock = setTimeout(function() { 266 | node.emit("input", {}); 267 | }, 2000); // wait 2 secs before starting to let things settle down - 268 | // e.g. UI connect 269 | 270 | var tick = setInterval(function() { 271 | node.emit("input", {}); 272 | }, 60000); // trigger every 60 secs 273 | 274 | node.on("close", function() { 275 | if (tock) { 276 | clearTimeout(tock); 277 | } 278 | if (tick) { 279 | clearInterval(tick); 280 | } 281 | }); 282 | 283 | } 284 | 285 | RED.httpAdmin.post("/scheduler/:id", RED.auth 286 | .needsPermission("scheduler.write"), function(req, res) { 287 | var node = RED.nodes.getNode(req.params.id); 288 | if (node != null) { 289 | try { 290 | node.emit("input", { 291 | payload : "reset" 292 | }); 293 | res.send(200); 294 | } catch (err) { 295 | res.send(500); 296 | node.error("Inject failed:" + err); 297 | } 298 | } else { 299 | res.send(404); 300 | } 301 | }); 302 | 303 | RED.nodes.registerType("scheduler", SchedulerNode); 304 | } 305 | --------------------------------------------------------------------------------