Port JSR223 bundle to openHAB 2

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.

Hello i hope you guys can help me. I am trying to define and import my own modules.
Therfore i created the file $ohdir/lib/python/openhab/timer.py with the followng content:

import threading
import time
         
class MyTimer(threading._Timer):
    started_at = None
    def start(self):
        self.started_at = time.time()
        threading._Timer.start(self)
    def elapsed(self):
        return time.time() - self.started_at
    def remaining(self):
        return self.interval - self.elapsed()

In my script i import this class with

from openhab.timer import MyTimer

and i am able to create an object where the type() returns the correct type. But the problem ist that my own methods (e.g. remaining()) do not work. If i however define the class MyTimer in the script itself it works like a charm. What is wrong with importing my class from a library?

My seconds issue presumably has the same root cause. I would like to define a global dictionary, which i can access from every script

devicesdictionary= {"Lb":"Gluehbirne", "Ls":"Lichtschalter", "Ht":"Heizungsthermostat", "Us":"Universalsensor", "Wc":"Fensterkontakt", "Dc":"Tuerkontakt", "Sd":"Rauchmelder", "Ps":"Steckdose"}

where and how do i have to define it properly? Tried several ways (e.g. loading as “component”) which didn’t work.

Thanks :slight_smile:

In Python, “global” variables are scoped to a module. If you create a module and expose your dictionary though a “global” variable in the module, you can then import that module into multiple scripts.

For your timer example, what is threading._Timer (with the underscore) vs. threading.Timer?

Thanks the import of a dictionary works like you said :slight_smile:

threading.Timer is a function and threading._Timer is a class which MyTimer inherits from. My function basically does the same as threading.Timer but has a .remaining() and .elapsed() method which returns a time in seconds

Interesting. I didn’t realize Timer was a function (never tried to subclass it). Importing classes from modules is not a problem in general. However, I don’t understand what you’re doing with the _Timer subclass. It appears you are not invoking the _Timer constructor which has required arguments. How does that work?

Hi steve thanks again for your help. It actually works now (with the class like I posted above) but i have to

import threading, time

in my script which is strange imo, because it is imported in the module.

It would have been easy to identify if i would be able to see the error messages the script is throwing.
Is there a way to display them all, like a was in a python shell? Now I am debugging with a lot of logging statements, which is very annoying :frowning:

Use a try/except around the code (possibly the entire script body) and log the traceback.

1 Like

Currently I am observing the same error „NameError: name ‘SimpleRule’ is not defined in at line number xy“ as described above when openHAB is started. When touching the file at a later time it is loaded without an issue.

Has there been any solution for that problem?

Thanks for your help,
Juelicher

Hello i got two questions i can not answer by searching the forum or docs:

  1. Is it possible to have a trigger similar to Xtend “System started”? Like:
self.triggers = [
  Trigger("SystemStartedTrigger", "???", Configuration({ ??? })),
  ]
  1. What are the other options for
automationManager.addRule(MyRule())

In particular i would like to be able to remove single rules under certain condition. The obvious

automationManager.removeRule(MyRule())
automationManager.deleteRule(MyRule())

does not work.

Thanks

It’s not built-in. Even the new SmartHome rule engine doesn’t support it, AFAIK. I’m guessing the reason it wasn’t officially supported with the new engine is because there is no well-defined “system started” event and so the trigger has always been a bit dubious. It’s possible to write your own custom trigger to do something similar. I created one in my openhab2-jython GitHub repo. In this case, “System Started” was really “Rule Defined”.

I’m not currently in a position to try it myself, but I’d recommend trying ruleRegistry.remove(ruleInstance.uid). You can also look at the source code (especially the documentation) for the RuleRegistryImpl class.

1 Like

Sorry I am not a very skilled programmer, I hope you can guide me on how to use your custom trigger. How would my class look like? If i do it like this it complains about a missing handler:

scriptExtension.importPreset("RuleSupport")
scriptExtension.importPreset("RuleSimple")
from openhab.triggers import StartupTrigger

class MyRule(SimpleRule):
    def __init__(self):
        self.triggers = [
             Trigger("SystemStartedTrigger", "openhab.triggers.StartupTrigger", 
                    Configuration())
        ]
        
    def execute(self, module, input):
        # some code

automationManager.addRule(MyRule())

From your source code i assume i need an “openhab.STARTUP_MODULE_ID”, but where do i get it from?
Everytime a rule gets loaded it gets a new random id. Is it possible to define an own rule id beforehand? I assume that would also help me to delete a specific rule as asked in my 2nd question.

Thanks in advance :slight_smile:

Hey @steve1:

You were right. I tried the following:

rules.setEnabled(u'TestCaseRunner', False)
rules.remove(u'TestCaseRunner')

setEnabled seems to work flawlessly:

2018-02-13 19:31:50.000 [.event.RuleStatusInfoEvent] - TestCaseRunner updated: DISABLED

remove however does nothing. Any Idea?

Edit:
It seems that I can only remove rules that were added during the script scope?

a = MyRule()
s.rules.add(a)
id = rules.getAll()[-1].getUID()
s.rules.remove(id)

produces:

2018-02-13 19:53:27.875 [thome.event.RuleAddedEvent] - Rule 'rule_1' has been added.
2018-02-13 19:53:27.875 [.event.RuleStatusInfoEvent] - rule_1 updated: INITIALIZING
2018-02-13 19:53:27.875 [.event.RuleStatusInfoEvent] - rule_1 updated: IDLE
2018-02-13 19:53:27.891 [ome.event.RuleRemovedEvent] - Rule 'rule_1' has been removed.