Doesn't "0-7" mean there are 8 days in a week?
It does, kinda, but I can see reasons to keep 7 when saying the week starts on Monday as 1-indexed
The underlying library supports 0 and 7 as Sunday for compatibility.
I believe it is deliberate.
E.g. you can use "0-6" or "1-7" or indeed "0-7".
An excerpt from internet...
This is a matter of portability. In early Unices, some versions of cron accepted 0 as Sunday, and some accepted 7 as Sunday -- this format is an attempt to be portable with both. From
man 5 crontab
This is to be compatible with different Cron formats. Again this is the part of the underlying Cron library I chose (because it has year support). It's to do with supporting 5, 6 or 7 part Cron formats.
E.g.
- if there are 5 parts, its minutes
m h md m wd
- 6 parts considers the 1st part to be seconds
s m h md m wd
- 7 parts considers the last part as year
s m h md m wd y
Haha, you gotta love UNIXy things right?!
@afelix, if you can provide an example of this, I'll check it out. Could be a bug. The description library is unrelated to the Cron library, perhaps there are some incompatible expressions. Though it may be the expressions themselves. For example
* * * * * *
== every second0 * * * * *
== every minute
Both are 6 part expressions but since the seconds part is 0, it means when seconds == 0 thus every minute (same as 5 part * * * * *)
Iâm about to go to bed, will post a sample in the morning. I noticed how when I was typing it would update the preview and it made me confused as I typed out crontabs dozens of times and had to recount it every time to see if I was doing the right thing
I cannot get my head around the possibilities of this node, awesome work!
Thinking about mqtt; a node has a specific outcome, publishes schedule object to mqtt, mqtt connected to cronplus node and schedules, like an advanced trigger node (without millis)
Yes. I'm just annoyed that Steve beat me to it and made a better job of it as well most likely .
Something like this was on my backlog.
I set it up today with a basic schedule for all my medication to send reminders on when to take them. Next step is to add the variable types followed by a switch to see if itâs needed before sending this. Before cron-plus I tried messing with both schedex and bigtimer when I first discovered node-red. This is a perfect replacement for those standardised schedules. Probably going to hook it up to my pain levels check tomorrow.
Huge thank you to @Steve-Mcl for literally making my life easier and more tolerable this way! Your node will be doing the heavy lifting for most of my daily usage that I had designed my most basic flows for on paper.
Now we just need a way to visualise/control a schedule from uibuilder
Working on that someday soon
Visible schedules that are in place, and options to edit them for when medication changes for example. No need to dive back into the flow when you can set it dynamically like this amazing node allows.
Been trying to find time to do an awesome demo showing how you can setup from a web page and list out existing schedules etc from the node etc but never seem to find the time (sic)
Because sometimes a picture does say more than a thousand words
Cron+ is helping me out a lot this week. Had an emergency doctor visit this weekend and got more pills. Except somehow both the doctor on call and the pharmacist managed to miss that the antibiotics they gave me could not be taken with my calcium supplements (which I cannot safely go without either), even though they both saw the recent list of medication and I mentioned it twice or so, plus halfway joking (so they were aware of allergies and everything) that I wouldnât be eating/drinking dairy as I could not have it either way so no problems there.
Long story short, I figured out a schedule that allowed me to take everything with even a bit of buffer time if needed, without causing problems in their effects. After triple checking it was right I added them to my regular medication/notification scheduler in Node-RED. Which sends notifications to my phone for when I have to take what/when I need to drink because I forget it too often.
So thanks a lot Steve, toasting to my health, quite literally here
Hi Lena,
That looks like a neat solution for your scheduling requirement.
Could you share some of your NR flow as I'd like to see if I could do something similar with my Android phone and Telegram ??
Redacting the regular medication names (as those are quite a bit too specific), but here you go I'm actually attempting some Telegram compatibility for another flow using node-red-contrib-chatbot
, so I'll see if I can integrate that later as well. As I did this quickly while still working on the actual interface (the uibuilder node in there), the schedules are hardcoded but can be added dynamically too.
[{"id":"a104913f.0bb62","type":"cronplus","z":"d83aa04f.39ec3","name":"Reminders","outputField":"payload","timeZone":"Europe/Amsterdam","options":[{"topic":"drink_water","payload":"{\"notificationName\":\"DrinkWater\"}","type":"json","expression":"0 0 9,11,13,15,17,19,21 * * *"},{"topic":"medication_<redacted>_wednesday","payload":"{\"notificationName\":\"Medication\",\"title\":\"It's medication time\",\"text\":\"Please replace your <redacted>.\"}","type":"json","expression":"0 0 9 * * 3 *"},{"topic":"medication_<redacted>_saturday","payload":"{\"notificationName\":\"Medication\",\"title\":\"It's medication time\",\"text\":\"Please replace your <redacted>.\"}","type":"json","expression":"0 0 19 * * 6 *"},{"topic":"medication_<redacted>","payload":"{\"notificationName\":\"Medication\",\"title\":\"It's medication time\",\"text\":\"Please take your <redacted>.\"}","type":"json","expression":"0 0 9 * * * * "},{"topic":"medication_calcium_oct_2019","payload":"{\"notificationName\":\"Medication\",\"title\":\"It's medication time\",\"text\":\"Please take your calcium supplements now.\"}","type":"json","expression":"2019-10-06T12:00:00+0200,2019-10-07T12:00:00+0200,2019-10-08T12:00:00+0200,2019-10-09T12:00:00+0200,2019-10-10T12:00:00+0200,2019-10-11T12:00:00+0200,2019-10-12T12:00:00+0200"},{"topic":"medication_antibiotics_oct_2019_morning","payload":"{\"notificationName\":\"Medication\",\"title\":\"It's medication time\",\"text\":\"Please take your antibiotics.\"}","type":"json","expression":"0 0 9 6,7,8,9,10,11,12 10 * 2019"},{"topic":"medication_antibiotics_oct_2019_evening","payload":"{ \"notificationName\": \"Medication\", \"title\": \"It's medication time\", \"text\": \"Please take your antibiotics.\" }","type":"json","expression":"0 30 19 6,7,8,9,10,11 10 * 2019"}],"x":150,"y":180,"wires":[["4e0abfe4.031448"]]},{"id":"a5d2820b.99343","type":"http request","z":"d83aa04f.39ec3","name":"Send to Pushcut","method":"POST","ret":"txt","paytoqs":false,"url":"https://api.pushcut.io/<pushcut_secret_token>/notifications/{{{payload.notificationName}}}","tls":"","persist":false,"proxy":"","authType":"","x":550,"y":140,"wires":[["94f33c5b.a0a6a8"]]},{"id":"94f33c5b.a0a6a8","type":"debug","z":"d83aa04f.39ec3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":730,"y":140,"wires":[]},{"id":"4e0abfe4.031448","type":"switch","z":"d83aa04f.39ec3","name":"","property":"cronplus.triggerTimestamp","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":310,"y":180,"wires":[["a5d2820b.99343"],["390597.07458a6a","fc1af895.fa61a8"]]},{"id":"a4ac0cd9.3d944","type":"comment","z":"d83aa04f.39ec3","name":"Scheduled output","info":"","x":550,"y":100,"wires":[]},{"id":"390597.07458a6a","type":"uibuilder","z":"d83aa04f.39ec3","name":"Scheduler Interface","topic":"","url":"scheduler_ui","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":false,"showfolder":false,"x":530,"y":260,"wires":[["30cceccb.f36f24"],[]]},{"id":"8d3c9780.78bc08","type":"comment","z":"d83aa04f.39ec3","name":"Control messages","info":"","x":550,"y":220,"wires":[]},{"id":"fc1af895.fa61a8","type":"debug","z":"d83aa04f.39ec3","name":"Cron+ control messages output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":540,"y":380,"wires":[]},{"id":"377097ba.e8eff8","type":"inject","z":"d83aa04f.39ec3","name":"","topic":"cronplus_control","payload":"{\"command\":\"status-all\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":400,"wires":[["a104913f.0bb62"]]},{"id":"30cceccb.f36f24","type":"switch","z":"d83aa04f.39ec3","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"cronplus_control","vt":"str"},{"t":"eq","v":"uibuilder_debug","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":750,"y":240,"wires":[["cfb54d82.8c789"],["9860849d.47b328"]]},{"id":"e38af679.55d71","type":"link in","z":"d83aa04f.39ec3","name":"Cron-plus IN","links":["cfb54d82.8c789"],"x":90,"y":60,"wires":[["a104913f.0bb62"]],"l":true},{"id":"cfb54d82.8c789","type":"link out","z":"d83aa04f.39ec3","name":"UIbuilder-OUT-cronplus","links":["e38af679.55d71"],"x":875,"y":220,"wires":[]},{"id":"9860849d.47b328","type":"debug","z":"d83aa04f.39ec3","name":"uibuilder debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":940,"y":260,"wires":[]}]
Thanks - I'll study it later as it's my IoT Club today.
I'm reasonably sure I can port it to use Telegram in about an hour or so, and I need a distraction from an overhaul to my current flow so be sure to check back later
Stop the clock, it's working
I technically had the flow finished in an hour, spent the last 15 minutes squashing a bug in it
[{"id":"d12e2291.f01a4","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"7e97b34c.f9bc5c","type":"chatbot-telegram-receive","z":"d12e2291.f01a4","bot":"784718d7.7053e","botProduction":"","x":130,"y":180,"wires":[["398dd694.5e493a"]]},{"id":"c063b4af.f7e87","type":"chatbot-debug","z":"d12e2291.f01a4","x":750,"y":160,"wires":[]},{"id":"398dd694.5e493a","type":"chatbot-authorized","z":"d12e2291.f01a4","x":330,"y":180,"wires":[["aec4d27f.f70cd8"],[]]},{"id":"1a4a9de0.983f72","type":"chatbot-conversation","z":"d12e2291.f01a4","name":"","botTelegram":"784718d7.7053e","botTelegramProduction":"","botSlack":"","botSlackProduction":"","botFacebook":"","botFacebookProduction":"","botViber":"","botViberProduction":"","botUniversal":"","botUniversalProduction":"","botTwilio":"","botTwilioProduction":"","botDiscord":"","botDiscordProduction":"","chatId":"","transport":"telegram","messageId":"","contextMessageId":false,"store":"","x":110,"y":420,"wires":[["2d509d49.cfb3ba"]]},{"id":"8494496f.986818","type":"chatbot-message","z":"d12e2291.f01a4","name":"Send notification to user","message":[],"answer":false,"silent":false,"x":510,"y":420,"wires":[["d2872997.824c"]]},{"id":"fc85fbeb.6802","type":"cronplus","z":"d12e2291.f01a4","name":"Reminders","outputField":"payload","timeZone":"","options":[{"topic":"drink_water","payload":"{\"readableTitle\":\"Remember to drink\",\"message\":\"It's time to drink some water or tea.\",\"to\":{\"username\":\"afelix\"}}","type":"json","expression":"0 0 9,11,13,15,17,19,21 * * *"}],"x":110,"y":280,"wires":[["ff3061e4.af867"]]},{"id":"ff3061e4.af867","type":"switch","z":"d12e2291.f01a4","name":"Is reminder?","property":"cronplus.triggerTimestamp","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":290,"y":280,"wires":[["cebf1bde.f1ea4"],[]],"outputLabels":["regular output","control message"]},{"id":"aec4d27f.f70cd8","type":"switch","z":"d12e2291.f01a4","name":"User seen before?","property":"known_chats","propertyType":"flow","rules":[{"t":"hask","v":"payload.userId","vt":"msg"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":530,"y":160,"wires":[[],["c063b4af.f7e87","c118e5b.a2b4198"]],"outputLabels":["yes","no"]},{"id":"c118e5b.a2b4198","type":"function","z":"d12e2291.f01a4","name":"Store user for future interactions","func":"const known_chats = flow.get('known_chats') || {};\n\nknown_chats[msg.payload.userId] = {\n \"userId\": msg.payload.userId, // yes redundant but it might spare an extra lookup\n \"chatId\": msg.payload.chatId,\n \"transport\": msg.payload.transport\n};\n\nflow.set('known_chats', known_chats);\nreturn msg;","outputs":1,"noerr":0,"x":830,"y":200,"wires":[[]]},{"id":"d2872997.824c","type":"chatbot-telegram-send","z":"d12e2291.f01a4","bot":"784718d7.7053e","botProduction":"","track":false,"passThrough":false,"outputs":0,"x":750,"y":420,"wires":[]},{"id":"cebf1bde.f1ea4","type":"function","z":"d12e2291.f01a4","name":"Prepare for sending to the right user","func":"// Incoming payload:\n//{\n// \"readableTitle\": \"Remember to drink\",\n// \"message\": \"It's time to drink some water or tea.\",\n// \"to\": {\n// \"username\": \"afelix\"\n// }\n//}\nconst known_chats = flow.get('known_chats') || {};\n\nif (known_chats.hasOwnProperty(msg.payload.to.username)) {\n const user_details = known_chats[msg.payload.to.username];\n \n msg._outgoing_text = `**${msg.payload.readableTitle}**\\n${msg.payload.message}`;\n\n msg.payload = {\n 'chatId': user_details.chatId\n }\n return msg;\n}\nelse {\n node.error(`The user (${msg.payload.to.username}) set to receive this message is not known to the Telegram bot yet. Please start a conversation first.`, msg);\n}","outputs":1,"noerr":0,"x":590,"y":280,"wires":[["1a4a9de0.983f72","205697b0.a7c4c8"]]},{"id":"e782f439.df1568","type":"comment","z":"d12e2291.f01a4","name":"Create flow store on first run","info":"","x":140,"y":20,"wires":[]},{"id":"eb39eb9a.ebde8","type":"comment","z":"d12e2291.f01a4","name":"Actual handler","info":"","x":90,"y":140,"wires":[]},{"id":"4c5e468d.08c67","type":"inject","z":"d12e2291.f01a4","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":60,"wires":[["3ab07597.e87632"]]},{"id":"3ab07597.e87632","type":"function","z":"d12e2291.f01a4","name":"Create flow store if needed","func":"const known_chats = flow.get('known_chats') || {};\nnode.warn(known_chats);\nif (Object.keys(known_chats).length === 0) {\n // new store\n flow.set('known_chats', known_chats);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":60,"wires":[[]]},{"id":"205697b0.a7c4c8","type":"debug","z":"d12e2291.f01a4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":850,"y":280,"wires":[]},{"id":"2d509d49.cfb3ba","type":"change","z":"d12e2291.f01a4","name":"Set text as payload","rules":[{"t":"move","p":"_outgoing_text","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":420,"wires":[["8494496f.986818"]]},{"id":"784718d7.7053e","type":"chatbot-telegram-node","z":"","botname":"<username of your bot>","usernames":"<your own username/user-id to set as authorised>","providerToken":"","polling":"1000","store":"f2eb2047.10f9e","log":"","parseMode":"","debug":true,"webHook":"","connectMode":"polling"},{"id":"f2eb2047.10f9e","type":"chatbot-context-store","z":"","name":"context-memory","contextStorage":"memory","contextParams":""}]
Required nodes: node-red-contrib-cron-plus
(duh) and node-red-contrib-chatbot
(for the telegram nodes).
You're most welcome. And thanks for the kind words. Take care.