Port JSR223 bundle to openHAB 2

@steve1 You’re absolutely right. I should really pull the latest updates before trying to be too smart. :slight_smile:

Today I came across a thing that seems to be by-design and not a bug, but maybe someone else will stumble there as well and wonder why things don’t work as anticipated.

It seems the triggername (the last argument to the *Trigger functions in the libs provided by @steve1) must be unique, at least for one class (maybe across different rule classes, I didn’t test that yet).

Loading this rule:

from openhab.rules import rule, addRule
from openhab.triggers import ItemStateChangeTrigger

@rule
class Test(object):

    def getEventTriggers(self):
        return [ ItemStateChangeTrigger('dcTest', None, 'dcTest'),
                ItemStateChangeTrigger('dcEingang', None, 'dcTest')
         ]

    def execute(self,modules,inputs):
        self.log.info('Rule executed')

addRule(Test())

triggers these log mesages:

20-Aug-2017 12:08:37.855 [INFO ] [ript.rulesupport.internal.loader.ScriptFileWatcher] - Loading script 'triggerTest.py'
20-Aug-2017 12:08:37.970 [DEBUG] [org.eclipse.smarthome.automation.module.core      ] - ServiceEvent REGISTERED - {org.eclipse.smarthome.core.events.EventSubscriber}={event.topics=smarthome/items/dcTest/*, service.id=1273, service.bundleid=220, service.scope=singleton} - org.eclipse.smarthome.automation.module.core
20-Aug-2017 12:08:38.027 [DEBUG] [ript.rulesupport.internal.loader.ScriptFileWatcher] - Script loaded: triggerTest.py

There is just one Service Event registered.

However changing the trigger name to a different string:

from openhab.rules import rule, addRule
from openhab.triggers import ItemStateChangeTrigger

@rule
class Test(object):

    def getEventTriggers(self):
        return [ ItemStateChangeTrigger('dcTest', None, 'dcTest'),
                ItemStateChangeTrigger('dcEingang', None, 'dcEingang')
         ]

    def execute(self,modules,inputs):
        self.log.info('Rule executed')

addRule(Test())

generates these log messages:

20-Aug-2017 12:11:13.971 [INFO ] [ript.rulesupport.internal.loader.ScriptFileWatcher] - Loading script 'triggerTest.py'
20-Aug-2017 12:11:14.098 [DEBUG] [org.eclipse.smarthome.automation.module.core      ] - ServiceEvent REGISTERED - {org.eclipse.smarthome.core.events.EventSubscriber}={event.topics=smarthome/items/dcTest/*, service.id=1280, service.bundleid=220, service.scope=singleton} - org.eclipse.smarthome.automation.module.core
20-Aug-2017 12:11:14.149 [DEBUG] [org.eclipse.smarthome.automation.module.core      ] - ServiceEvent REGISTERED - {org.eclipse.smarthome.core.events.EventSubscriber}={event.topics=smarthome/items/dcEingang/*, service.id=1281, service.bundleid=220, service.scope=singleton} - org.eclipse.smarthome.automation.module.core
20-Aug-2017 12:11:14.192 [DEBUG] [ript.rulesupport.internal.loader.ScriptFileWatcher] - Script loaded: triggerTest.py

Now both triggers are registered as ServiceEvents.

Yes, that’s true, although I’m not completely sure if they must be unique globally or just within a specific rule or module handler factory. That’s why I generate a GUID by default to be safe either way. I may remove those trigger name arguments since it’s easy to run into this issue. I never use the names in my own rules. Was there a reason you were providing names for the triggers?

I use the trigger names for debugging purposes. Maybe the solution would be to prepend a user configured triggername to the generated GUID?

Has anybody tried to trigger on astro channel? How to write this rule from the astro docs with jython and @steve1’s lib?

rule "example trigger rule"
when
    Channel 'astro:sun:home:rise#event' triggered START 
then
...

This might help…

If someone creates a Jython trigger class to wrap the raw trigger, I’ll add it to my library.

I think it’s a good idea to start new specific topics for future questions. This topic is getting quite long.

There is a new trigger type in ESH that can be used now:

class ChannelEventTrigger(Trigger):
    def __init__(self, channelUID, event, triggerName=None):
        config = { "channelUID": channelUID }
        config["event"] = event
        Trigger.__init__(self, triggerName, "core.ChannelEventTrigger", Configuration(config))
        self.setLabel(triggerName)

Example:

  • channelUID: ‘astro:sun:local:nauticDusk#event’,
  • event: ‘START’

I use this here: https://github.com/schnidrig/openhab2-rollershutters

Wow! Your rollershutter automation is really impressive!

Has anyone figured out how to get the filename of the module?
__file__ does not work.

When you say module, do you mean script (which is not exactly the same thing)? I am seeing __file__ defined in modules. For scripts, the scriptLoaded function is called with the script’s file URI as the argument.

Hm - I might not have the proper wording. I am interested in the file that got loaded last. E.g. if I have a file “myrules1.py” this or the full path would be what I want.

Hello!

General question - what is the plan at the moment of this making it into OH stable?

Also, how would I be able to define an additional folder for jython?
In Openhab1 I could user the -Dpython.path="configurations/scripts/lib" but this does not seem to work anymore.
I tried -Dpython.path="{OPENHAB_CONF}/automation/lib/" in the JAVA_OPTS in set_env.

Edit:
Nevermind - I got it. Was confused between linux/windows shell variables.

Running openhab2 SNAPSHOT 2.2.0~20171112133452-1 on openHABian (on Raspberry Pi 3 booting from HDD)
with Jython 2.7.0 and the following in /etc/default/openhab2
EXTRA_JAVA_OPTS="-Xbootclasspath/a:/opt/jython/jython.jar -Dpython.home=/opt/jython -Dpython.path=/etc/openhab2/lib/python -Dgnu.io.rxtx.SerialPorts=/dev/rfxcom:/dev/zwave:/dev/cul"
CLASSPATH="/opt/jython/jython.jar"

Whatever i try to get Timers back to work i always end up with: “Fail to execute action: 1”

Here is some sample-code for reproduction:

from openhab.rules import rule, addRule
from threading import Timer

@rule
class TimerTest(object):
    def __init__(self):
        self.log.info("init finished")
        
    def getEventTrigger(self):
        return [ CommandEventTrigger("Active_Corridor") ]

    def getName(self):
        return type(self).__name__

    def execute(self, module, inputs):
        def TimerFunc():
            self.log.info("TimerFunc")

        def timerScript():
            self.log.info("Timer fired")

        self.log.info("creating Timer")
        # the following line will fail with:
        # [ERROR] [.automation.core.internal.RuleEngine] - Failed to execute rule 'TimerTest-81e1041c-97ba-44b5-b900-419e4d423328': Fail to execute action: 1
        t = Timer(10, timerScript)
        self.log.info("Timer created")
        t.start() 
        self.log.info("Timer started")

addRule(TimerTest())

The latest ESH-Null-Bug gave me some weeks of headaches too:

So i finally updated to 2.2.0~20171111035519-1 today and a couple of hours later on to 2.2.0~20171112133452-1.

I successfully used the following before the ESH-Null-Bug happended:

  • github .com/steve-bate/openhab2-jython
  • 0oh1compat . py from github .com/smerschjohann/docker-oh2-jsr223 modified to not use QuartzScheduler but Timer instead (in oh.createTimer) - which now also does not work any more, hence the stripped down example above.

Fortunately JSR223 is back working again, but i cannot get Timers back to work.

Could someone give me a hint how to get Timers back into my JSR223-Scripts in OH2?

I updated as well and fortunately my timers are still working well. But I use all timers inside of python modules and not as part of rules. That’s why I quickly tested your code on my setup. To make it short: It was working well…

But I am using a totally different setup. In my case, OpenHab is running in Docker on an x64 architecture.
Maybe your problem/the bug is architecture specific and only appears on armhf processors.

Thank you a lot for your answer!

Moving the entire system to a different host would require quite some effort because i use a lot of different bindings, as well as a couple of transceivers including gpio-stuff. Also if it comes to wireless, the location of the receiver is relevant. Therefore the raspberry is quite perfect as my 8-Core-Server is positioned at a position of bad reception and cannot be easily be moved to the central position the openhab-raspi is placed at.

I tried it anyway but after updating to the latest nightly on my openHabian setup, timers in jsr223 are working again on armhf.

So for now i can continue running openHabian and moving legacy-rules over to jsr223.

Again: Thank you a lot your your time to test my example-reproduction-code and feedback on it!

Many people, myself included, run light-weight scripts on a RPi for the gpio stuff and use something like MQTT to send receive to OH. This lets you run OH on a server or VM where ever you happen to have it but get the control close to the device you want to control.

Similarly, you can plug your transceivers into a RPi and access them over the network so you can place those in ideal locations while your heavy iron machines running OH are in a closet in the basement.

1 Like

@drscheme123: I am facing now a similar issues with my system. All my timer functions breaks after the first execution. The problem only appears on the snapshot build. I reported the issue here:

Can someone confirm the problem with timer and the current snapshot?

Hi,

I’m currently also writing my first jython rules and when using them on the local instance, everything seemed to run fine. However on my installation of openhab the rule also works, when I touch the file after openhab has completely started, but during the start process I currently get the following error message for a very basic rule when the scripts are loaded and also when I touch them immediately after they have been loaded:

20:40:12.409 [ERROR] [ript.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab2/automation/jsr223/light/src/klo_light.py': NameError: name 'SimpleRule' is not defined in <script> at line number 22

Here is the rule:

scriptExtension.importPreset("RuleSupport")
scriptExtension.importPreset("RuleSimple")

class Light_Rule(SimpleRule):
def __init__(self):
    self.triggers = [
        Trigger("TestTrigger","core.ItemCommandTrigger",
            Configuration({"itemName": "Motion_Light_Klo"}))
    ]

def execute(self, module, input):
        events.sendCommand("Klo_Dimmer", "100")


automationManager.addRule(Light_Rule())

In the logs I recognized, that some bundles of org.eclipse.smarthome.automation are started way after the scripts have been loaded and when I touch the file after they have been loaded, it works out of a sudden. So for me it looks like my scripts are loaded too early … any idea how I can change this? Unfortunately they are also not reloaded automatically after those bundles have startet.