Design Pattern: Working with Groups in Rules

Tags: #<Tag:0x00007faee5c150e8> #<Tag:0x00007faee5c14f58>

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.

1 Like

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