A praise for JSR223 with Jython (highly recommended)

I am trying my first jython scripts and want stop the thread for 1.5 seconds. When using

time.sleep( 1.5)

I am receiving the following error messages.

2016-04-29 19:49:37.570 [ERROR] [.c.j.i.e.RuleExecutionRunnable] - Error while executing rule: org.python.proxies.builtin$Martin_kommt_nach_hause$107@1bdf00
org.python.core.PyException: null
at org.python.core.Py.NameError(Py.java:284) ~[jython.jar:na]
at org.python.core.PyFrame.getglobal(PyFrame.java:265) ~[jython.jar:na]
at org.python.pycode.pyx37.execute$11(:65) ~[na:na]
at org.python.pycode.pyx37.call_function() ~[na:na]
at org.python.core.PyTableCode.call(PyTableCode.java:167) ~[jython.jar:na]
at org.python.core.PyBaseCode.call(PyBaseCode.java:307) ~[jython.jar:na]
at org.python.core.PyBaseCode.call(PyBaseCode.java:198) ~[jython.jar:na]
at org.python.core.PyFunction.call(PyFunction.java:482) ~[jython.jar:na]
at org.python.core.PyMethod.instancemethod___call
(PyMethod.java:237) ~[jython.jar:na]
at org.python.core.PyMethod.call(PyMethod.java:228) ~[jython.jar:na]
at org.python.core.PyMethod.call(PyMethod.java:218) ~[jython.jar:na]
at org.python.core.PyMethod.call(PyMethod.java:213) ~[jython.jar:na]
at org.python.core.PyObject._jcallexc(PyObject.java:3626) ~[jython.jar:na]
at org.python.core.PyObject._jcall(PyObject.java:3658) ~[jython.jar:na]
at org.python.proxies.builtin$Martin_kommt_nach_hause$107.execute(Unknown Source) ~[na:na]
at org.openhab.core.jsr223.internal.engine.RuleExecutionRunnable.run(RuleExecutionRunnable.java:36) ~[na:na]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_73]

That is the code:
class Martin_kommt_nach_hause(Rule):

def getEventTrigger(self):
    return [
        ChangedEventTrigger("Presence_Martin")
    ]

def execute(self,event):
    oh.logInfo("Martin_kommt_nach_hause","hallo")
    item = event.item
    oh.logInfo("Event :", str(ItemRegistry.getItem("Presence_Martin").state))
    oh.logInfo("Event :", str(item.state))    
    if str( event.newState) == "OFF":
        oh.sendCommand("Command_2_speech", "Martin hat das Haus verlassen")
        oh.sendCommand("Home_big_vorkurzem", "OFF")

    if str( event.newState) == "ON":
        #oh.sendCommand("Martin_home_time", new DateTimeType())
        oh.sendCommand("Command_2_speech", "Martin kommt gleich")

    time.sleep( 1.5)

The previous message is somehow not displaying completely.
I have no idea why.

I can only guess, but are you importing the time module?

Thanks. That was the solution.
My first 4 rules are working now :smile:

Maybe if I could ask another question.
I always had the statement

postUpdate(Martin_home_time, new DateTimeType())

to store the moment something has happend.
How would this be translated in Jython?

And maybe a second question:
How am I canceling a timer?

In general I am struggling quite a bit with the lack of documentation and examples and also I did not find the source code of the jsr233 addon. The syntax to interact with the specific openhab classes therefore is not clear for me. For example, is it possible to create a trigger that triggers if a certain state is reached.
the equivalent to

when
Item Presence_Martin_big changed to OFF
then

Could you provide a link to the github of the source?
Maybe I will find some questions to my answers there.

@martin_klimke I’m going to create another topic where we can discuss JSR223 Jython usage.

1 Like

Thanks for answering my questions and taking your time.

Hello,

I use the JSR223 also. But with groovy. This is a really great feature.

The only problem I saw was when I had a old rules with jsr223 rule at the same time. Openhab had really weird behaviour. When I switch all my rule to jsr223 it fix the problem.

Now I would love to see application rule like the one in smartthings.

Joel

I have no problem mixing rules.
Currently half of my rules are in jython and half of them are still in the old engine.

But the best part is, that now I have always deterministic behavior.
In my sleeping room is a button which I don’t push that often.
Sometimes the light would go on almost immediately, sometimes after a couple of seconds.
The delay was long enough to keep me wondering if I pressed the button correctly.

I switched the rule to jython today. Really fast response on the first press.
No more wondering! This is so awesome!

1 Like

I have meanwhile converted about 10 rules.
The learning curve has been steap but after this I can benefit from the power of a well documented language overcoming of the weaknesses of the Openhab DSL.

Hope that this approach will be continued in openhab 2.
Jython is awesome and really great.
Everybody here should at least give it a try.

Thanks to everybody who contributed to this valuable enhancement.

1 Like

I had a lot of frustration with xtext based rules since openhab seems to pose its own limitations on top of the “documented” xtext. For example, lambdas might not be able to refer to other variables (e.g. functions/lambdas) – except in the rules directly. This really limited the usability of the language for me.

Decided eventually bite the bullet and start to use JSR223. I decided to go with the built-in javascript as that seems to be 1) well developed & maintained 2) commonly used language 3) bring minimal additional dependencies, 4) I have some doubts over Jython**.

In any case, I really enjoyed the extra power you get from the language. I can’t stress enough how much smaller and easier the rule code base becomes with the JSR language.

I would like to get feedback on the packaging of JSR. I’m running openhab on raspberry pi and the official debian packages. I found it a bit weird that (installed) files need to be modified in order to have JSR->Java imports to work (see wiki on -Dorg.osgi.framework.bundle.parent=ext flag). Shouldn’t this be part of the JSR deb installation process? What do you think?

**) Even though I’m really a python fan, not really fan of jython as it seems to develop very slowly (Python version has been behind the CPython a long time, 2.7.0 vs 2.7.10, not even mentioning ever-growing trend to move to python3) and frankly speaking I’m a bit concerned over the future development.

By this do you mean that named lambdas (i.e. lambdas defined as global vals)? This isn’t so much as a limitation imposed by the Rules DSL as a side effect of how the Rules DSL is implemented. I think there were design decisions early on that assumed there wouldn’t be lambdas like that. The root of the problem is that when the global vars and vals are loaded into memory there is no context for the lambda to inherit (like there is when you create a lambda when calling createTimer). Because of this the named lambdas are unable to access global vars and vals which includes other lambdas.

You can pass lambdas and other global vars and vals into a named lambda as an argument, but that is admittedly a less than satisfactory solution, particularly given the limitation of 7 arguments.

The fact that you find this to be the case makes me think you were fighting against the Rules DSL syntax. I’ve found Rules DSL code can be very concise and flexible when approached in certain ways (i.e. bend the tool rather than trying to bend the tool to you). I’m not trying to convince you to switch back or anything, just providing a counterpoint. When I first started out I had lambdas everywhere and hacked together storing a bunch of state in HashMaps and such. My rules where thousands of lines of code, brittle, and ugly. Then I learned some design patterns and approaches that fit better with the Rules DSL and dropped my LOC to less than half and eliminated all but one of my lambdas. And the code does more and is way more flexible.

The Rules DSL gets a lot of bad press, primarily from experienced coders who come to it and try to code in it as if it were a procedural or OO type language as opposed to an event driven language. Frankly, based on my experience, the fact that you want to create lambdas that call lambdas is a code smell in the Rules DSL. It shouldn’t be necessary.

There are lots of advantages to Jython though, particularly if this is how you prefer to code or you end up in the rare situation where you really do need the code to run really really fast.

I do agree. However OH 1 is kind of end of life with most of the focus on getting OH 2 completed and fully released. In OH 2 I’m pretty certain that this edit is no longer required. Beyond that, eventually OH 2 will support the ability to code in a greatly expanded set of languages (e.g. Lua) natively so there won’t even be the need for the the binding.

All that being said, if you are willing to take it upon yourself to update the apt script to sed that change into the start script or whatever I’m sure the PR will be accepted.

Do you have a pointer to more information about this? What do you mean by “native”? Do you mean JNI instead of JSR223? One of the advantages of Jython is the tight integration with Java. The integration would be much less capable using a JNI binding to CPython.

As an aside, I’ve tried several JSR223 script engines (JLua, JRuby, Armed Bear Common Lisp, …) and the only ones I found that have have the features to support openHAB JSR223 are Nashorn (Javascript), Jython and Groovy.

That configuration is specific to the Nashorn script engine. It’s not required by Jython and I’d want to be sure it doesn’t cause issues with it. Jython needs a whole different set of startup script changes.

There were a couple threads several months back where @kai and @watou discussed some plans for how OH 2 would support the new Rules Engine and how it would make the JSR233 capability a part of the core instead of a binding. That is what I mean by native, as in native to OH core, not JNI.

My knowledge on the topic is probably getting dated. I also have no idea if it is an ESH thing or an OH 2 enhancement.

Ah, ok. Interesting. Currently it’s a binding… in the OH1 core packages. So, it’s a little of both. In any case, there will still need to be some thought in an OH2 context about how to configure the JVM in the startup scripts for specific JSR223 languages.

It will be built into the new rule engine, the discussion is right here.

By this do you mean that named lambdas (i.e. lambdas defined as global vals)? This isn’t so much as a limitation imposed by the Rules DSL as a side effect of how the Rules DSL is implemented. I think there were design decisions early on that assumed there wouldn’t be lambdas like that. The root of the problem is that when the global vars and vals are loaded into memory there is no context for the lambda to inherit (like there is when you create a lambda when calling createTimer). Because of this the named lambdas are unable to access global vars and vals which includes other lambdas.

You can pass lambdas and other global vars and vals into a named lambda as an argument, but that is admittedly a less than satisfactory solution, particularly given the limitation of 7 arguments.

Yeah that’s what I was referring to exactly.

The Rules DSL gets a lot of bad press, primarily from experienced coders who come to it and try to code in it as if it were a procedural or OO type language as opposed to an event driven language. Frankly, based on my experience, the fact that you want to create lambdas that call lambdas is a code smell in the Rules DSL. It shouldn’t be necessary.

You might be indeed right that there is way to structure the code differently to workaround the issue. For me it is natural to write a function, e.g. from item name to some another item name, Temp3 -> Temp3Valid if the same operation is called in different contexts. In my use case I was calling this simple function/lambda from another lambda. This lambda is then called from different rules having different triggers.

so basically I have the following scenario

val func1 = ... // function that calls helper function func2 val func2 = ... // helper function func2

rule "rule1" when ...some triggers... then func1(params) end

rule "rule2" when ...some triggers... then func1(params) end

Sounds like great things are upcoming in openhab2! Hopefully we will have the scripting functionality supported in OH2 as well.

All that being said, if you are willing to take it upon yourself to update the apt script to sed that change into the start script or whatever I’m sure the PR will be accepted.

I did not realize actually that this is related only to javascript scripting – might be wiser to wait for the OH2 to become reality :slight_smile:

Just to continue the discussion for the sake of discussion. When I’m faced with a situation similar to what you describe my approach is to merge rule1 and rule2 into a single rule, add some logic to determine which logic is appropriate, implement funct1 in the rule and if it is still necessary call func2 from there.

Usually when I have a situation where I had separate rules like this that do the same thing it is because the Rules DSL does not have a straightforward way to determine which Item generated the event that triggered the rule. However, with persistence and Groups you can determine which Item caused the event under most circumstances (see the first two lines of the Override Lights rule on this posting):

However, I’ve found that in many circumstances I can adjust the code such that I don’t even have to know or care which Item triggered the rule and still merge the rules.

1 Like

Sounds like you’ve been figuring out these :slight_smile: Great to have some feedback and get the common usage issues/solutions documented! I do get your point and surely groups can help in many cases.

After looking at the code now, my case was actually a bit different than explained previously. Not sure if it should be opened in this topic but I do understand that basically everything can be made to work with Xtext with bit of re-organization. Certainly there are ways to go around these issues but did not really see it worth the effort as there was JSR approach available around the corner. Also I’m worried that some of the changes might be sacrificing readibility “just to make it work”.

You are right that the programming background might actually make things worse. For example, I would like see the actual triggers as shallow wrappers calling “library” of re-usable functions. With this approach it is easy to follow the “main story” of the triggers themselves as they would be just calling other functions.

Sounds like we are really close to OH2 having JSR scripting, looking forward experimenting with that!

Best,
Sami

I can see this but in my experience I find my updated code to be more readable rather than less readable. However, that is primarily because the little lightbulb went off in my head so I expect to see it coded in this way now and when I see a more procedural approach being used I find that harder to read.

It could just be me. Though I will admit that the hack I use to get the Item that triggered the event is just that, a hack and not at all satisfactory. But I keep it around because it really really helps cut down and simplify my code.

An interesting idea. I’m not sure what I think about that. I’ve not seen that approach tried in other similar event driven programming environments I’ve used (e.g. Tasker, Asterisk, HomeGeni) so I don’t know if no one has thought of it or if there is some technical reason why it doesn’t work well.

I am pretty sure that would not be possible in the rules engine as it is currently written.

I personally kind of like having the rules triggers and the rules logic together as it helps to self document the context of the logic. With a separation like that I the context involved when the logic is called is not as apparent. Though if they are generic the context shouldn’t matter that much. Then again, in my entire configuration I have only three situations where the logic is ever going to be anything but specific to one event.

Something to ponder.

This wouldn’t be the first thread that drifted to some other topic. If you want to pursue it you can do it here, open a new thread, or send me a PM. I enjoy looking at other people’s code and recommending improvements.

1 Like

Got my first rules running :slight_smile:

However why do i get the following errors

-bash:  -Dpython.home=/home/openhabian/jython2.7.0: No such file or directory

or

/usr/bin/openhab-cli: 27: /etc/default/openhab2: -Dpython.home=/home/openhabian/jython2.7.0: not found

for example when opening an ssh connection to my openhabian raspi or opening a connection to the karaf console? The folder is existing since my rules are working.

Any clue?