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

Tags: #<Tag:0x00007f434a96f7f0> #<Tag:0x00007f434a96f200> #<Tag:0x00007f434a96edf0>

Was really frustrating figuring that out… wondering if we should try and catch that in the add/remove methods?

Overall, the combination of Java and Python types requires a fair bit of understanding and troubleshooting.

But it still beats DSL anyday :slight_smile:

Michael, I just did a little bit of research on Jython and unicode. From this link, it seems we can replace

isinstance(obj, str)

with:

isinstance(obj, basestring)

since basestring is the superclass of both str and unicode objects.

I agree, despite the Python and Java type issues, I prefer JSR223 Jython to DSL.

1 Like

That looks like a nice solution. And I agree, Jython is far superior to DSL. One just has to remember that you need to think about variable types when working to/from Java.

@5iver

Scott, would like to restart a bundle from Jython. I have been digging through OH github but can’t find what I need - do you have any ideas?

I found the registry and the code below works but haven’t found how to reset a binding

Mike

BindingRegistryInfo = osgi.get_service("org.eclipse.smarthome.core.binding.BindingInfoRegistry")


bis = BindingRegistryInfo.getBindingInfos ()

log.info ('Binding info {}'.format(bis))

I found the documentation on a bundle - and this looks like what is needed to stop/start a bundle. @5iver or @scottk would either of you know where the registry is that we can get the bundle from?

https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fcore%2Fruntime%2FPlugin.html

Sorry for the late reply. I am slogging through a really nasty flu/cold and I’m doing my best to catch up on a lot of things.

This is for another Eclipse project, not Eclipse Smart Home.

Looking through the code and from what I understand about OH/ESH, I’m pretty certain this would not be possible. However, you can start/stop install/uninstall bindings through Karaf, which can be done programmatically. Here is an example using a shell script., and here is a DSL Rule (I have this in Jython too, if you want it). You should setup key auth for this.

I’m curious… why do you need to programmatically restart the binding?

@5iver, no worries…

I use both the ISY99 and Omnilink bindings, both are built on the google guava libraries. Both of these bindings can have problems with reconnecting to their external devices if the connection is lost. At this time, a binding restart seems to be the only way to deal with the issues. I thought doing a bundle restart from python would be cleaner than try to launch a script to tell karaf to do this.

OH has suggested not using guava in the future - but we are not sure if this is specific to that version of any version.

@scottk,

Have started trying this out - items work as expected but a group switch does not seem to generate an event when changed… See below.


openhab.items.add (item="TESTGROUP",item_type="Group", gi_base_type = "Switch", group_function = Or(OnOffType.ON, OnOffType.OFF))
openhab.items.add ("TESTITEM","Switch",groups=["TESTGROUP"])


g =ir.getItem('TESTGROUP')
i = ir.getItem ('TESTITEM')

log.info ('ITEM {}'.format (g))
log.info ('ITEM {}'.format (i))

events.postUpdate ('TESTGROUP',"ON")
events.postUpdate ('TESTITEM',"ON")

log.info ('ITEM {}'.format (g))
log.info ('ITEM {}'.format (i))

events.postUpdate ('TESTGROUP',"OFF")
events.postUpdate ('TESTITEM',"OFF")

log.info ('ITEM {}'.format (g))
log.info ('ITEM {}'.format (i))

openhab.items.remove('TESTGROUP')  
openhab.items.remove('TESTITEM') 

2018-11-10 09:54:03.145 [vent.ItemStateChangedEvent] - TESTITEM changed from NULL to OFF

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

2018-11-10 09:54:03.149 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTGROUP (Type=GroupItem, BaseType=SwitchItem, Members=1, State=OFF, Label=null, Category=null)

2018-11-10 09:54:03.152 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTITEM (Type=SwitchItem, State=OFF, Label=null, Category=null, Groups=[TESTGROUP])

2018-11-10 09:54:03.156 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTGROUP (Type=GroupItem, BaseType=SwitchItem, Members=1, State=OFF, Label=null, Category=null)

2018-11-10 09:54:03.159 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTITEM (Type=SwitchItem, State=OFF, Label=null, Category=null, Groups=[TESTGROUP])

2018-11-10 09:54:03.165 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTGROUP (Type=GroupItem, BaseType=SwitchItem, Members=1, State=ON, Label=null, Category=null)

2018-11-10 09:54:03.168 [INFO ] [eclipse.smarthome.model.script.Rules] - ITEM TESTITEM (Type=SwitchItem, State=ON, Label=null, Category=null, Groups=[TESTGROUP])

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

2018-11-10 09:54:03.178 [vent.ItemStateChangedEvent] - TESTITEM changed from OFF to ON

2018-11-10 09:54:03.185 [vent.ItemStateChangedEvent] - TESTITEM changed from ON to OFF

Hi Michael, several others have noted this behavior for which @5iver opened this GitHub issue:

good - I am not going crazy :slight_smile:

I have some generic scene mode rules that won’t work until this issue is resolved. I hope it doesn’t take long to sort out the problem.

I added a few new changes with the help of @scottk. First, the rule UID is now added as an attribute of the function. Second, you can now optionally set tags for the rules. The third is just a validation check to make sure the rule names are unique, which isn’t a real requirement for rules to have (the UIDs are the unique identifiers… :slightly_smiling_face:), but it makes searching for tags easier. The new rule engine allows for rules to be enabled and disabled, but getting a rule UID can be difficult. These changes and examples will make it easier. Enabling/disabling rules adds a whole new layer of possibilities for automation!

One thing to keep in mind… rules will get new UIDs every time the script is saved, which means it will always be enabled. This means the enabled/disabled status of a JSR223 rule does not persist through an OH restart. Rules created in Paper UI do persist their status though. I’ll submit an issue for this.

Here is an example of a rule that disables itself after the first time it runs:

from openhab.rules import rule
from openhab.triggers import when
from openhab import osgi
ruleManager = osgi.get_service("org.eclipse.smarthome.automation.RuleManager")

@rule("One-shot trigger rule", tags=["Away"])
@when("Item Virtual_Switch_1 received update")
def oneAndDone(event):
    # do something        
    ruleManager.setEnabled(oneAndDone.UID, False)

Here is an example of a rule that enables another rule by name (same imports):

@rule("Enable by name", tags=["Evening", "Away"])
@when("Item Virtual_Switch_1 received update")
def exampleEnableByName(event):
    ruleUID = filter(lambda rule: rule.name == "One and done", rules.getAll())[0].UID
    ruleManager.setEnabled(ruleUID, True)

Here is an example of a rule that will enable/disable all rules with the tag “Away” (same imports):

@rule("Select by tag")
@when("Item Presence changed)
def exampleByTag(event):
    for rule in rules.getByTag("Away"):
        ruleManager.setEnabled(rule.UID, True if event.itemState == StringType("Away") else False)

Here’s basically the same one, but using multiple tags and with some extra logging (same imports):

@rule("Select by tags")
@when("Item Presence changed)
def exampleByTags(event):
    log.debug("JSR223: Test [{}]".format(map(lambda rule: rule.name, rules.getByTags("Away", "Night"))))
    for rule in rules.getByTags("Away", "Night"):
        log.debug("JSR223: Test1: before: [{}]: getStatus=[{}], getStatusInfo=[{}]".format(rule.name, ruleManager.getStatus(rule.UID), ruleEngine.getStatusInfo(rule.UID)))            
        ruleEngine.setEnabled(rule.UID, True if event.itemState == StringType("Away") else False)
        log.debug("JSR223: Test1: after: [{}]: getStatus=[{}], getStatusInfo=[{}]".format(rule.name, ruleManager.getStatus(rule.UID), ruleEngine.getStatusInfo(rule.UID)))
3 Likes

Scott, we should add to the Wiki a list of all globals. I spent a few hours trying understand some weird behavior before I finally realized what was going to :frowning:

dir () gives

['Action', 'ActionBuilder', 'ActionType', 'CLOSED', 'Command', 'Condition', 'ConditionBuilder', 'ConfigDescriptionParameter', 'Configuration', 'CronTrigger', 'DECREASE', 'DOWN', 'DateTimeType', 'DecimalType', 'FASTFORWARD', 'File', 'FileUtils', 'FilenameUtils', 'HSBType', 'INCREASE', 'ImperialUnits', 'IncreaseDecreaseType', 'Item_Occupancy_Event_Metadata', 'Logger', 'LoggerFactory', 'MOVE', 'Metadata', 'MetadataKey', 'MetadataRegistry', 'Metadata_Easy', 'MetricPrefix', 'ModuleBuilder', 'ModuleType', 'NEXT', 'NULL', 'NextPreviousType', 'OFF', 'ON', 'OPEN', 'OnOffType', 'OpenClosedType', 'PAUSE', 'PLAY', 'PREVIOUS', 'PercentType', 'PlayPauseType', 'PointType', 'QuantityType', 'REWIND', 'RawType', 'RewindFastforwardType', 'Rule', 'SIUnits', 'STOP', 'SimpleActionHandler', 'SimpleConditionHandler', 'SimpleRule', 'SimpleTriggerHandler', 'SmartHomeUnits', 'State', 'StopMoveType', 'StringListType', 'StringType', 'StringUtils', 'Trigger', 'TriggerBuilder', 'TriggerType', 'UNDEF', 'UP', 'URLEncoder', 'UnDefType', 'UpDownType', 'Visibility', '__builtins__', '__doc__', 'audio', 'automationManager', 'events', 'ir', 'itemRegistry', 'items', 'log', 'metadata_easy', 'osgi', 'rule', 'ruleRegistry', 'rules', 'scriptExtension', 'se', 'things', 'voice', 'when']

I thought this was pretty well documented here. What were you stepping on?

I guess I should read before I type :slight_smile:

Perhaps, a statement that one needs to be careful before they declare any variables that step on preset globals - there is no warning that you are doing something that will cause you lots of pain :frowning:

1 Like

Scott, is there a way that a rule could be notified it is about to get unloaded? My testing indicates that if you are using a Python timer that if a rule gets unloaded, the timer still persists and is not garbage collected. I have not tested this with Java/DSL timers.

Is it also possible with your new implementation, to set the rulename (at least partially, maybe a random suffix should be created) as UID?

Otherwise the log statements are not human readable

2018-11-13 19:48:20.401 [.event.RuleStatusInfoEvent] - a7b86e52-a729-4bbf-baa6-c4921cc5e249 updated: RUNNING

That would be really great and btw thanks for your great library :wink:

The rule name is being set… take a look in Paper UI> Rules and you will see all of your JSR223 rules listed by name. That log is displaying the rule UID, which can’t be modified through SimpleRule. Changing that log entry should be a really quick fix, and I’ve been meaning to update it for a while now… thank you for the reminder!

1 Like

Not OOTB, that I’m aware of. But a composite module, like the the StartupTrigger.py, may work for you. I’d really like to see a ShutdownTrigger.py get built. But I don’t know how you’d inform the timer to cancel itself. How do you know the timer is not garbage collected? I would think it would have to be, but maybe only after the timer terminated.

Scott, I haven’t dug deeply into this but… it seems timers in Python persist even after rules are reloaded and then they fire. I haven’t had the time to fully understand what is happening. In my use case, the timers are held within an object, and if the object got a “rule unloading” event, the object could clean up before its done.