Design Pattern: Working with Groups in Rules

I just uploaded some code to github that uses some of the techniques in this pattern.

I have temperature sensors and smart vents in a bunch of different rooms in my house, and I use this code to manage the smart vent states. I have it set up so that I can use the thermostat set point for heat/cool target or override the target temperature by room. It is also possible to turn individual room control on and off manually or via other external rules.

Just thought I’d share in case it’s useful to anybody else.

2 Likes

Is there any solution to this problem with DateTime groups, @rlkoshak … 2 years later? :wink: According to the openHAB docs MIN/MAX should work on decimal types only.

Could you elaborate, @davorf?

The updates to the docs are the solution. This used to work in OH 1.8 but did what ever reason, they decided not to support DateTime any more. There were lots of bugs and changes necessary to the aggregation functions at that time which probably broke DateTime, and the fact it worked before that may have been a fluke.

I need to update the tutorial.

1 Like

Currently I am doing this:

group.members.forEach[ i |
            i.sendCommand(OFF)
        ]

        sceneGroup.members.forEach[ i |
            i.sendCommand(ON)
        ]

However its a bit unnecessary to switch lights off then on again, so I would like to do something like this:

group.members.forEach[ i |
        if i.memberOf( sceneGroup) {
            i.sendCommand(ON)
        else{
          i.sendCommand(OFF)
        }
        ]

but in the examples I can not figure how to figure out if an item is member of a group

First idea is to use simthing like (Not tested)

sceneGroup.members.filter[ j | j.name == i.name].size > 0
1 Like

I’m using the following rule to alert me when my plants need water:

rule "Planten water geven"
when
    Member of gMoistures changed //or
//    Item test_switch changed
then
    val StringBuilder sb = new StringBuilder
    gMoistures.allMembers.filter[ i | i.state <= 30].forEach[i | sb.append(transform("MAP", "plants.map", i.name)  + "\n") ]
    if (sb.length < 1){
        return;
    } else { 
    logInfo("notifications", "Sending notification via mail." + gMoistures.members.filter[ e | e.state <= 30])
    sendMail("xxx", "Planten water geven", "De volgende plant(en) heeft/hebben dorst: \n\n" +sb)
    }
end

But I have the idea I’ve overcomplicated things. When I use Member of gMoistures changed the rule only runs on the member that triggered the rule, right? Isn’t there an easier way to only check the item that trigger the rule for <=30 instead of searching all group members for that value?

Yes you can simplyfy that. Just use triggeringItem to access the item that triggered the rule

1 Like

I think that is essentially correct, but I’d put any if-condition in brackets
if ( i.memberOf(sceneGroup) ) {
What happens when you try it?

Rule ‘Update lights’: ‘memberOf’ is not a member of ‘org.eclipse.smarthome.core.items.Item’; line 37, column 8, length 22

That works, I assume its a bit slow if you have big groups…

group.members.forEach[ i |
    if (i.getGroupNames.contains("sceneGroup") {
        i.sendCommand(ON)
    else {
        i.sendCommand(OFF)
    }
]

So after several testing now, here is the conclusion:

  1. if i.memberOf( sceneGroup) { FAILS
  2. sceneGroup.members.filter[ j | j.name == i.name].size > 0 SLOW
  3. if (i.getGroupNames.contains(sceneGroup.name) { FAST

@5iver thanks for the tip, however you had a small typo:

 if (i.getGroupNames.contains("sceneGroup") {

should be

 if (i.getGroupNames.contains(sceneGroup.name) {

@rlkoshak maybe you can add this method to your orginal post of how to find out if an item is a member of a group.

1 Like

Huh? sceneGroup.name equals “sceneGroup”.

Not quite: sceneGroup.name can be like this…

Group Group_Livingroom_Lights_Kitchen
Group Group_Livingroom_Lights_Lounge

See my design pattern for more info.

The former should be: all the direct members of the Group. In other words, if you define:

Group grpA
Group grpB (grpA)
Group grpC (grpA)

String strA (grpA)
String strB (grpB)
String strC (grpC)

Then grpA.members returns the following items:

  • grpB
  • grpC
  • strA

And grpA.allMembers returns only non-group items by resolving all groups into their items. In the example above, grpA.allMembers returns the following items:

  • strA
  • strB
  • strC
1 Like

How would you archive this in Jython?

Im trying to get all members of a GroupA which are also members of GroubB.
Without Iterating over 100 of items.

=> Say All “gTemperatures” in “gLivingroom” - Same as the Doc Group Example from “Items”.

// Example Item
Number Livingroom_Temperature "Temperature [%.1f °C]" (Livingroom, Temperatures)

I never used Jython myself (although I will probably start to in the near future), but I think you can just use two list comprehensions to achieve this (somebody correct me if I am wrong with this :slight_smile: ):

temperature_list = [i.name for i in ir.getItem('gTemperatures').members]
livingroom_temperatures = [i for i in ir.getItem('gLivingroom').members if i.name in temperature_list]

you could also make two sets out of your groups and intersect them.

I answered someone else a couple days ago (I’ll add this to the docs)…

Here are a couple more examples that will be in the helper library docs…

        list_of_items = [item for item in itemRegistry.getItem("gDS_FamilyRoom").members if "gMotion_Sensor" in item.groupNames]

        # or
        list_of_items = [item for item in itemRegistry.getItem("gDS_FamilyRoom").members if item in itemRegistry.getItem("gMotion_Sensor").members]

        # or
        list_of_items = [item for item in itemRegistry.getItem("gMotion_Sensor").members if item in itemRegistry.getItem("gDownstairs").allMembers]
2 Likes

Hello,

thanks for Pattern!
I have put all items in test.items file and unfortunatly i get error

2019-12-05 12:44:50.050 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'test.items' has errors, therefore ignoring it: [7,57]: missing EOF at '('

It goes away only if i delete all the “(gDoorsTimers)” from file.
Pi4 OH2.5 M5
What can it be?
Thanks!

There’s something wrong in your rules file that we cannot see from here. I’ll guess it may be a copy-paste problem about quotemarks

Spot the difference in highlighting -

fred=“some text”
mary="more text"

You must use the correct type of “plain” quotemarks in xxx.items files.