Enable / Disable a Rule in Jython

Writing Rules in JSR223 Jython …

Would there be any benefit for disabling a rule that wont be needed. Would this improve performance, would it be worth it? does anyone do this and why? Also, is there a way to enable/disable a rule programmatically in Jython? Obviously I can achieve this with a simple switch test, but I’m wondering if there is anything built-in to do this so the rule doesn’t get called at all. Or, if there is any performance benefit to bother.

i dont know if it would improve performance, for example i deactivate rules that control my shutters. one rule for each shutter to have individual open / close times and to setup for each shutter if shutter must not close if window is open…

somewhere here in the forum i found an example and played around with it. perhaps you can adjust it to your needs:

"""
This example shows how to retrieve the RuleRegistry service and use it to query rule instances based on tags,
enable and disable rule instances dynamically, and manually fire rules with specified inputs.
Requires a rule with a "Test tag" tag.
Tags can be set in the rule decorator or rule constructors (self.tags = ["tag1", "tag2"]).
"""
from core.rules import rule
from core.triggers import when
from core import osgi

ruleManager = osgi.get_service("org.eclipse.smarthome.automation.RuleManager")


import time

from core.osgi import get_service
from core.log import logging, LOG_PREFIX

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

# Get rules by tags
# Tags can be set in rule constructors
# Example: self.tags = ["tag1", "tag2"]
rule_registry = get_service("org.openhab.core.automation.RuleRegistry") or get_service("org.eclipse.smarthome.automation.RuleRegistry")
rules_with_tag = rule_registry.getByTag("Hundemelder")

for rule in rules:
    rule_status = rule_registry.getStatusInfo(rule.UID)
    log.info("Rule name=[{}], description=[{}], tags=[{}], UID=[{}], status=[{}]".format(rule.name, rule.description, rule.tags, rule.UID, rule_status))

    # disable a rule
    rule_registry.setEnabled(rule.UID, False)

    # later...
    time.sleep(5)

    # reenable the rule
    rule_registry.setEnabled(rule.UID, True)

    # fire the rule manually (with inputs)
    log.info("Triggering rule manually")
    rule_registry.runNow(rule.UID, False, {'name': 'EXAMPLE'})

so for example this is a function that disables itself and re-enables itself after three seconds:

@rule("Dachboden Lichttimer",\
    description="Schaltet nach 30 Minuten das Licht wieder aus")
@when("Item iDachboden_Li received update ON")
def dachboden_lichttimer(event):
    ruleManager.setEnabled(dachboden_lichttimer.UID, False)

    sendCommand("iJython_Busbefehl", "*#1*67#4#01*#2*0*30*0##")
    LogAction.logInfo(u"iJython_Busbefehl", u"Licht Dachboden für 30 Minuten eingeschaltet")

    ScriptExecution.createTimer(ZonedDateTime.now().plusSeconds(3), lambda: ruleManager.setEnabled(dachboden_lichttimer.UID, True))
1 Like

I don’t think it would be worth it. A rule doesn’t do anything if it’s never triggered.

FYI, you can use UI rules to enable/disable other UI rules. I don’t think you can use this to control rules in text files, but I’m mentioning it in case you’re interested and for future readers.

I use virtual items as flags to determine when rules should run with if conditions. If I ever get around to moving my rules to the UI, then I might instead use those flags to enable/disable rules. For example, some rules will only be enabled when I’m at home, and others will be enabled when I’m away or asleep.

Technically, this does offer a performance improvement. As things are, I’m triggering rules and then immediately exiting them. But it costs almost nothing in time/resources to carry out that task, so I can’t imagine there being a noticeable real-world difference.

It might get triggered often by various Motion sensor but the logic in that rule is not needed (for whatever reason)

Yeah, sorry. I should have edited that sentence. What I meant was that if the rule isn’t doing anything (which I assumed to be the case since you don’t need the rule to be enabled), then there’s no major performance hit.

I guess if you’re doing stuff in the rule and then not using it, you’ll see more impact. But I’d still be surprised if it’s enough to make disabling worthwhile.

2 Likes

Agreed … it probably doesn’t matter for performance, but I can see some use cases … maybe (but still might be better to just do the qualifying within the rule.)

Thanks Stefan … I’m playing around with the code now … sorry, but it’s not clear how to set a tag on a rule… where is that done?

The way I see it, there’s nothing wrong with doing it just for heck of doing it. That’s why I think of home automation as a hobby. :wink:

1 Like

just at the beginning of the rule, after the description, for example:

@rule("Hundemelder",\
    description="Bei Bewegung im EG-Flur blinkt Licht im Schalfzimmer",\
    tags=["Hundemelder"])   # description and tags are optional
@when("Item iEGFlur_PIr received command ON")
def hundemelder(event):
    ...

but as you see in my example futher up i select the rule that i want to deactivate / activate by its uid - thats just the name of the rule and easier, at least for me :smile:

1 Like

Ok … I was typing “tag” :roll_eyes:

But now I am getting an error:

2022-07-28 11:35:01.107 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/python/personal/Hello_world.py': AttributeError: 'org.openhab.core.automation.internal.RuleRegistryI' object has no attribute 'getStatusInfo' in /etc/openhab/automation/jsr223/python/personal/Hello_world.py at line number 754

Wondering why the “RuleRegistryI” has an “I” at the end … it isn’t in the code.

you are right, sorry, i also cannot get it work. as i wrote this was an example that i found, tried a little and then used it like in my second example. but this works as i use it often.

1 Like

No problem … I will figure it out (i’m heading out for a few days) … but thank you for this … learned a bunch.

ok, one more try, this works:

from core.rules import rule
from core.triggers import when
from core import osgi
from core.actions import LogAction


ruleManager = osgi.get_service("org.openhab.core.automation.RuleManager")

@rule("tic_toc")
@when("Time cron 0/5 * * * * ?")
def tic_toc(event):
    LogAction.logInfo("tic_toc", "rule tic_toc triggered")


@rule("on_off_rule")
@when("Item ivi changed")
def on_off_rule(event):
    thing_manager = osgi.get_service("org.openhab.core.thing.ThingManager") or osgi.get_service("org.eclipse.smarthome.core.thing.ThingManager")
    if items["ivi"] == ON:
        LogAction.logInfo("on_off_rule", "rule tic_toc enabled")
        ruleManager.setEnabled(tic_toc.UID, True)
    else:
        LogAction.logInfo("on_off_rule", "rule tic_toc disabled")
        ruleManager.setEnabled(tic_toc.UID, False)

i use a switch item called ivi. when i switch off the cron rule will be disabled, wenn i switch on the cron rule will log every 5 seconds

1 Like

Great thanks Stefan, I’ve got your original working too … it should have been using ruleManager instead of the rule_registry.

Appreciate your help!

There are use cases where disabling a rule makes a lot of good sense.

Not enough to measure.

Depends on the use case.

Not in Jython since I don’t use it any more, but in general. I currently mainly use this approach because at Christmas time I repurpose some smart plugs that drive room humidifiers for use to control the Christmas lights. Obviously it’s completely different rules so I have a TisTheSeason Switch Item and a couple of rules that disable the humidifier rules and enable the Christmas lights rules when it’s ON and vice versa when it’s OFF.

I could have added that as a Condition to the rule (I use UI rules) but from a design perspective disabling the rules in this way is simpler and more obvious to future me what is going on. I also don’t like to mix stuff together. The humidifier rules should really just care about humidity, not that it’s Christmas time.

Yes.

It can be done in pretty much anything except Rules DSL and Blockly (for now).

@bastler has you covered for Jython.

In the UI when adding an Action choose “Other Rules”:

In JS Scripting there’s a helper in the library.

rules.setEnabled('rule:uid', true);

Note: I don’t know if this has changed or not but it used to be the case in Jython that the rule’s UID was randomly generated each time it’s loaded. So you’d have to look the rule up by name. In JS Scripting when using text files you have the option to set the UID or else it will be randomly be generated too. In the UI the UID gets fixed at the time of creation of the rule and it won’t change.

1 Like

Just curious Rich, what was the reason for moving away from jython and what is your preferred scripting language now (for openhab)? Maybe you already answered this in a previous post :slight_smile:

This was also my main use case I was considering.

There are a lot of things really but ultimately it boils down to the following:

  • Jython is really on shaky ground. It’s still Python 2.7 which is now almost two years past end of life. There is no indication that a Python 3 version will ever be released and there isn’t a lot of activity on their repo so it will probably go belly up at some point and we’ll be forced to remove support.

  • Using Jython without the Helper Library is really not worth it. But the Helper Library never actually became a part of the openHAB project proper. When I gently pushed to make that happen the maintainers resisted. And we got to experience what problems that can cause after OH 3 was release and the maintainer of the Helper Library disappeared. The library had to be forked and maintained (and continued to be maintained) in that fork. So to use it one has to clone and install a git repo to use Jython effectively which is the opposite of new user friendly. I need to focus my efforts more towards what beginner users will gravitate towards.

  • Of all the languages supported by openHAB, only Blockly and JS Scripting have taken the quirks and unique issues that arise for users writing rules in the UI seriously (Blockly by necessity, JS Scripting because I was able to convince the maintainers that some allowances needed to be made for those users). This means, in my opinion, JS Scripting is the primary candidate as a default advanced language. This will likely be the language users who outgrow pure UI rules or Blockly will most likely migrate to, unless they already have an affinity for a different language. So that’s what I use. Note, JS Scripting is also the only “advanced” language whose Helper Library comes with the add-on and doesn’t require installing it from somewhere else. It’s part of the openHAB project, not maintained outside it as well so if a maintainer disappears someone can step up to take over.

  • The JS Scripting helper library is really good. Everything is wrapped so, with few exceptions, you are only ever working with JavaScript Objects. You almost never have to stop and consider “is this a Java int or a JavaScript Number?” It’s all JavaScript meaning you can do JavaScript things with it in JavaScript ways. So many problems and so much confusion comes from type issues caused by the mix of language native and Java Objects.

  • The JS Scripting add-on and helper library are under active development and much of the development going on there is also improving support for all the other languages too.

  • I don’t have strong opinions about individual languages themselves. Heck, if there was support for Perl 5 or TCL that met all the same requirements that JS Scripting meets I’d choose and promote them too (even though programming in them is really arcane). So I’m not a Python or JS Scripting or Ruby partisan. All things considered equal, if I were not concerned about maintaining my ability to support newer users, I’d probably choose HABAp (Python 3 but outside of OH) or jRuby (mature and capable but the helper library isn’t well documented in the official docs, isn’t part of the openHAB project, and only relatively recently paid much attention to UI rules writers) as my rules environment. I don’t hate JS but Python and Ruby are closer to how I think.

So all my rules are in the UI and written in JS Scripting. I exclusively use UI rules now because:

  • it’s the only way to write rule templates which can be published to the marketplace (you can now install rules like add-ons)

  • it’s most likely what new users will migrate to

  • I like the option to be able to do quick adhoc adjustments to rules remotely

  • I love the addition of the conditional part of the rule (the But only if… section)

  • performance seems better but I’ve not measured it.

2 Likes