Simplified Jython rule definition (similar to Rules DSL) using a universal decorator

I’m assuming you are at the start of a function…

def testFunction(event)
    if event.oldItemState == UnDefType.NULL:
        return
    # do stuff

I’ve added this to the README

yes ,that is it.

@5iver is it possible that group events are not being properly processed. For instance, I have an item that is a member of 3 groups. When it changes, I see the event in the group that I am monitoring. But then I also see events come in through the same rule, but with the other 2 groups the item is a member of …

item A is a member of Groups B,C,D

have a rule capturing any changes to members of Group B

log all events from the rule and print the item name.... and I see

A
C
D

when it should just be A

It looks like you have run into this bug…

What I’ve done as a workaround, is to add this to the rule. It still misfires, but doesn’t execute the rest of the rule unless the triggering item is actually in the group. If this does not fit for you, share what the rule is triggering on.

@rule("Light: Area trigger")
@when("Member of gArea_Trigger changed")
def areaTrigger(event):
    if "gArea_Trigger" in ir.getItem(event.itemName).getGroupNames():
        # rest of rule

How is JSR223/Jython in regards to the “problems” with Thread::sleep and timers? are there any “static” limits on threads? Ie. Can I hypothetically run 10 different rules (in the same rules file or otherwise) with different sleep delays all at the same time?

Ref.:

I have not specifically looked through the code for a definitive answer, but from my experience, JSR223 rules do not have the same threadpool limitations found in the Rules DSL. Here is an example…

I have not tried it, but there is potentially multi-processing support too!

1 Like

that will work as a work around…

I am not a python expert, but it looks like the timer object is built on the thread tools of python - so I am guessing jython doen’t have the thread limitations of the dsl.

It goes deeper than that, but you should also be able to use the createTimer action. I haven’t tried it though, as I prefer to use Jython where available.

Any thoughts on simplifying the code below?

item A belongs to groups F, G, H

Group F belongs to group Z

Using item A, I want to find the group item A is a member of (F,G,H) is also in group Z.

ie in this case group F
    triggeringItem = itemRegistry.getItem (event.itemName)
    gArea = itemRegistry.getItem ("gArea")
    areaItem = None
    for area in gArea.getMembers ():
        for name in triggeringItem.getGroupNames ():
            if name == area.name:
                areaItem =itemRegistry.getItem (name)
                break```

These should not be as much of a problem in jython. I don’t think JSR223 Rules use the thread pools, maybe the Quartz thread pool?

However, there is indeed a limit to the number of threads you can run in Python or any other programming language on a given system. I had a sensor module in sensorReporter that was working with BT. I was spinning off a separate thread to poll for nearby devices looking for known macs. I then put reelyActive on the same machine which also uses BT (it’s Node.js not python). So reelyActive got a permanent lock on the BT device and my sensorReporter scanning threads just got stuck and piled up. Eventually the program just died. I would expect something similar to happen in the JSR223.

But, I don’t think there is any hard and fast predefined limit. I think it will continue spawning threads until you run out of RAM to create new one.

1 Like

The code you posted looks good and works, but here are some suggestions:

  1. There are some spaces between the function names and (). This will work, but looks odd.
  2. The break will only exit the current loop. You could add a check above the second for loop to see if areaItem is not None, and then break out of that loop too.
  3. You can use ir as an alias to itemRegistry to save some typing.
    triggeringItem = ir.getItem(event.itemName)
    gArea = ir.getItem ("gArea")
    areaItem = None
    for area in gArea.getMembers():
        if areaItem is not None:
            break
        for name in triggeringItem.getGroupNames():
            if name == area.name:
                areaItem = ir.getItem (name)
                break

BUT, another approach is to use a list comprehension, which is a very powerful tool in Python. Makes your code a one-liner…

areaItem = list(group for group in ir.getItem("gArea").members if ir.getItem(event.itemName) in group.members)[0] 

What are you looking to achieve with the rule? There might be an easier way to get there. This weekend I plan to post about a rule strategy that uses nested groups containing triggering items and action items (Area_Trigger and Area_Action), and a single rule that takes care of all lighting automation (I use it to turn on speakers too), and then some others that make light level adjustments based on lux level. Looks like you may be doing something similar.

1 Like

That worked perfectly… need to read more about python. Thank you

Scott, have you done any work with creating items dynamically?

The rule above is part of an Occupancy concept I had been using in another HA software package. Now adopting it to OH.

When I saw your reply, I remembered there is a very slight performance gain using getGroupNames vs group.members…

areaItem = list(group for group in ir.getItem("gArea").members if group.name in ir.getItem(event.itemName).getGroupNames())[0]

Nope.

You may want to hold off until my post this weekend. Think of groups for each area within your house, holding items that would trigger whether the area is active or not. And groups holding the items that should respond to activity in the area. That is what my rule does. The special sauce is in the group functions… takes the logic out of the code and puts it into the groups, Get another light, motion sensor, door sensor, etc., add it to a group, and you’re done… no change in code needed.

That’s very similar to what I am doing. I use tags for each area that describe the duration an area is to be occupied for and the actions to be taken when an area becomes vacant or occupied. Each item in an area also has tags that describe what happens when that item is triggered

Nice! I use a dict (was a HashMap in Rules DSL) to store that info, but would like to use Item metadata. Just haven’t figured out how to access it yet.

Never mind, typo was causing the the problem. getTags() works fine.

Scott, is it possible the item.getTags() is broken? I cannot get it to return the tags of an item that I know has tags…

==> /var/log/openhab2/openhab.log <==

2018-10-12 10:03:45.079 [INFO ] [eclipse.smarthome.model.script.Rules] - gOccupancyItem Changed  TF_Office_MotionSensor (Type=ContactItem, State=CLOSED, Label=Third Floor Motion, Category=motion, Tags=[OE:Motion_Standard], Groups=[gTF_Office, gSS_InteriorMotionSensor, gOccupancyItem])


1 Like

Hey Scott, thanks a lot for this library, it looks very promising.

Is it still compatible with the old rule decorators (e.g. item_triggered)?
I think i read about the compatibility some days ago but now i cannot find it anymore.
I do not find the definitions in your new triggers.py

I would test it, if it doesn’t brake my other rules, so that i can convert a few rules from time to time.

Best regards