Find group containing an item where the group is nested

I have the following groups:

Group               gRoom (gHouse)
Group               gBlueRoom               "Blueroom"                  (gRoom)
Group               gBathroom               "Bathroom"                  (gRoom)
Group               gMotionDetector         "Motion Detector"

And have a rule with the following trigger:

rule "Motion Detected"
when
	Member of gMotionDetector received update
then

I have a zwave motion detector that is a member of gBlueRoom and also a member of gMotionDetector. I would the rule to be able to determine the group directly below gRoom that contains the item that triggered the rule. I can find the groups in gRoom but I can’t seem to find the correct syntax to iterate through these groups and in turn check whether any of the members of these groups contain the triggeringItem. My code so far:

	val StringBuilder sb = new StringBuilder
	//gRoom.members.filter[roomGroup| roomGroup instanceof GroupItem].forEach[roomGroup | sb.append(", " + roomGroup.name) ] //this works!
	gRoom.members.filter[i| i instanceof GroupItem].members.filter[device | device.name == triggeringItem.name].forEach[device | sb.append(", " + device.name) ]

In the third line I’m attempting to filter the members of i with the condition that their name must match that of the triggeringItem. However, this code produces a syntax error:

Rule 'Motion Detected': 'members' is not a member of 'org.eclipse.smarthome.core.items.GroupItem';

Much googling has not revealed the answer to me, I bet it’s something really simple too!

You can get a list of all the direct members of a Group using:

MyGroup.members

You can get a list of all the members of the Group including subgroups using:

MyGroup.allMembers

You can get a list of all the Groups an Item is immediately a member of using:

triggeringItem.getGroupNames

But there is no way I know of it get the list of ALL groups up hierarchy the Item may be a member of.

So assuming that triggeirngItem is directly a member of gBlueRoom

if(triggeringItem.getGroupNames.contains("gBlueRoom")) {
    // member of gBlueRoom code
}

However, if triggeringItem is NOT a direct member of gBlueRoom (i.e. it is a member of a Group under gBlueRoom) I know of no way to discover that short of:

if(gBlueRoom.allMembers.findFirst[ i | i.name == triggeringItem.name] !== null) {
    // triggeringItem is a member of gBlueRoom or a subgroup of gBlueRoom
}

The problem is filter returns a List even if there is only one match. There is no members method on a List. You would need to do something like:

gRoom.members.filter[i| i instanceof GroupItem]forEach[g as GroupItem | g.members.filter[device | device.name == triggeringItem.name].forEach[device | sb.append(", " + device.name) ]]

The last piece of code you posted is just what I’m trying to do but it gives a syntax error:

Configuration model 'lighting.rules' has errors, therefore ignoring it: [18,73]: no
viable alternative at input '|'

I was thinking that it would help if I could cast the results of the members list to an object of type GroupItem and then continue filtering. Character 73 is the character after the second pipe in the line:

gRoom.members.filter[i| i instanceof GroupItem].forEach[g as GroupItem | g.members.filter[device | device.name == triggeringItem.name].forEach[device | sb.append(", " + device.name) ]]

I.e., between GroupItem and g.members.filter. I shall continue investigating…

Reducing the code further to:

gRoom.members.filter[i| i instanceof GroupItem].forEach[g as GroupItem | sb.append(", " + g.name) ]

Results in the same error:

Configuration model 'lighting.rules' has errors, therefore ignoring it: [19,73]: no viable alternative at input '|'

The following code seems to work:

	logInfo("Rules", "NG: Motion detector triggeringItem = (" + triggeringItem.name + ")."  )
	gRoom.members.forEach[roomGroup|
	 	if (roomGroup instanceof GroupItem)
	 	{
	 		roomGroup.members.forEach[roomMember|
	 			if (roomMember.name == triggeringItem.name)
	 			{
	 				logInfo("Rules", "NG: Triggering item was (" + triggeringItem.name + "), in room group (" + roomGroup.name + ")")
	 			}
			]
	 	}]

Giving the following log entries:

NG: Motion detector triggeringItem = (PIRMotionSensor3_BinarySensor).
NG: Triggering item was (PIRMotionSensor3_BinarySensor), in room group (gBlueRoom)