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

Maybe I am just being dense here, but I do not understand your comment about “Notice in the logs of globals there is only one entry for mainFunction.” I see two log entries for “objects at global scope” at timestamps 07:53:09.831 and 07:53:09.904 – one for mainFunc at 0x06 and one for mainFunc at 0x07.

Hi, Bob. In the code I posted, there are two places where the same two log sequences occur, the first occurs after the rule and when decorators have run for the first definition of mainFunction (body of mainFunction at location 0x06) and the second after those decorators have run for the second definition of mainFunction, for which the log entry shows mainFunction at location 0x07. This shows that the location of the code that will run when mainFunction runs changed from the first to the second definition of mainFunction. Because the decorators were executed once before the second definition, they captured the location of the body of the first definition. The second time the decorators were executed, they captured the then current location of the body of mainFunction at 0x07.

Hi again everyone

I’ve been struggling with removing rules via Jython, and was wondering if anyone could point me in the right direction. I’m calling rules.remove(<rule UID>) but getting a None result back (I believe this should be returning the rule itself) and the rule still appears to be present. It also continues to show in Paper UI and comes up in karaf as well.

import inspect
import org.eclipse.smarthome.config.core.Configuration
from openhab.rules import rule
from openhab.triggers import when
from openhab.log import logging

from datetime import datetime, timedelta
LOG_BASE = "org.eclipse.smarthome.model.script.JS223.ufh"

@rule("UFH Cron Manager")
@when ("Member of gUFHScheduleTime received update")
def ufhCronManager(event):
    log = logging.getLogger(LOG_BASE + "." + inspect.currentframe().f_code.co_name)
    log.info("[DEBUG] Rule triggered. event = {}".format(event))

    rule_name = event.itemName + " Cron Rule"
    filtered_rules = filter(lambda rule: rule.name == rule_name, rules.getAll()) 
    log.info("[Log 1] Filtered list of rules: {}, len = {}".format(filtered_rules, len(filtered_rules)))

    cron_rule = filtered_rules[0] if len(filtered_rules) >0 else None
    if cron_rule:
        log.info("[Log 2] Rule for item '{}' exists with UID {}. Removing.......".format(event.itemName, cron_rule.UID))
        log.info("-------> Removal result: {}".format(rules.remove(cron_rule.UID)))

    filtered_rules = filter(lambda rule: rule.name == rule_name, rules.getAll()) 
    log.info("[Log 3] Rules collection post removal of cron_rule: {}, len: {}".format(filtered_rules, len(filtered_rules)))
    cron_rule = filtered_rules[0] if len(filtered_rules) >0 else None
    if cron_rule:
        log.info("[Log 4]  Double check shows rule with UID {} STILL EXISTS after deletion (len = {}). Abandoning....".format(cron_rule.UID, len(filtered_rules)))
    else:
        log.info("[Log 5] Creating new rule...")
        @rule(rule_name)
        @when("Time cron 0 " + str(datetime.now().minute + 2) + " " + str(datetime.now().hour) + " * * ?")
        def newcronrule(event):
            log = logging.getLogger(LOG_BASE + ".newCronRule")
            log.info("[Log 6] CronRule execute called: {}, type {} .................................................".format(event, type(event)))

The log results show:

2018-11-28 22:33:54.325 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [DEBUG] Rule triggered. event = UFH_MasterEnSuite_SchedOnTime2 updated to 1820
2018-11-28 22:33:54.329 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 1] Filtered list of rules: [], len = 0
2018-11-28 22:33:54.332 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 3] Rules collection post removal of cron_rule: [], len: 0
2018-11-28 22:33:54.334 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 5] Creating new rule...
2018-11-28 22:33:54.347 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [DEBUG] Rule triggered. event = UFH_MasterEnSuite_SchedOnTime2 updated to 1810
2018-11-28 22:33:54.349 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 1] Filtered list of rules: [org.eclipse.smarthome.automation.core.internal.RuleImpl@45d591ef], len = 1
2018-11-28 22:33:54.351 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 2] Rule for item 'UFH_MasterEnSuite_SchedOnTime2' exists with UID 4f6c9220-74a0-4885-afcf-52985f503019. Removing.......
2018-11-28 22:33:54.353 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - -------> Removal result: None
2018-11-28 22:33:54.355 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 3] Rules collection post removal of cron_rule: [org.eclipse.smarthome.automation.core.internal.RuleImpl@45d591ef], len: 1
2018-11-28 22:33:54.358 [INFO ] [odel.script.JS223.ufh.ufhCronManager] - [Log 4]  Double check shows rule with UID 4f6c9220-74a0-4885-afcf-52985f503019 STILL EXISTS after deletion (len = 1). Abandoning....

Interestingly, karaf also doesn’t seem to remove the rule, claiming that the id does not exist. I’ve tried using both the ‘ID’ value and ‘UID’ as listed by karaf’s listRules but both come back with the same error:

openhab> smarthome:automation listRules
----------------------------------------------------------------------------------------------------
ID     UID                                 NAME                                STATUS
----------------------------------------------------------------------------------------------------
1      1a94476a-f3ce-4392-95e4-a257bafa4417 MasterEnsuite UFH Cron1             IDLE
2      29922cc3-63e2-44ea-b87f-417a68c40bb3 UFH_MasterEnSuite_SchedOnTime2 Cron Rule IDLE
3      66be4353-c651-46dd-851e-62d6420a0c7a MasterBedroom Xiamoi Switch Ch2     IDLE
4      732279c9-847b-45cb-80ab-ad1ca09649f9 Light_FF_MasterEnsuite_Spots changed IDLE
5      79de5b51-b6c0-43e2-b131-db7f3083e235 Office_MotionSensor_Last changed    IDLE
6      7f231d45-1208-420e-a2ff-68fac77d201d MasterBedroom Xiamoi Switch Ch1     IDLE
7      80a50241-70e3-4b07-b9ad-2852eef83988 This is the name of a test rule     IDLE
8      a54bba71-5b3f-47db-a5f9-222d8d15359b Motion Sensor Triggered             IDLE
9      bcba078b-b81d-4ecc-bb96-564e8732647e UFH Cron Manager                    IDLE
----------------------------------------------------------------------------------------------------

openhab> smarthome:automation removeRule 29922cc3-63e2-44ea-b87f-417a68c40bb3
Rule with id '29922cc3-63e2-44ea-b87f-417a68c40bb3' does not exist.

Finally, I’ve also tried using the REST api with the same UID, but again the rule just will not delete…

Am I missing something obvious here?

Following on from my previous post on issues with removing rules, it seems as if the solution is to use ruleRegistry.remove and not rules.remove - this was picked up by @Spaceman_Spiff - Port JSR223 bundle to openHAB 2 - #160 by Spaceman_Spiff . @steve1 also mentions in the same thread:

There are a few scope objects that are created for each script. I think that the automation manager and rule registry are two of those.

This may explain the difficulties I had in removing the rules via REST/karaf etc. Anyway, I haven’t tested extensively, but looking good so far.

@smar , I took a look this morning and found Karaf worked properly. For some reason, my REST API isn’t working at all in build 1445, so I couldn’t test that. In a script, I could also delete a rule with…

rules.remove("568add56-ce52-4be5-a178-1e9c0a535217")

So, deleting rules is working for me. You might want to just try a simple test to confirm for yourself. Create a rule in Paper UI, copy the line above into a test script using the UID from your test rule, save the file, and verify the rule has been removed from Paper UI (I had to refresh it).

rules is an instance of org.eclipse.smarthome.automation.RuleRegistry provided in the default scope.

Thanks @5iver (and I see now that rules is an instance of ruleRegistry).

It seems as if the issue I’ve been experiencing is only with rules that are dynamically created from within my main rule. Rules that I create in PaperUI are correctly removed from both karaf and via rules.remove. Rules created dynamically give the unknown ID message in karaf, but delete correctly from within the same script used to create the rule. Is this a scope issue, such as the one Steve mentioned in the other post?

EDIT: On further testing (build 1445 as well), rules.remove definitely does not remove a rule that was created dynamically using the code I gave previously. It does remove rules created in paperUI. However, ruleManager.remove works for the dynamic rules as well. I haven’t had a look a the source yet, but there certainly seems to be some difference in the way the two work, beyond rules just being an instance of ruleManager (a quick type shows rules as <type function> and ruleManager as <type 'org.eclipse.smarthome.automation.core.internal.RuleEngineImpl').

Hi all,

I’m planning to migrate from DSL to Jython soon.
I was wondering if it is possible to use the new MQTT action in Jython since this PR (https://github.com/eclipse/smarthome/pull/6579) has been merged a week ago?
Will it work out of the box or does it also need to be implemented in the Jython repo?
I know that it is possible to use the paho client but it looks cleaner to me if the action can be used.

Greetings,
Frederic

Based on this, I do not believe so. However, there isn’t much you can’t get to when using JSR223, so it may still be possible (haven’t tried).

Started working on changing some code to use Jython’s ability to add/remove items and saw this in the logger - this is on 2.4M7.

[WARN ] [core.internal.items.ItemRegistryImpl] - Deprecation: You are using a deprecated API. Please use the ItemBuilder OSGi service instead.

Michael (@mjcumming),

I’ve been seeing the same. I’ve tried to track down the referenced service, at least how to get an instance without success. Please post here if you learn more. I’ll do the same if I should sort this out.

Hi Scott, I have done some digging but haven’t found the source either but that doesn’t really mean much :slight_smile:

Quick question - do you know how to determine if an item exists in the registry -> I need to check prior to creating the item if it exists or not and getItem, if the item doesn’t exist generates an error.

Without having tested it, maybe this will work?

if "itemName" in items:
    ...

I suspect there is a more direct way to check for the existence of an item, but the following code snippet shows how I do it:

            item = None
            try:
                item = ir.getItem(name)
            except:
                pass
            else:
                logger.debug("removing item: %s" % item.name)
                openhab.items.remove(item)

I am wondering if there might be a bug in the documentation. According to the docs the following should work, to get the maximum value using persistence:

PersistenceExtensions.minimumSince(ir.getItem("Weather_SolarRadiation"), DateTime.now().minusHours(1)).doubleValue()

If I try this, I get the following error:

AttributeError: 'org.eclipse.smarthome.model.persistence.extensions' object has no attribute 'doubleValue'

What does work for me is the following (added the .state):

PersistenceExtensions.minimumSince(ir.getItem("Helligkeit_Terrasse"), DateTime.now().minusMinutes(5)).state.intValue() 

I tried this with .doubleValue() as well and this works too.

There is

getItems from

Just need to figure out how to iterate over a HashMap from Jython

I don’t know if it is the best/right way to do it, but this works. Could you submit a PR for it? If so, please create a branch off of the restructuring branch (plan to merge this weekend). If not, I’ll get to it eventually :wink:.

from core import osgi
itemBuilderFactory = osgi.get_service("org.eclipse.smarthome.core.items.ItemBuilderFactory")
testItem = itemBuilderFactory.newItemBuilder("Switch", "Test_Switch_5").withLabel("Scripted item").build()
log.debug("JSR223: Test: [{}]".format(testItem.name))

Here is ItemBuilderFactory

Yep… I forgot the .state! I’ve included the correction in this PR and removed the doubleValue(). Thank you for pointing this out… and don’t hesitate to submit your own corrections/updates/additions too!

You might want to try out (the next method down from getItems)…

ir.isValidItemName("My_Item_Name")
1 Like

from my read, isValidItemName only checks if the name is valid not if it exists

Oops… I read through it too quickly… totally not what you needed!

turns out you can do

if ir.getItems('itemname')

returns 1 (true) if the item name exists or 0 if it doesn’t

ir.getItems returns a tuple that you can iterate using list comprehensions.

1 Like