That fixed it for me!
Thanks a lot!
That fixed it for me!
Thanks a lot!
@5iver, you have probably already received a PR notification for this PR, but thought I would bring it to your attention here.
I’m not that familiar with jython but can I use a jython script/rule that imports https://lmstools.readthedocs.io/en/latest/ (lmstools) ? I need some extra functionality (playlist control, add/remove track) the squeezebox binding doesn’t have…
You’ll need to try importing it to know for sure…
mhm after updating to 2.4 all my jython rules stopped working they are also not showing in the ui anymore
Without much to go on, my best guess is that the Experimental Rule Engine add-on may not be installed?
More detail about your system would help:
My guess is that OH over wrote the JAVAEXTRAOPTs during the update
@scottk: The Rule engine is installed. This was successfully running until the update.
@mjcumming: You were absolutley rigtht. Thanks!
Also i had to change my imoprts from openhab. something to core.something.
Hi,
I am currently testing out some functions for migrating all my DSL rules to Jython.
I have defined the following function, to test if I can use the createTimer action with functions defined within functions:
@rule("Test timer functions - Jython")
@when("Time cron 21 0/1 * * * ?")
def testFunctionsStates(event):
def theInnerFunction():
log.info("@@@@@@@@@@@@@@@@@@@@@ Inner Function was called")
log.info("################# Outer function was called ")
createTimer(DateTime.now().plusSeconds(30), lambda: theInnerFunction())
This worked well for some hours, but now I get the following exception:
2018-12-29 16:04:51.009 [INFO ] [e.smarthome.automation.test_rules.py] - @@@@@@@@@@@@@@@@@@@@@ Inner Function was called
2018-12-29 16:05:06.546 [ERROR] [org.quartz.core.JobRunShell ] - Job DEFAULT.2018-12-29T16:05:06.545+01:00: <function <lambda> at 0xaf> threw an unhandled Exception:
org.python.core.PyException: null
at org.python.core.Py.TypeError(Py.java:259) ~[?:?]
at org.python.core.PyObject._basic_add(PyObject.java:2141) ~[?:?]
at org.python.core.PyObject._add(PyObject.java:2119) ~[?:?]
at org.python.pycode._pyx98.checkSendCommand$2(<script>:34) ~[?:?]
at org.python.pycode._pyx98.call_function(<script>) ~[?:?]
at org.python.core.PyTableCode.call(PyTableCode.java:167) ~[?:?]
at org.python.core.PyBaseCode.call(PyBaseCode.java:153) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:423) ~[?:?]
at org.python.pycode._pyx98.f$14(<script>:156) ~[?:?]
at org.python.pycode._pyx98.call_function(<script>) ~[?:?]
at org.python.core.PyTableCode.call(PyTableCode.java:167) ~[?:?]
at org.python.core.PyBaseCode.call(PyBaseCode.java:124) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:403) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:398) ~[?:?]
at org.python.core.PyFunction.invoke(PyFunction.java:533) ~[?:?]
at com.sun.proxy.$Proxy220.apply(Unknown Source) ~[?:?]
at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
2018-12-29 16:05:06.549 [ERROR] [org.quartz.core.ErrorLogger ] - Job (DEFAULT.2018-12-29T16:05:06.545+01:00: <function <lambda> at 0xaf> threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: org.python.core.PyException
at org.python.core.Py.TypeError(Py.java:259) ~[?:?]
at org.python.core.PyObject._basic_add(PyObject.java:2141) ~[?:?]
at org.python.core.PyObject._add(PyObject.java:2119) ~[?:?]
at org.python.pycode._pyx98.checkSendCommand$2(<script>:34) ~[?:?]
at org.python.pycode._pyx98.call_function(<script>) ~[?:?]
at org.python.core.PyTableCode.call(PyTableCode.java:167) ~[?:?]
at org.python.core.PyBaseCode.call(PyBaseCode.java:153) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:423) ~[?:?]
at org.python.pycode._pyx98.f$14(<script>:156) ~[?:?]
at org.python.pycode._pyx98.call_function(<script>) ~[?:?]
at org.python.core.PyTableCode.call(PyTableCode.java:167) ~[?:?]
at org.python.core.PyBaseCode.call(PyBaseCode.java:124) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:403) ~[?:?]
at org.python.core.PyFunction.__call__(PyFunction.java:398) ~[?:?]
at org.python.core.PyFunction.invoke(PyFunction.java:533) ~[?:?]
at com.sun.proxy.$Proxy220.apply(Unknown Source) ~[?:?]
at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
... 1 more
As you can the from the line “@@@@@@@@@@@@@@@@@@@@@ Inner Function was called”, the timer triggered correctly.
I have no clue, why this exception is thrown. Maybe you have any idea?
Thanks for your help!
Juelicher
I have not used the java timer object in jython.
I have used the jython timer object with no problems.
from threading import Timer
timer = Timer(time_out_seconds, timeout_callback)
Well, I am a bit lazy and using the createTimer action I do not have to store the timer in a variable and start it explicitly
Today I did some testing with the Python timer, using the example linked in the documentation as a template.
I think, that there a two bugs in the example:
First:
if chargerTimer1 is None or str(chargerTimerAttached.getState()) == "TERMINATED":
Beside the typo in the timer name “chargerTimerAttached”, I get an error, that the timer object has no method “getState()”
Second:
chargerTimer1.stop()
A timer can be created by calling:
createPythonTimer(15, 'TestTimerName', lambda: theInnerFunction())
There I also get an error, that this method does not exists, instead “cancel()” can be used.
What seems to work for me:
testTimer = {}
def createPythonTimer(elapseTime, item, callback):
myTimer = testTimer.get(item) # returns none, when key does not exists
if myTimer is None or not myTimer.isAlive():
myTimer = Timer(elapseTime, callback)
testTimer[item] = myTimer
myTimer.start()
log.info("test_rules.py - Created python timer, item: " + item + ", time: " + str(elapseTime))
def stopPythonTimer(item):
myTimer = testTimer.get(item) # returns none, when key does not exists
if myTimer is not None and myTimer.isAlive():
myTimer.cancel()
testTimer[item] = None
log.info("test_rules.py - Stopped python timer, item: " + item)
In this small examples I store the timers in a dictionary, so that I do not have to create a variable for each timer.
According to what I found in the internet, “myTimer.finished” should be better than “myTimer.isAlive()”, as there is a small time between finishing the timer and exiting the background thread, internally used for the timer and therefor setting “isAlive()” to false. But checking “finished” does not work for me, I do not know why.
A better way to track timer state (running/stopped) would be to use the callback to set the state of the timer. Or you could remove the timer item from your dictionary in the callback. You do not need to check if the timer is alive to call cancel.
Thanks for the tipps, I will try that out. Have to think about it, as I use the same callback functions in multiple timers, so the timer would probably have to be passed to the callback.
I am still in the stage of figuring out how to convert my rules from DSL to Jython, but even now it makes some tasks much easier!
You could do
testTimer = {}
def createPythonTimer(elapseTime, item, callback):
myTimer = testTimer.get(item) # returns none, when key does not exists
if myTimer is None:
def cb():
testTimer[item] = None
callback()
myTimer = Timer(elapseTime, cb)
testTimer[item] = myTimer
myTimer.start()
log.info("test_rules.py - Created python timer, item: " + item + ", time: " + str(elapseTime))
def stopPythonTimer(item):
myTimer = testTimer.get(item) # returns none, when key does not exists
if myTimer is not None:
myTimer.cancel()
testTimer[item] = None
log.info("test_rules.py - Stopped python timer, item: " + item)
Thanks for your suggestion, good idea, I will try that!
Today I encountered an other problem, probably Jython related.
I created a mylib.py file in the lib/python/personal directory
In principal I can import that file in my rules files in jsr223/personal. But when I change the mylib.py the compiled bytecode file in mylib$py.class is not updated.
I can trigger an update by renaming the mylib.py and the import statement, but there surely is an other way…
Thanks for your help again and a happy new year to all of you!
Juelicher
Modules do not reload unless you restart OH. You can solve this by reloading any modules.
Example:
import area_occupancy_event_metadata
reload (area_occupancy_event_metadata)
from area_occupancy_event_metadata import Area_Occupancy_Event_Metadata
You can trigger the reload by changing and saving the script that call the module.
I don’t know if Windows provides a command equivalent to touch
on linux, but on a linux system you can simply do the following to trigger the reload:
sudo touch /etc/openhab2/automation/jsr223/personal/mylib.py
Thanks, booth of you, for your quick help!
I am running openHAB on macOS, touch is available. But the mylib$py.class was not updated even when it was older than the mylib.py file. Maybe this is related to macOS and the HFS file system?!
Reloading mylib from of my rules files triggers recompiling the bytecode, this seems as a good enough workaround, as the library will (hopefully ) nor be changed very often.
Sorry if it gets annoying…
While moving my functions to the library file I stumble about an other issue, I would like to understand to get a better grasp of the internals:
In my rules I can use OnOffType, itemRegistry and other types without explicitly importing them. In the library file I had to add “from core.jsr223.scope import itemRegistry, OnOffType, UpDownType, OpenClosedType”
Why is that?
Thanks again!
Juelicher
I think it is because in your rules files, you have the following import:
from core.rules import rule
The file automation/lib/python/core/rules.py
has some imports that take care of automagically making available those types and globals.