How to trigger a flow every X days at a specific time? (instead of on specific weekdays or interval)

I’m trying to build an irrigation system with node red.

There is a whole bunch of other stuff going into this automation (Soil moisture sensors, rain senors etc.) but the one thing I’m struggling with is just setting up the cron job! I’ll make the example as simple as possible, leaving out all other parameters:

My plants want water every second day at 22:00.

Sounds simple enough, right? But how to I trigger a flow every second day at a specific time?

Normally I schedule flows with the inject node. However this does not seem to work in this case:

  • I can tell it to inject at specific times at specific weekdays, but that wont work because the week has 7 days and that is not dividable by two. I’d be watering Sunday as well as Monday:

  • I can tell it to inject after 0.1 seconds and after that every 48 hours, but that would just mean it starts counting whenever I deploy instead of at 22:00

Am I not seeing the obvious or is there any other way to achieve this?
Any Ideas? Thanks in advance!

Couple of solutions:

The cronplus node is amazing and has everything you could ever need, and manages ‘every other day at 22:00’ with just a couple of clicks using the expression builder.
The ‘drawback’ is that the cron computes based on Unix day count, hence I don’t think you can select to run to differentiate between starting today or starting tomorrow.

If you don’t want an extra node, or if you want to specify the exact day to run from, then you can use the standard inject node, with a trigger at every day, and then add a switch node with some JSONata to select only ‘other days’, either as even or odd.

$floor($millis()/86400000)%2=0

This gets the current Unix millisecond time, floors down to the day count, and returns the modulus remainder as either 0 (even days) or 1 (odd days). Setting the predicate to test for =0 will be true only on even days, hence the switch will only pass on the even days.

This approach is more powerful, since you can use something like %7 and then

$floor($millis()/86400000)%7 in [3, 4]

to pick out day ‘3’ and ‘4’ each week. It just takes a bit of testing to find out which ‘day’ today is (5 apparently) and then the above selects Sunday and Monday each week [subject to your actual timezone in relation to UTC]

Some experimentation may be necessary.

1 Like

Amazing, thank you so much for you explanations! I will try both and see what suits me better.

You could also create a binary template sensor in home assistant. Jinja can return day of the year which then can be checked if odd == 1 or even == 0.

{{ now().strftime('%j') | int % 2 == 0 }}

E. On leap years, the last day will be 366. That would be the only time this wouldn’t work correctly and run 2 days in a row. Rather it’s the opposite leap year it would work correctly. Every year Dec 31(365) and Jan 1 will both be odd

I now tried building a switch node with different rules for different day-intervals (because some plants need water more often than others) however the node will only ever test true for the %1 test. Since today is day number 161 the switch node should - to my understanding - test true for both %1 as well as %7

Or am I completely misunderstanding how this works?

Here is the code:

[{"id":"9adc4eccb5e32c01","type":"switch","z":"e7fd40e55103c187","name":"Every X days","property":"payload","propertyType":"msg","rules":[{"t":"jsonata_exp","v":"$floor($millis()/86400000)%1=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%2=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%3=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%4=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%5=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%6=0","vt":"jsonata"},{"t":"jsonata_exp","v":"$floor($millis()/86400000)%7=0","vt":"jsonata"}],"checkall":"true","repair":false,"outputs":7,"x":2310,"y":500,"wires":[["e09910e4e41c66d3"],["2374172a21c37d51"],["5cd5aad9f106929c"],["a561b5d34c507b36"],["39c0322f3b2ad621"],["61f14bfbbf9ed3da"],["6e5e674ff849b8be"]]},{"id":"25410a9a5b06ddcd","type":"inject","z":"e7fd40e55103c187","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":2040,"y":500,"wires":[["9adc4eccb5e32c01"]]},{"id":"e09910e4e41c66d3","type":"debug","z":"e7fd40e55103c187","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":380,"wires":[]},{"id":"2374172a21c37d51","type":"debug","z":"e7fd40e55103c187","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":420,"wires":[]},{"id":"5cd5aad9f106929c","type":"debug","z":"e7fd40e55103c187","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":460,"wires":[]},{"id":"a561b5d34c507b36","type":"debug","z":"e7fd40e55103c187","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":500,"wires":[]},{"id":"39c0322f3b2ad621","type":"debug","z":"e7fd40e55103c187","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":540,"wires":[]},{"id":"61f14bfbbf9ed3da","type":"debug","z":"e7fd40e55103c187","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":580,"wires":[]},{"id":"6e5e674ff849b8be","type":"debug","z":"e7fd40e55103c187","name":"debug 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2600,"y":620,"wires":[]}]

What am I doing wrong here?

Nothing wrong, it is just the way it works.

So $millis() gets the Unix milliseconds of the epoch. This is the time measured (in milliseconds) from 1st January 1970. Was 1749573646748 just a few seconds ago…

If we divide this by the number of milliseconds in a day, and then floor this rather than round it, we get the number of whole days since 1st January 1970.

At the moment, based on UTC time, this turns out to be 20249 days.

The simple %2 takes the modulus - that is the % operator. This divides by the given figure (2) and returns the remainder (1).

Hence 20249%2.

As a basic guide, the modulus remainder will only be 0 when the integer is an exact multiple of the modulus number we are using.

X%0 is division by 0, so will return null in JSONata.
X%1 will always return 0, since every number is exactly divisible by 1.

Today, being 20249 days since the Unix Epoch, just happens to be interesting because 20249 is prime, hence the only numbers that divide into it are 1 and 20249. This means that no other numbers will return zero. You just have to wait until tomorrow…

Put another way, if you start counting today, and then look at every second day you get the even numbers. If you also count every third day you get those, but the only day that is both even and divisible by 3 is 6. Since 17 is prime, on the 17th day, nothing in 1, 2, 3, 4, 5, 6, 7 is a factor of 17.

Using the day of the year may seem a good approach, however since that is 365 or 366, it has a discontinuity when looking, for example, at every 7th day when you cross into a new year. The factors of 365 are only 1, 5, 73, 365, hence only 5 divides neatly, and that does not work on a leap year. Better to use a monotonically increasing number, such as the Unix Epoch.

And to close the number logic. the lowest common multiple of 2, 3, 4, 5, 6 and 7 is 420. This means you have to wait 420 days to next have a day when nothing everything waters…

1 Like

Damn dude, I probably dind’t get such an informative and good math explenation since finishing my studies. Definitely learned something today - thanks so much!

Using epoch to avoid problems when switching years is very elegant - althougt I will not be irrigating my plants in winter anyway.

But man - the chance of us having this conversation on such a high prime number day since Unix epoch is ridiculous!

Thank you so much again for all this - my garden will flower because of and for you!

Yes I had to ask Google for the factors of 20249, and a fair degree of head scratching on this one, but it certainly is an auspicious day.