General questions about JSR223 Jython

@martin_klimke

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?

In Python, you don’t need the new keyword to create an instance of a Python or Java type. You can do the update with

oh.postUpdate(Martin_home_time, DateTimeType())

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

You’ll need to save a reference to the timer somewhere. Usually, an attribute of your rule instance is a good choice. For example…

self.mytimer = oh.createTimer(expire_time, self.handle_timer)

and later

self.mytimer.cancel()

In general I am struggling quite a bit with the lack of documentation and examples

I agree. Like most of openHAB the documentation is rather sparse. I assume you’ve been looking at the documentation on the wiki.

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

The ChangedEventTrigger class has three constructor arguments, the first is the item name, the second is the “from” state and the third is the “to” state. It also can take just the item name and then it will trigger on every change (but updates to the same value won’t trigger it). It’s important to pay attention to the argument types that have been declared in the openHAB Java code. For example, this would not work…

ChangedEventTrigger("Switch", "OFF", "ON")

since the second and third argument must be state objects. You’d need to use…

ChangedEventTrigger("Switch", OnOffType.OFF, OnOffType.ON)

instead. If you are using the OnOffType enums frequently in a rule file, you might want to simplify the syntax a bit by putting the following code near the top of your rule file.

OFF = OnOffType.OFF
ON = OnOffType.ON

Then you can define the trigger with

ChangedEventTrigger("Switch", OFF, ON)

If you don’t care about one of the states in the trigger, you can use the Python None value for it (the same as null in Java). For example,

ChangedEventTrigger("Switch", None, ON)

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

Because of the transition between OH1 (1.8.x) and OH2 (1.9.x), you must be one the 1.8 branch of the openhab git repository since the code is not on the master branch. On the web, you can see it at…

Keep in mind that Jython is mostly Python but there is a learning curve when interacting with Java code. A good resource for Jython-specific topics is…

http://www.jython.org/jythonbook/en/1.0/

3 Likes

Having finally got Jython installed on my system, I agree that the documentation needs to be filled out in order to make it useable.

How about linking the items under Interaction with Openhab to their own wiki page, with information such as in the previous answer.

I have been experimenting with using a class as an item, rather than an event, which each item defining the events that it reacts to. However, when I try to use addRule for a RuleSet, I get an UnsupportedOperationException.

Is their any way to dynamically add rules to the RuleSet?

Yes, you should be able to do this. Can you include the relevant part of the stack trace?

I’m away for a bit from tomorrow, so here is the code to show how I’m trying to acheive this. There are various classes defined above, but it all saves fine if I comment out the theRules.addRule(self.motionLamp) line.

If I had time tonight I’d fiddle around further no that I know its supposed to be possible, but I have to pack :unamused:

class Lamp:
    class Motion(Rule):
        def execute(self, event):
            oh.logDebug("motion", Test)

        def getEventTriger(self):
            return [ StartupTrigger() ]

    def __init__(self, itemName, delay):
        self.motionLamp = self.Motion()
        theRules.addRule(self.motionLamp)



theRules = RuleSet([ArriveHome(),
        eTRVArriveHome(296, 20, 16), eTRVArriveHome(329, 17, 21),
        eTRVLeaveHome(296,16), eTRVLeaveHome(329, 16),
        MotionLamp("Light_GF_Living_Table", "Sensor_GF_Living"),
        MotionLamp("Light_GF_Living_Lamp", "Sensor_GF_Living"),
        TimedLamp("Light_GF_Living_Table", 15, sensor="Sensor_GF_Living",
            OnWhenOut=True),
        TimedLamp("Light_GF_Living_Lamp", 5, sensor="Sensor_GF_Living"),
        #TimedLamp("Light_FF_Bed_Table", 45),
        TestRule()])

lamp = Lamp("Light_FF_Bed_Table", 47)   # line 265

def getRules():
    return theRules

For adding the rule, what you’re doing should be fine (in my opinion). The reason for the exception is subtle. The RuleSet class constructor that takes an array converts the array to a list internally. However, this list cannot be extended since it uses the provided array as the storage and the array is fixed length. (Note: the Python list is extendable, but it is converted to a Java array when the Java class constructor is called.) I think the JSR223 code should be changed to add the array items to the internal list instead of creating an internal list that can’t be extended.

If you use the default constructor for the RuleSet then you can add rules with addRule. You could alternatively maintain a Python list (theRules = []) and then create the RuleSet with the list when getRules is called.

A few other issues with the Lamp class…

  1. You need to declare theRules as a global variable in the __init__ method.
  2. There’s a typo in getEventTriger. It should be getEventTrigger.
  3. The log statement in the execute method is referencing a Test variable which doesn’t exist (in the example code).

After making those changes the example worked for me.

Well, there is a reason for that. In my proposed version of JSR223 for OH2 I allow direct adding and removing without the use of the getRules method. Nonetheless, in the current state, the sum of rules, a script can have, is fixed after the one and only call to getRules().

This means rule generation inside this method or during the processing of the method is okay, but you should be aware that after initial construction, no further additions to the RuleSet is possible. That is a limitation of the current implementation. (As it only put the triggers of the rules into the according rule maps at this point in time)

I understand that the set of rules for a script cannot change after getRules returns. However, Graham’s Lamp class was attempting to add the rule to the RuleSet in the constructor before the rule set was returned by getRules.

With the OH1 implementation, the following code raises an exception:

rules = RuleSet([])
rules.addRule(MyRule())

but the following does not…

rules = RuleSet()
rules.addRule(MyRule())

The existence of the addRule and removeRule methods, which work with the default RuleSet constructor, could make one think the set is designed to be mutable (in general, rather than depending on which RuleSet constructor was used).

Thanks Steve.

After a weeks holiday I’ve lost my train of thought, but I should be able to get back to this at some point.

Hi Steve,

I am still struggling with very simple things.
e.g. have a timer trigger a command.
The last state creates error message I cannot interpret

class Home_big(Rule):

def __init__(self):
    self.timer = None

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

def execute(self, event):
        self.timer = oh.createTimer(DateTime.now().plusSeconds(10),oh.sendCommand("Home_big_vorkurzem", "ON"))

Hi Steve,

You advised me with the following

I am receive a "Null " error.
I have already added the quotes
oh.postUpdate(“Martin_home_time”, DateTimeType())

but this seems not to do the trick.

Any further idea?

correct syntax is oh.postUpdate(“Martin_home_time”, str(DateTimeType()))