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

That worked perfectly… need to read more about python. Thank you

Scott, have you done any work with creating items dynamically?

The rule above is part of an Occupancy concept I had been using in another HA software package. Now adopting it to OH.

When I saw your reply, I remembered there is a very slight performance gain using getGroupNames vs group.members…

areaItem = list(group for group in ir.getItem("gArea").members if group.name in ir.getItem(event.itemName).getGroupNames())[0]

Nope.

You may want to hold off until my post this weekend. Think of groups for each area within your house, holding items that would trigger whether the area is active or not. And groups holding the items that should respond to activity in the area. That is what my rule does. The special sauce is in the group functions… takes the logic out of the code and puts it into the groups, Get another light, motion sensor, door sensor, etc., add it to a group, and you’re done… no change in code needed.

That’s very similar to what I am doing. I use tags for each area that describe the duration an area is to be occupied for and the actions to be taken when an area becomes vacant or occupied. Each item in an area also has tags that describe what happens when that item is triggered

Nice! I use a dict (was a HashMap in Rules DSL) to store that info, but would like to use Item metadata. Just haven’t figured out how to access it yet.

Never mind, typo was causing the the problem. getTags() works fine.

Scott, is it possible the item.getTags() is broken? I cannot get it to return the tags of an item that I know has tags…

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

2018-10-12 10:03:45.079 [INFO ] [eclipse.smarthome.model.script.Rules] - gOccupancyItem Changed  TF_Office_MotionSensor (Type=ContactItem, State=CLOSED, Label=Third Floor Motion, Category=motion, Tags=[OE:Motion_Standard], Groups=[gTF_Office, gSS_InteriorMotionSensor, gOccupancyItem])


1 Like

Hey Scott, thanks a lot for this library, it looks very promising.

Is it still compatible with the old rule decorators (e.g. item_triggered)?
I think i read about the compatibility some days ago but now i cannot find it anymore.
I do not find the definitions in your new triggers.py

I would test it, if it doesn’t brake my other rules, so that i can convert a few rules from time to time.

Best regards

Scott, with itemState, for a dimmer, how do we convert that state to a number?

I am trying to work with an if statement that can deal with either the OnOffType or a numerical value for the state.

found it :slight_smile:

if triggeringItem.state > DecimalType (0):

At first I thought they would be, but they are not. I could take a look to see if there is anyway to add backwards compatibility. But you’re using a milestone or snapshot, right? I do not plan to back port the new decorators for OH 2.3.

yes i am using the latest milestone (i think it should be m4)
i would be really great if you could add the backwards compatibility, otherwise it would be a big hurdle to switch to your great library

thanks in advance :slight_smile:

I’ll look into it, but it really shouldn’t be that difficult to use both. Just rename your triggers.py to triggers_old.py (or whatever you want), modify your imports to use the new name, and drop in the updated modules.

1 Like

Hi Scott,

I just wanted to thank you for your magnificent work!!

Today I finally had time to update openHAB to the 2.4 M4 Milestone and install your library. Now I am looking forward to testing it out!

Juelicher

1 Like

Is it possible to use a group item as “event” and only do something when the group state changed and not the triggering item?

Example:

I have a Group:Switch:OR(ON, OFF) with 2 switches as members.
I only want to do something when the state of the group changed either from ON to OFF or the other way. My problem is that event.oldItemState and event.ItemState contain the states of the triggering item but not the group. I hope it is more or less understandable what i mean otherwise i could copy some code when i am home…

Thanks :slight_smile:

Are you defining the trigger like this? If so, I’ll do some testing.

@when("Item gMotion_Sensors changed")

That works totally fine, i was just confused on how this library behaves compared to the previous one…

That brings me to my next question, because this also behaves differently from what i know from the old lib:

I have an item which is a window contact state (Ct_Dc_State_Livingroom). This belongs to a group of all window contacs of the living room (gWindowcontacs_Livingroom) which belongs to a group with all window contacs in the house (gWindowcontacs). How can i use the top level group as trigger so that event.itemName gives me window contact item that changed?

@when(“Member of gWindowcontacs changed”) gives me event.itemName = “gWindowcontacs_Livingroom”

@when(“Descendent of gWindowcontacs changed”) gives me event.itemName = “gWindowcontacs”

but i would like to get the item “Ct_Dc_State_Livingroom” that actually triggered the whole rule.

Thanks in advance :slight_smile:

This is what should work for you… but I did a quick test and found a typo :roll_eyes:. There was a missing ‘s’ in descendent at line #176. Thank you for reporting this!

You can correct the file yourself, or download the latest triggers.py. Either way, you’ll need to restart OH, or use this trick in your rule to reload the module (add this, then save the rule, then clear it out and resave the rule). I did it like this…

import sys
from openhab.triggers import when
reload(sys.modules['openhab.triggers'])
from openhab.triggers import when

Something to note… the newly released M5 milestone build includes a fix for ItemStateTriggerHandler, which will correct some issues in rules with groups in their triggers. Now might be a good time to upgrade!

1 Like

Hi Michael, I’ve just submitted a PR for an enhanced implementation of the add() method found in lib/python/items.py. The enhancement provides a complete implementation of an item creator, which includes the ability to specify category, group membership, and label. For group items, the base item type and a group function may be specified.

Here is my modified version of items.py:

# NOTE: Requires JythonItemProvider component

from org.slf4j import Logger, LoggerFactory

from openhab import osgi, jsr223, JythonItemProvider
from openhab.jsr223 import scope

__all__ = ["add", "remove"]


logger = LoggerFactory.getLogger("org.eclipse.smarthome.model.script.Rules.jsr232.openhab")

def add(item, item_type=None, category=None, groups=None, label=None, gi_base_type=None, group_function=None):
    try:
        if isinstance(item, str):
            if item_type is None:
                raise Exception("Must provide item_type when creating an item by name")

            baseItem = None if item_type != "Group" or gi_base_type is None else scope.itemRegistry.newItemBuilder( gi_base_type       \
                                                                                                                  , item + "_baseItem")\
                                                                                                   .build()
            group_function = None if item_type != "Group" else group_function
            item = scope.itemRegistry.newItemBuilder(item_type, item)  \
                                     .withCategory(category)           \
                                     .withGroups(groups)               \
                                     .withLabel(label)                 \
                                     .withBaseItem(baseItem)           \
                                     .withGroupFunction(group_function)\
                                     .build()

        JythonItemProvider.add(item)
    except:
        import traceback
        logger.error(traceback.format_exc())

def remove(item):
    JythonItemProvider.remove(item)

… and a test that illustrates its usage:

"""
This example demonstrates item creation.
"""

import unittest
import time

from org.eclipse.smarthome.core.library.types.ArithmeticGroupFunction import And, Or
from org.slf4j import Logger, LoggerFactory

from openhab.testing import run_test
import openhab.items
reload(openhab.items)
import openhab.items


logger = LoggerFactory.getLogger("org.eclipse.smarthome.model.script.Rules")
    
class MyUnitTest(unittest.TestCase):
    def setUp(self):
        try:
            groupItems = {"gItem_0": Or(OnOffType.ON, OnOffType.OFF), "gItem_1": And(OnOffType.ON, OnOffType.OFF)}
            for iname, gfunc in groupItems.items():
                openhab.items.add(item = iname, item_type = "Group", gi_base_type = "Switch", group_function = gfunc)

            newItems = {"Item0": "Switch", "Item1": "Dimmer", "Item2": "Dimmer", "Item3": "Switch"}
            for iname, itype in newItems.items():
                openhab.items.add(iname, itype)

            gItem = ir.getItem("gItem_0")
            for member in ("Item0", "Item2"):
                gItem.addMember(ir.getItem(member))

            gItem = ir.getItem("gItem_1")
            for member in ("Item1", "Item3"):
                gItem.addMember(ir.getItem(member))
        except:
            import traceback
            logger.error(traceback.format_exc())

    def tearDown(self):
        itemNames = ("gItem_0", "gItem_1", "Item0", "Item1", "Item2", "Item3")
        for iname in itemNames:
            openhab.items.remove(iname)

    def test_item(self):
        itemNames = ("Item0", "Item1", "Item2", "Item3")
        for item in itemNames:
            events.postUpdate(item, "OFF")

        time.sleep(1)
        for item in itemNames:
            self.assertEqual(items[item].as(OnOffType), OnOffType.OFF)

        self.assertEqual(items["gItem_0"], OnOffType.OFF)
        self.assertEqual(items["gItem_1"], OnOffType.OFF)

        itemStates = {"Item0": "ON", "Item1": "33", "Item2": "67", "Item3": "OFF"}
        for item, cmd in itemStates.items():
            events.postUpdate(item, cmd)

        time.sleep(1)
        for item, state in itemStates.items():
            logger.info("item: {}".format(ir.getItem(item)))
            self.assertEqual(str(items[item]), state)

        self.assertEqual(items["gItem_0"], OnOffType.ON)
        self.assertEqual(items["gItem_1"], OnOffType.OFF)


run_test(MyUnitTest)

2 Likes

awesome, I will dig into this weekend.

I am trying to determine how to import modules from github. My thoughts are they should be installed in the OH config directory so they can be included in backups.

Not sure best way to do this - PIP? Pull the repository from github?