[Solved] Cron from DateTime Item

Hi folks,

I want a rule to fire up by a given time. Normally I would use a cron to start the rule, but the start time varies because it depends on another DateTime Item.

After thinking about different ways with no luck I found this article about jsr223 scripting which I dont really understand.

But it sounds like jsr223 is able to create a rule out of a script?!??

And I thought maybe one is able to help writing a little script which takes the state of an DateTime Item as Cron and creates a Rule filled with this Data to Switch an other Item… I know it sounds a bit weird and I don’t even know Iif it would work this way, because I dont know java at all, but maybe you understand what I’m trying to achive.

The script should:

  • wait for an Switch Item to fire up
  • take the DateTime string of another Item …lets name it CronTimeData
  • create a Rule with a cron
  • fill the cron from CronTimeData
  • switch an Item when cron launches …lets name it AutoCronSwitch

If it works like this, one could launch any other Rule to fire up when AutoCronSwitch changes.

Any creative thoughts about this?

Cheers

Look at the several “alarm clock” postings.
Set up a rule to run when the target-time Item changes.
This rule creates a timer that goes off when at the target-time.
You can either do whatever it is you wanted in the timer code, or change a dummy Item so that the dummy changing fires other rule(s).

I’m sure I already posted something like this, but I couldn’t find it. I’ll add this one to the helper library docs… Helper Libraries for openHAB Scripted Automation — openHAB Helper Libraries documentation. This will create a rule that watches for changes in the DatetTmeItem and reschedules a cron rule when it changes. BTW, forget the official scripted automation documentation… it is very out of date. I plan to update them when/if my add-ons are merged.

from core.rules import rule
from core.triggers import when


@rule("Monitor DateTime Item for changes")
@when("Item Virtual_DateTime_1 changed")
@when("System started")
def monitor_datetime_changes(event):
    zoneddatetime = items["Virtual_DateTime_1"].zonedDateTime
    cron_expression = "{} {} {} {} {} ? {}".format(zoneddatetime.getSecond(), zoneddatetime.getMinute(), zoneddatetime.getHour(), zoneddatetime.getDayOfMonth(), zoneddatetime.getMonthValue(), zoneddatetime.getYear())
    monitor_datetime_changes.log.info("Creating cron rule from DateTimeType: Virtual_DateTime_1: {}, cron_expression: {}".format(items["Virtual_DateTime_1"], cron_expression))
    if event:# the existing rule needs to be removed or its trigger needs to be changed (removing is easier)
        # event will only be None when the trigger is 'System started'
        scriptExtension.importPreset("RuleSupport")
        ruleRegistry.remove(monitor_datetime_changes.cron_uid)
    @rule("Cron rule based on DateTime Item")
    @when("Time cron " + cron_expression)
    def cron_example(event):
        cron_example.log.warn("Cron triggered")
    monitor_datetime_changes.cron_uid = cron_example.UID

For a simple installation of Jython and the helper libraries, I have created an add-on…

Let me know if you have any other questions! Oh… and for beer donations, click here! :beers:

1 Like

Ok guys,

@rossko57 I asked for a solution because I want to avoid timers because they’re unreliable in my eyes - every hickup or restart will kill the timer… the timer needs to run for 8.5 hours in my case. Thanks anyway or do you think about timers in jsr223??

@5iver
Thanks for this quick and (i think :wink: ) usable reply… I’ll have to get deeper into jsr223 / jython thing… first of all I’ll try to install jython and get everything running. I’ll report back when I got so far…
I have to ask one question, what would be the file extension of your script and where do these go to be executed? Thanks for your help so far…

A bit offtopic: Will jython scripts become a part of openhab core or is this just a workaround? I ask because if I would be able to quit this nasty xtend crap I would go with a new way of writing rules but I don’t want to put any effort in jython before it is clear that it stays with openhab.

thanx and have a nice day

It’s a timing function. Whether the machinery is hidden behind the scenes or exposed in front, it’s the same timing function.

You can write code to re-establish timers at every restart. This is exactly what features like Astro do for setting up a “dawn” event and the like. This exactly what the framework itself does when you have a (fixed) cron rule.

yes but how would one tell the new timer how many time was left when the timer accidentially has been killed - I think the jsr223 scripting is a much better solution because the rule script will be executed every restart…

Exactly the same way you set it up in the first place. Interpret your target time Item. Although I’m not really sure what you think is going to go around and accidentally kill timers?

thanks @rossko57 but I think it is much more effort to program this workaround for an unreliable timer instead of using a reliable cron rule script to do the job.

I am using timers all the time if they are running for max. 30mins. and sometimes they dont fire up because of a java exception caused by timers - if I can avoid them I use cron rules - but in this special situation where the start time is a variable this won’t work out of the box with the xtend crap…

Sorry if I don’t match up with your opinion.

Have a nice day anyway

Hi I’m back…

what I got so far:

  • backed up my openhab lxc-container
  • updated to openHAB 2.5.8 Release Build
  • installed Rule engine via PaperUI
  • managed to copy the jar to the /addons folder
  • downloaded and copied the automation folder
  • renamed the mentioned files in /automation/lib/

by the way which owner, group and privilegues should I use on these files?
openhab:openhab or root?
what I don’t understand is 10: Add/modify the EXTRA_JAVA_OPTS
where should I do that?

thanks,
Dan

I’ve tested this, so I know it works. Just replace Virtual_DateTime_1 with your DateTimeItem.

This should be answered here… File Locations — openHAB Helper Libraries documentation.

No, Jython and the helper libraries will not be included in OHC. There are many different ScriptEngineFactories for scripting languages that can be used, they tend to be large in size, and they are not part of the framework, so they fit best as extensions. IMO, the rule engines should also be extensions, and they have no place in OHC.

Scripted automation is definitely not a work around… it is included in the new rule engine, which is currently the only rule engine in OH 3.0. I’ve submitted a PR for a Jython extension and have Groovy, Kotlin, and jRuby queued up for after they have been approved, and there is an update JS extension someone else is working on. Scripted automation provides MUCH more functionality than the old proprietary rules DSL (Jython, scripted automation, jsr223)! If you plan to upgrade to OH 3.0, the rules DSL has been grafted onto the new rules engine, so there is no telling what will break or will have to be rewritten in your rules. The only reason to use the rules DSL is because of the incredible community support, but this is shifting to scripted automation, Jython, and the helper libraries.

They need to be able to be executable by the account that runs openHAB. This wil be openhab, unless you have changed it.

When using the add-on, you don’t need to add anything to EXTRA_JAVA_OPTS, since this is taken care of for you. You also do not need anything under /automation/lib/python/ except for the configuration.py.

thanks this makes it a bit clearer for me - was a little confusing first
I reply when I get it to work…

thanks a lot

1 Like

Hi Scott,

I think I got it working now…

2020-08-30 19:48:13.673 [INFO ] [me.core.service.AbstractWatchService] - Loading script 'python/personal/0050_daystart_rule.py'

One last question, If the script should trigger a dummy Item, what would be the syntax to do so and where should it go? I guess somewhere here:

def cron_example(event):
------>>>> ??
        cron_example.log.warn("Cron triggered")
    monitor_datetime_changes.cron_uid = cron_example.UID

thanks for all your effort on this, I think I like it, but I have to get deeper into it…

its good to know that scripted rules are included to the new rule engine, now it makes more sence to learn more python / jython language.

have a nice day

Your guess is correct (somewhere in the cron_example function). Updating and sending commands to Items is covered here…

But How Do I…? — openHAB Helper Libraries documentation.

1 Like

You have come to a solution so I don’t have much to offer except there recently was a post of a tutorial showing how to enter DateTimes in a more reasonable manner than the Alarm Clock approaches that require a separate Item for each part of the time which might be useful SchreibWas II - Using HTML5 Input-Type in OpenHAB Basic-UI - good or not.

I’m not sure what brings you to this conclusion. Timers are very reliable in my relatively extensive experience. In fact, I’ve replaced cron triggers with Timers for my latest implementation of time of day in Jython. You may not know this, but I believe Timers and cron triggered rules are actually managed by the same code. The only gotcha is you are responsible for some of the book keeping to:

  • make sure timers are cancelled when the script is unloaded; in Jython define a scriptUnloaded function and cancel the timers there; in Rules DSL you just have to live with the errors
  • make sure to recreate the timers when the script is loaded; use a System started trigger on the same rule that triggers on a change to your datetime Items.

You can see an additional example at https://github.com/rkoshak/openhab-rules-tools/tree/main/ephem_tod.

The timers in that code have been running for up to 24 hours every day for many months. I’ve never missed an event yet.

Just to make it clear, when you schedule a timer you give it an exact date and time to trigger. That’s why in all the alarm clock examples you have to reschedule the timers around midnight for the upcoming day. Timers are not relative so you don’t have to figure out how much time is left. You just need to recalculate the exact date and time the timer should run just like you do when the alarm Items change.

If you were in a situation where you would need to figure out how much time is left, your best bet is to create an Item that gets persisted and restored on startup. Keep that Item up to date with how much time is remaining, or set a timestamp for when the activity started or the like. Since the Item is persisted when OH comes back online check that Item and you can calculate how much time has passed.

This can be particularly use in cases like handling the edge case where you are irrigating some plants, OH goes offline and comes back online and wants to take into account the time that OH was offline when calculating when it’s time to turn off the irrigation. But for a case like this, you don’t care how much time has passed because you are calculating an exact time date time and not a relative date time. Therefore it will come up with the same date time even if it recalculates it on a reload as long as the source data (i.e. alarm clock Items) haven’t changed.

If you reload your .rules file while a Timer is scheduled, that Timer becomes orphaned and generates an error in the logs. Otherwise the lambda inside the Timer is throwing an exception and moving that logic to a cron triggered rule is not going to make the error go away. In short, the behavior you are seeing isn’t caused by the timers, it’s caused by something else. Timers are very reliable.

Hi Rich,

My Server is restarting weekly, and my lxc openhab container gets reloaded every night. Maybe Timers are more reliable than I believe - but these exceptions caused by whatever happened… I didn’t know that timers can have an exact date and time to trigger. the only timers I used run for a specific amount of time, not until a given time.
So if OH restarts or the timer hangs I’m not able to say how much time is left.

Some kind of misunderstanding…
So I think we both are right in our way of looking at it.

But I think if you compare a timer that runs for 8.5h with a rule with a hardcoded start time, the rule would be easier to handle in cases of a system restart or any hickup.

cheers Dan

Assuming that it is current 2020:08:31T14:48:00.000, when you call now.plusMinutes(5) you are getting a DateTime that represents exactly 2020:08:31T14:53:00.000.

Anything that uses now.plusX is calculating an exact point in time. It’s effectively calculating the exact millisecond in the future that will be plusX from now.

In the alarm clock examples, you may see something like now.withTimeAtStartOfDay.plusHours(H).plusMinutes(M). In that case the withTimeAtStartOfDay takes now to midnight today, the plusHours adds H hours from midnight today and the plusMinutes adds M minutes to that to get an exact hour and minute for today.

Alternatively you might see DateTime(H, M, 0) which creates a new DateTime with today for the date and H for the hours and M for minutes and 0 for the seconds. But again, you end up with an exact point in time for today.

It is actually impossible to schedule a Timer with anything but an exact point in time when it’s to run.

Again, this is why you need to recreate the timers every day. Also note that cron triggered rules do the exact same thing. It looks at the cron expression and creates a Timer to run the rule at the time represented by the expression. It’s the exact same code that handles both.

Depends on your definition of easier to handle. If you are looking to have a rule to create a rule with a cron expression as described in the OP than no, I do not agree at all. It’s far easier to create a scriptUnloaded function to cancel your Timers and either put in a scriptLoaded function or a System started triggered rule the recreation of the Timers. System started is particularly useful for cases where the time is defined by an Item or Items as the exact same rule that runs when the Item(s) changes can be triggered by a System started trigger and you are done. Restarts are automatically handled with no effort at all.

The amount of time the Timer is scheduled for is irrelevant. I have Timers that live for days at a time and never fail to trigger.

The only thing that is easier to handle is if you in fact do have a cron triggered rule with a hard coded start time. In that case it’s easier because you don’t have to do any of the book keeping to make sure the Timer get’s cancelled and recreated properly. But that isn’t what you were asking about in the OP. You were asking for a way to create a rule with a cron trigger that is based on the value of some Item. You’ve replaced a bunch of relatively easy (and with some of the libraries I and others have written becoming even easier) with an bunch of complicated rule management. At best you’ve replaced one set of complicated stuff with another set of complicated stuff without actually making anything simpler.

I bet you will find that if you move to Jython and make sure to write a scriptUnloaded function for all your modules where Timers are created to cancel all the running Timers you will find your errors (and therefore misimpression that they are unreliable) go away.

Sorry Rich,

english is not my native language, and I think this discussion is going nowhere…

I simply said that using this script above is easier to handle in my eyes, because I don’t have to take care of daychanges or anything else - put it in the folder and you’re good to go.

Thanks for all your explanation that makes things clearer, but don’t changes my mind at this time…

btw I don’t like this xtend-based rule management for its unflexibility. this workaround to use items to have global variables is a pain… just to name one… .but I think this behavior is the same using jython.

If I could use something more flexible I would go with it… Until now I know nearly nothing about this jython thing but It looks way more promising than the old way of programming rules in my opinion (as far as I can see for now)…

call me ignorant but that’s what I think.

cheers Dan

Using Jython, you have many options for storing data that can be accessed elsewhere in the script, other scripts, modules, and rules. You have global variables, ScriptExtensions, modules, attributes, etc. You can even store data to your file system using Jython or using Java through Jython. You can use Jython to save data to Java properties, or even to the OH JSONDB. There are a lot of options!

But to which behavior are you specifically referring? If you want to access to the data through an OH UI, you’re still going to need to use an Item :slightly_smiling_face:.

1 Like

Thanks good to know… was busy the last days, sry for the late reply

I have no special behaviors in mind yet… just wanted to mention that xtend-based rules are lacking many features one has in other (script-)languages…

cheers

1 Like