Timer and date calculations in rules -- need help

In OH1 I have a timer that automatically switches off the lights at night when we are on vacation.

I used to calculate a random time “Off” of 30 minutes after 11pm with this statement:

var DateTime timeOff = parse(""+now.toLocalDate.toDateTimeAtStartOfDay).plusHours(23).plusMinutes((Math::random*30).intValue)
logInfo("vacation", timeOff)

The logInfo returns this error:

2017-01-09 07:09:26.023 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'VacationOff': An error occured during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.LogAction.logInfo(java.lang.String,java.lang.String,java.lang.Object[]) on instance: null

What has changed in OH2 that this no longer works?

I think you have an errant paren. Shouldn’t it be:
var DateTime timeOff = parse(""+now.toLocalDate.toDateTimeAtStartOfDay.plusHours(23).plusMinutes((Math::random*30).intValue))

And given that toDateTimeAtStartOfDay is deprecated, now is already a DateTime, and the result from plusMinutes is already a DateTime you probably simply want:

var DateTime timeOff = now.withTimeAtStartOfDay.plusHours(23).plusMinutes((Math::random*30).intValue)

There should have been no changes that make this not work. What you posted should have generated errors in OH 1.8 as well as written.

Thanks for the reply. I Will gave this a try asap.

I did some testing, but it isn’t working out.

I have now separated the code. Have this rules file:

test.rules

/* test.rules */

rule "TestNow"
when
	Time cron "*/15 * * * * ?"
then

	logInfo("TestNow", ">>> START")

	var DateTime dt1 = now
	logInfo("TestNow", ">>> 1")
	logInfo("TestNow", "" + dt1)

	var DateTime dt2 = now.withTimeAtStartOfDay
	logInfo("TestNow", ">>> 2")
	logInfo("TestNow", "" + dt2)

	logInfo("TestNow", ">>> END")

end

which shows this logging:

2017-01-10 20:27:36.219 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'test.rules'
2017-01-10 20:27:45.564 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> START
2017-01-10 20:27:45.570 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> 1
2017-01-10 20:27:45.577 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.ObjectExtensions.operator_plus(java.lang.Object,java.lang.String) on instance: null
2017-01-10 20:28:00.022 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> START
2017-01-10 20:28:00.030 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> 1
2017-01-10 20:28:00.040 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.ObjectExtensions.operator_plus(java.lang.Object,java.lang.String) on instance: null

When I change

	logInfo("TestNow", "" + dt1)

to

	logInfo("TestNow", "" + dt1.toString)

I get this logging:

2017-01-10 20:32:30.020 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: The name '<XFeatureCallImplCustom>.toString' cannot be resolved to an item or type.

What happens if you change this to

logInfo("TestNow", "{}", dt1)

I believe you can also use dt1.state. Had the same problem and if I remember right that was my issue.

	logInfo("TestNow", "" + dt1.state)

doesn’t work eiter:

2017-01-10 21:59:15.034 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: The name '<XFeatureCallImplCustom>.state' cannot be resolved to an item or type.

(dt1 isnt’ an Item)

Did that and that seems to work

	logInfo("TestNow", "{}", dt1)

produces this output:

2017-01-10 22:01:15.013 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> START
2017-01-10 22:01:15.017 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> 1
2017-01-10 22:01:15.021 [INFO ] [lipse.smarthome.model.script.TestNow] - 2017-01-10T22:01:15.015+01:00
2017-01-10 22:01:15.026 [INFO ] [lipse.smarthome.model.script.TestNow] - >>> 2
2017-01-10 22:01:15.032 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: The name '<XFeatureCallImplCustom>.toString' cannot be resolved to an item or type.

==> Could you please explain why this is actually working (to learn from you)?

This is also not working:

rule "TestNow"
when
	Time cron "*/10 * * * * ?"
then

	var DateTime dt1 = now
	var DateTime dt2 = now.withTimeAtStartOfDay
	
	if (dt1 == dt2) {
		logInfo("TestNow", "EQ")
	} else {
		logInfo(TestNow", "DIFF")
	}

end
2017-01-10 22:59:25.018 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule TestNow: An error occured during the script execution: The name '<XFeatureCallImplCustom> == <XFeatureCallImplCustom>' cannot be resolved to an item or type.

Have I ran into some kind of bug?

I did an explicit import in the rules file that seemed to do the trick:

import org.joda.time.DateTime

@rlkoshak @watou Can you please confirm this is necessary and/or is the actual solution?

Not a bug.

DateTime is a complicated type and complicated types rarely work with ==. Try

if(dt1.equals(dt2))

It’s a long explanation which I won’t go into here. Suffice to say it is what many consider to be a design flaw in Java and therefore a design flaw inherited by the Rules DSL which runs on Java.

For a rule of thumb, you can only use == for:

  • Number
  • BigDecimal
  • primitives
  • Strings
  • Boolean
  • Enums (e.g ON, OFF, OPEN, CLOSED)

All other types such as DateTime must either be converted to one of the above to use == or you must use the .equals method instead.

As to the logInfo statement, @watou provided a way to call logInfo that has the smarts to figure out how to extract the String representation of dt1 and dt2.

I suspect the following would have worked as well:

logInfo("TestNow", dt1.toString)

Interesting…

I was under the impression that all the Joda DateTime stuff was imported by default so you wouldn’t need that import. This implies that is incorrect and there is something that is being missed (mainly the Operator Equals) by the default import.

I would indeed think this is a bug, probably on ESH.

Filed issue. Time for bed now. :slight_smile:

1 Like

The rule language is a close cousin of the Xtend language, which is syntactically similar to Java, but == handling is different. This section says the following:

Defined Operators in The Library

Xtend offers operators for common types from the JDK.

Equality Operators

In Xtend the equals operators (

==
!=

) are bound to

Object.equals

. So you can write:

if (name == 'Homer')
    println('Hi Homer')

Java’s identity equals semantic is mapped to the tripple-equals operators

===
!==

in Xtend.

It’s normally supposed to, but there appeared to be some problem with resolving the toString method. My workaround assumed that the rule engine would at least pass the dt1 Object to the logInfo method, where the code therein would not have a problem calling dt1’s .toString() method, since all Java objects have a .toString() method.

A new whack at clear rule language documentation is definitely in order! :page_with_curl: :page_with_curl::page_with_curl:

That is very interesting because in practice I’ve never found this behavior to actually be the case in the Rule’s DSL. I just assumed that, like Classes and arrays that was one of the things that the Rules DSL didn’t implement the same way. Perhaps I was doing something else weirdly that got fixed when I started calling .equals explicitly or comparing the toStrings instead of the base objects directly.

I completely agree. Its just so big I don’t know where to start. Plus it isn’t clear to me what the longevity of the current Rules DSL is. With the Experimental Rules engine and the effort to port JSR233 to OH 2, is it worth the effort?

There are also a lot of little things like the “{}” notation on logInfo that just seemed to sneak into the language without my noticing. It still looks odd to me. Though that could be a result of being taught to use cout >> rather than printf back when dinosaurs roamed the mainframes.

From the Extend doc, it may be that some java classes have .operator_equals() adapters while others don’t, which is of course a minefield if true. I like the rules engine, but its fatal flaw is a lack of comprehensive documentation. I would rather use Lua 5.1 as a rule language for the simple reason is that it is thoroughly documented and users of it don’t have to handle data types. (Of course it’s missing lots of things by comparison, but my point is that a fully documented language is so much more valuable in practice than one that only works by understanding working examples.)

A much better place to be than the current situation is be completely out of the language documentation business, instead only documenting the finite context (built-in classes, objects and methods) supplied to any language, still supporting the old language but shifting focus to a small set of stable, well known languages that fully support JSR233. So any question about how to do something in the action of a rule is usually answered by a external link to the definitive language reference. So people could use JavaScript, Python, Groovy, and even Lua, and all we have to document is what context is supplied.

Depends on the difficulty of finding the complete set of answers to the current unknowns! Even the Xtend documentation itself is woefully incomplete, so it’s probably a big job. If resources could be put into making the new rule engine solid, easy to use, and extendable with any JSR233-enabled language, so many issues could be solved at once.

It’s not actually a language feature; it’s a feature of the logging API that’s exposed via the built-in logging actions. And yes, it does look odd, but then again, if dyed-in-the-wool Java programmers were thrust into Lua programming, they would probably feel full-on nausea at first. Arrays begin indexing at 1 and not 0, for heaven’s sake, and such a thing might offend a senior developer’s sensibilities. But on the other hand, it’s friendlier to the complete novice, just like not having to know what data types are is very friendly. Obviously, one size definitely does not fit all.

1 Like

Sacrilege! :smiley: Seriously, I’ve actually read a lot about Lua and it does seem to be a good choice for this sort of application.

I completely agree with all of the above. Despite my defence of the Rules DSL on this forum, I fully recognize that having a custom language like this which is not fully documented is a major problem. I’d go even further to suggest that choosing a hybrid like language like Xtend to base it on was not the best choice. The relatively sparse documentation for Xtend/Xtext coupled with the weird interactions between Java and the DSL results in a mine field of subtle errors and special cases.

One thing that always really annoyed me is how the wiki points at the Xtend docs for the “language docs” but there are so many things that Xtend can do that the Rules DSL cannot (arrays, classes, for loops are different, etc) and the only way I was able to find them out was through experimentation. The lack of meaningful errors and the failure to pinpoint which line of code in the Rule generated the error is also a major problem.

There are a lot of things it gets right though and it is the default option for Rules development so I’ve stuck with it. Once I got over the the initial hurdles I find it a very well suited language for most HA purposes. But I agree, we would be in a lot better shape if we could get out of the language definition business entirely and limit our documentation to the framework/libraries we need to use from these other existing languages to interact with OH.

Despite my many going back and forth with others on this forum, I’d probably jump to Python in a second if I were sure that that rules engine were a first class citizen in the ecosystem. My Rules DSL experience is also probably where I provide the most added value on this forum so I’d have to take that into consideration as well.

I have my reservations with the new Experimental Rules Engine as I fear that we might end up in the same situation or we may end up with a Rules Engine that can’t do everything we can do now. I’m going to wait and see though.

2 Likes

Well, I know I am a newbie here, but have used a variety of languages and written a couple of DSLs myself.

Do we have a solid idea if the rules engine is going to be changing? Regardless, I would be prepared to invest some time to help the OpenHab 2.0 rule engine get documented with examples.

Let me know… :slight_smile:

1 Like

The Rules Engine is not going to change much if at all short of bug fixes and such. However, it will be joined by the currently Experimental rules Engine and JSR233 which allows one to write rules in Python, JS, and I think Groovy as well.

I’m sure we would all welcome anything you can contribute.