JSR223: ItemUpdatedTrigger

I’m exploring some of the new stuff that is possible with JSR223 and have stumbled upon the OSGI Event Rule trigger. In particular I’m interested in ItemUpdatedTrigger. Unfortunately the Helper Library Docs and Extensions examples are not very clear about when this event triggers and I can’t figure out how to create a Rule that actually uses this trigger to try and experiment and answer some of these questions on my own.

  • When any part of the Item is updated, e.g. metadata, Group membership, label, etc?
  • What get’s put into the event Object passed to the Rule?
  • Can I use the @when decorator? If so what is the syntax? “Item Blah updated” doesn’t appear to work.
  • Can I use “Member of Item BlahGroup updated”?
  • Are the docs up to date for creating a Rule using Extensions? I can’t even get as far as creating a Rule with as I keep getting
Error during evaluation of script 'file:/openhab/conf/automation/jsr223/python/personal/dp/timeofday.py': TypeError: __init__() takes exactly 1 argument (2 given) in <script> at line number 31

The code

from core.rules import rule
from core.triggers import when, ItemUpdatedTrigger
from org.joda.time import DateTime
from core.utils import sendCommandCheckFirst
from core.metadata import get_key_value
from time import sleep
from core.actions import ScriptExecution
from configuration import tod_group, tod_item
from core.log import log_traceback, logging, LOG_PREFIX


class ToDUpdatesRule(object):

    def __init__(self):
        self.triggers = [ItemUpdatedTrigger("vDay_Time").trigger]
        self.name = "Update Items when they change"
        self.description = "Triggers when an Item changes it's definition."
        self.tags = set("ToD")
        self.log = logging.getLogger("{}.ToDUpdatesRule".format(LOG_PREFIX))

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


Note that there are lots of extra imports because I have the rest of my ToD Rule in this file. I’m looking at ways to update Item when the Item’s metadata changes, for instance.

The error points to the automationManager line or, when I try to use the @rule decorator it points at the @rule line. As you can see, I’m not passing in two arguments to any constructor as far as I can tell.

I’m using the latest snapshot of OH with the latest master from the helper libraries.

And probably fell over it :wink:, since I have not yet migrated OsgiEventTrigger after the major breaking changes in automation introduced in S1319. Mind you, this is only available in the Jython helper libraries and is not specific to JSR223. After migrating all of the other triggers, I paused on this one, since I wanted to get the when decorator released and this trigger used a custom handler, which were also broken in the change. I’ve had this on a back burner since I got the custom handlers working again. There have never been docs for it, and I am not aware of anyone who has ever used it (maybe Steve).

As for your error, maybe you noticed the PR I merged yesterday that started this migration? An OSGi event trigger will be really interesting! I have the when decorator all setup to incorporate DirectoryEventTrigger and the ItemRegistryTriggers… I just need to find the time to finish the migration.

If you look at the Event Object Attributes doc, you’ll see there are a few triggers that I marked as “Not currently supported”. There are events for them though, so you could use the GenericEventTrigger in an extension or use the raw API. I will likely use GenericEventTrigger in when instead of the OsgiEventTrigger` for some of these triggers.

At this time, if you need to trigger a rule on an ItemUpdatedEvent, you can’t use the when decorator yet.

Since metadata is not stored in the ItemRegistry (it’s stored in it’s own Registry), I’m 99% sure the ItemUpdatedEvent does not fire when metadata changes.

When I get home I can write you up an example of how I did the dynamic triggers in Eos. It won’t help you much with the ItemUpdatedTrigger until it’s available in when but it might help you with generating triggers at runtime in general.

You arguments thing might be because you’re passing an argument to a constructor that only takes self. Any __init__(self) automatically gets self passed to it by the compiler, so if you give an argument as well that makes 2.

Well, don’t bring them off the back burner on my account. I’ve plenty of other stuff to look at in the mean time. :slight_smile: At least I’m not going crazy or just failing to read and understand the docs.

I think this trigger will be very powerful for library writers (not so much general users). In particular, if I can trigger a Rule when an Item is added to a Group, or metadata is triggered on an Item then my Time of Day implementation can adjust as users change their Items instead of requiring the Rule to reinitialize everything every time it runs, potentially causing the Rule to retrigger as the Items change through the initialization.

At this point I have very little knowledge of the core or how all of this works under the covers. I’m trying to avoid going too deep which might make it harder for me to see things from the new user’s perspective. Consequently, I saw that there was a PR but I didn’t actually review it.

I was looking in https://openhab-scripters.github.io/openhab-helper-libraries/Python/Core/Packages%20and%20Modules/triggers.html which didn’t have any warning. It never occurred to me to look at the events table.



That I’m aware of but I’m not passing any arguments.


That’s the line it complains about and as you can see, no arguments passed in the constructor.

1 Like

So this is what I’m doing with Eos for the auto generated rules when it loads. I’ve modified it to work like your example above but the steps are the same.

  1. Remove the old rule if it exists
  2. Remove old triggers from the rule function
  3. Generate new triggers
  4. Register the rule

RULE_UPDATE_NAME = "tod_update_times"

def rule_tod_update_times(event):
    rule_tod_update_times.log.info("vDay_Time updated")

@rule("ToD Generate Update Rule")
@when("System started")
#@when(ItemUpdated tod_group) # whenever we have a members changed trigger
def rule_tod_gen_update(event):
    # remove old rule if it exists
    for rule in [rule for rule in rules.getAll() if rule.name == RULE_UPDATE_NAME]:
    # remove any old triggers
    if hasattr(rule_tod_update_times, "triggers"):
        delattr(rule_tod_update_times, "triggers")
    # add triggers for current group members
    when("Member of {} changed".format(tod_group))(rule_tod_update_times)
    # create the rule
    if hasattr(rule_tod_update_times, "triggers"):
        rule(RULE_UPDATE_NAME, "Time of Day start times update rule")(rule_tod_update_times)
        rule_tod_gen_update.log.warn("No Time of Day start time Items found")

I need to think about this some. You need to recreate the Rule in this case because, IIRC, when you use a Member of trigger that gets expanded to separate triggers for each member and without reloading the Rule changes made to the Group itself will not impact the Rule triggers until there is a reload. Correct?

Correct … Or is that the Descendant of?
Member of creates a trigger for each member of the group. Descendant of does the same but recurses into groups instead of creating a trigger for groups as if they were also an item.

1 Like

Correct. When “Member of” is created in OHC automation, it will/should be a requirement to function as it does in the rules DSL. This could be included in the Jython HLs, but I’d rather remove it completely after putting the effort into getting it implemented so that everyone could use this trigger when using the NGRE.

If this is what you are using, you must have never tested it. You’re using the wrong tool and this will not remove rules created through scripted automation. You are trying to use ‘rules’, which is a RuleRegistryImpl, uses a ManagedRuleProvider, and is used to manage rules created through a resource bundle, REST API, console, etc. These rules, specifically their storage, is very different than rules created through scripted automation. Trying to delete a rule created through scripted automation using ‘rules’ will do nothing. Same for when you try to delete a rule created through scripted automation through Paper UI, although you do get a 404.

Rules created through scripted automation are managed through the RuleSupportRuleRegistryDelegate, which uses a ScriptedRuleProvider. This is what needs to be used to remove a rule created through scripted automation, and it is available through ruleRegistry in the RuleSupport scriptExtension preset.

Also, you don’t need to search for the UID if the rule was already created with ‘rule’, since the function will have a UID attribute…

if hasattr(rule_tod_update_times, "UID"):

This is not needed, since ‘core.rules.rule’ will set the function’s ‘triggers’ attribute to None after creating the rule to remove the old triggers.

You can just use “Member of” here… ‘when’ will not choke if the group has no members.

Since you ran it through when, the function will have a triggers attribute, so there’s no need to check.

1 Like

You are correct, I have not tested it using rules and forgot to include that note when I posted the example. I am using ruleRegistry for the search and remove because in Eos the function is in a library file not a script.

Also true, but I elected to search because if the script gets reloaded then the new instance of the module will not have the UID attached to the function object.

This is necessary, I got errors if triggers was None. I think from when but I can’t recall, it was over 2 months ago.

Indeed, I was really tired yesterday can you tell? I use single triggers in Eos and I was copying from that.

On first run there will be no triggers attribute though.