[jython] use of dynamically created rules

Dear community,
I try to create a class for a library module, that gets a configuration passed in and creates rules to call class methods.

As a starting point and for easier presentation in this post, ExampleClass is still in a script file, gets the name of an item and creates two rules that trigger on an ItemStateUpdateTrigger on this single item.

The event is passed to the class methods and I expected that these can use event.itemState, event.itemName and other methods described in the openhab-helper-libraries documentation.

A dir() on the passed-in object shows different attributes.

Here is the script file:

# jython imports
import time
import uuid

# openhab2 jsr223 imports
import core.items
from core.jsr223.scope import events
from core.log import logging, LOG_PREFIX
from core.triggers import ItemStateUpdateTrigger
from core.utils import getItemValue

scriptExtension.importPreset("RuleSupport")
scriptExtension.importPreset("RuleSimple")

module_name = "generic_rule_example_1"
module_prefix = module_name + "_"

log = logging.getLogger("{}.{}".format(LOG_PREFIX, module_name))

RESPONSE_TIME = 3
'''the time tests are waiting between their actions and
   checking results.
'''
class RuleTemplate(SimpleRule):

    def __init__(self, triggers, f):
        self.setTriggers(triggers)
        self.function = f

    def execute(self, module, input):
        try:
            self.function(input)
        except:
            log.error(traceback.format_exc())

class ExampleClass(object):

    def __init__(self, item):
        self.rule1 = automationManager.addRule(RuleTemplate([ItemStateUpdateTrigger(item.name).trigger], self.method_a))
        self.rule2 = automationManager.addRule(RuleTemplate([ItemStateUpdateTrigger(item.name).trigger], self.method_b))

    def method_a(self, event):
        log.info("ExampleClass.method_a()")
        log.debug("event: {})".format(event))
        log.debug("dir(event): {}".format(dir(event)))
        #
        log.debug("event.keys: {})".format(event.keys()))
        log.debug("dir(event.items): {}".format(dir(event.items)))

    def method_b(self, event):
        log.info("ExampleClass.method_b()")

    def cleanup(self):
        automationManager.removeHandler(self.rule1.UID)
        automationManager.removeHandler(self.rule2.UID)

def scriptLoaded(id):
    ITEM1NAME = "TestSwitch1"
    item1 = core.items.add_item(ITEM1NAME, "Switch")

    exampleObject = ExampleClass(item1)
    log.info("example object created.")
    try:
        time.sleep(RESPONSE_TIME)
        value = OFF if getItemValue(item1, OFF) != OFF else ON
        log.info("postUpdate({}, {})".format(item1.name, value))
        events.postUpdate(item1, value)
        time.sleep(RESPONSE_TIME)
        exampleObject.cleanup()
    except Exception:
        log.error("exception running example")
        log.exception("message")

    core.items.remove_item(item1)

def scriptUnloaded():
    # this executes automatically when the script is unloaded/reloaded. if you create any threads or timers, make sure you cancel/stop them here!
    log.info("script cleanup")

And the log output including dir() output:

07:08:18.801 [INFO ] [ort.internal.loader.ScriptFileWatcher] - Loading script 'python/personal/generic_rule_example_1.py'
07:08:18.830 [DEBUG] [jsr223.jython.core.items             ] - Item added: [TestSwitch1 (Type=SwitchItem, State=NULL, Label=null, Category=null)]
07:08:18.835 [DEBUG] [ipse.smarthome.automation.module.core] - ServiceEvent REGISTERED - {org.eclipse.smarthome.core.events.EventSubscriber}={service.id=945, service.bundleid=214, service.scope=singleton, event.topics=smarthome/items/TestSwitch1/*} - org.eclipse.smarthome.automation.module.core
07:08:18.839 [DEBUG] [ipse.smarthome.automation.module.core] - ServiceEvent REGISTERED - {org.eclipse.smarthome.core.events.EventSubscriber}={service.id=946, service.bundleid=214, service.scope=singleton, event.topics=smarthome/items/TestSwitch1/*} - org.eclipse.smarthome.automation.module.core
07:08:18.842 [INFO ] [jsr223.jython.generic_rule_example_1 ] - example object created.
07:08:21.847 [INFO ] [jsr223.jython.generic_rule_example_1 ] - postUpdate(TestSwitch1, ON)
07:08:21.850 [DEBUG] [tomation.core.internal.RuleEngineImpl] - The trigger 'bb274100a2d011e988629506815ad68e' of rule '7602490d-7bfd-44cb-a06d-2884e73ce8ff' is triggered.
07:08:21.852 [DEBUG] [tomation.core.internal.RuleEngineImpl] - The trigger 'bb28044fa2d011e9ab5c9506815ad68e' of rule '6beb94e7-55f4-4916-9ce1-b640984240c1' is triggered.
07:08:21.859 [INFO ] [jsr223.jython.generic_rule_example_1 ] - ExampleClass.method_b()
07:08:21.861 [DEBUG] [tomation.core.internal.RuleEngineImpl] - The rule '6beb94e7-55f4-4916-9ce1-b640984240c1' is executed.
07:08:21.862 [INFO ] [jsr223.jython.generic_rule_example_1 ] - ExampleClass.method_a()
07:08:21.866 [DEBUG] [jsr223.jython.generic_rule_example_1 ] - event: {state: ON, bb274100a2d011e988629506815ad68e.state: ON, event: TestSwitch1 updated to ON, bb274100a2d011e988629506815ad68e.event: TestSwitch1 updated to ON, module: bb274100a2d011e988629506815ad68e})
07:08:21.870 [DEBUG] [jsr223.jython.generic_rule_example_1 ] - dir(event): ['Entry', 'SimpleEntry', 'SimpleImmutableEntry', '__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__doc__', '__ensure_finalizer__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__str__', '__subclasshook__', '__unicode__', 'class', 'clear', 'clone', 'compute', 'computeIfAbsent', 'computeIfPresent', 'containsKey', 'containsValue', 'copy', 'empty', 'entrySet', 'equals', 'forEach', 'fromkeys', 'get', 'getClass', 'getOrDefault', 'has_key', 'hashCode', 'isEmpty', 'items', 'iteritems', 'keySet', 'keys', 'merge', 'notify', 'notifyAll', 'pop', 'popitem', 'put', 'putAll', 'putIfAbsent', 'remove', 'replace', 'replaceAll', 'setdefault', 'size', 'toString', 'update', 'values', 'wait']
07:08:21.873 [DEBUG] [jsr223.jython.generic_rule_example_1 ] - event.keys: [u'state', u'bb274100a2d011e988629506815ad68e.state', u'event', u'bb274100a2d011e988629506815ad68e.event', u'module'])
07:08:21.876 [DEBUG] [jsr223.jython.generic_rule_example_1 ] - dir(event.items): ['__call__', '__class__', '__delattr__', '__doc__', '__ensure_finalizer__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__str__', '__subclasshook__']
07:08:21.878 [DEBUG] [tomation.core.internal.RuleEngineImpl] - The rule '7602490d-7bfd-44cb-a06d-2884e73ce8ff' is executed.
07:08:24.857 [DEBUG] [jsr223.jython.core.items             ] - Item removed: [TestSwitch1]
07:08:24.859 [DEBUG] [ort.internal.loader.ScriptFileWatcher] - Script loaded: python/personal/generic_rule_example_1.py

I appreciate any hint on this, including a different approach without the use of the extra class RuleTemplate.

Take a look at the extension and raw api examples. Then check out core.rules and the event object attributes

Here is a table of the attributes available in event objects (or inputs['event'] , if using raw API or extensions)

I’m not sure why you couldn’t be using core.rules, but see if this helps…

    def execute(self, module, inputs):
        try:
            self.function(inputs.get('event'))
        except:
            log.error(traceback.format_exc())

Thank you, this worked!

I just could not figure how to use core.rules for this case, it would be great, if you could give an example on how to use that in this case.

Thank you in advance!

I have a few other things that I’m working on, but will take a look at it if/when I ever get to setting up testing for the helper libraries.

Here is the complete file:

# jython imports
import time
import uuid
import traceback

# openhab-helper-libraries imports
import core.items
from core.jsr223.scope import events
from core.log import logging, LOG_PREFIX
from core.triggers import ItemStateUpdateTrigger
from core.utils import getItemValue

scriptExtension.importPreset("RuleSupport")
scriptExtension.importPreset("RuleSimple")

module_name = "generic_rule_example_1"
module_prefix = module_name + "_"

log = logging.getLogger("{}.{}".format(LOG_PREFIX, module_name))

RESPONSE_TIME = 3
'''the time tests are waiting between their actions and
   checking results.
'''
class RuleTemplate(SimpleRule):

    def __init__(self, triggers, f):
        self.setTriggers(triggers)
        self.function = f

    def execute(self, module, input):
        try:
            self.function(input.get('event'))
        except:
            log.error(traceback.format_exc())

class ExampleClass(object):

    def __init__(self, item):
        self.rule1 = automationManager.addRule(RuleTemplate([ItemStateUpdateTrigger(item.name).trigger], self.method_a))
        self.rule2 = automationManager.addRule(RuleTemplate([ItemStateUpdateTrigger(item.name).trigger], self.method_b))

    def method_a(self, event):
        log.info("ExampleClass.method_a()")
        log.debug("event: {}".format(event))
        log.debug("event.getItemName(): {}".format(event.getItemName()))
        log.debug("event.getItemState(): {}".format(event.getItemState()))

    def method_b(self, event):
        log.info("ExampleClass.method_b()")

    def cleanup(self):
        automationManager.removeHandler(self.rule1.UID)
        automationManager.removeHandler(self.rule2.UID)

def scriptLoaded(id):
    ITEM1NAME = "TestSwitch1"
    item1 = core.items.add_item(ITEM1NAME, "Switch")

    exampleObject = ExampleClass(item1)
    log.info("example object created.")
    try:
        time.sleep(RESPONSE_TIME)
        value = OFF if getItemValue(item1, OFF) != OFF else ON
        log.info("postUpdate({}, {})".format(item1.name, value))
        events.postUpdate(item1, value)
        time.sleep(RESPONSE_TIME)
        exampleObject.cleanup()
    except Exception:
        log.error("exception running example")
        log.exception("message")

    core.items.remove_item(item1)

def scriptUnloaded():
    # this executes automatically when the script is unloaded/reloaded. if you create any threads or timers, make sure you cancel/stop them here!
    log.info("script cleanup")