Design Pattern: Working with Groups in Rules

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

group.members.forEach[ i |
    if (i.getGroupNames.contains("sceneGroup") {
    else {

So after several testing now, here is the conclusion:

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

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

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

should be

 if (i.getGroupNames.contains( {

@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? equals “sceneGroup”.

Not quite: 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 = [ for i in ir.getItem('gTemperatures').members]
livingroom_temperatures = [i for i in ir.getItem('gLivingroom').members if 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]


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?

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.

I have copy-pasted the items from the top

Group:Contact:OR(OPEN, CLOSED) gDoorSensors "The doors are [%s]"
Group:DateTime:MAX gDoorsLast "The last door event was [%1$tm/%1$td %1$tH:%1$tM]" 
Group:Switch:OR(ON, OFF) gDoorsTimers

Contact vGarageOpener1 "Garage Door Opener 1 is [%s]" (gDoorsSensors)
DateTime vGarageOpener1_LastUpdate "Garage Door Opener 1 [%1$tm/%1$td %1$tH:%1$tM]" (gDoorsLast)
Switch vGarageOpener1_Timer { expire="1h,command=OFF" } (gDoorsSensors)

Contact vGarageOpener2 "Garage Door Opener 2 is [%s]" (gDoorsSensors)
DateTime vGarageOpener2_LastUpdate "Garage Door Opener 2 [%1$tm/%1$td %1$tH:%1$tM]" (gDoorsLast)
Switch vGarageOpener2_Timer { expire="1h,command=OFF" } (gDoorsTimers)

Contact vFrontDoor "Front Door is [%s]" (gDoorsSensors)
DateTime vFrontDoor_LastUpdate "Front Door [%1$tm/%1$td %1$tH:%1$tM]" (gDoorsLast)
Switch vFrontDoor_Timer { expire="1h,command=OFF" } (gDoorsTimers)

Contact vBackDoor "Back Door is [%s]" (gDoorsSensors)
DateTime vBackDoor_LastUpdate "Back Door [%1$tm/%1$td %1$tH:%1$tM]" (gDoorsLast)
Switch vBackDoor_Timer { expire="1h,command=OFF" } (gDoorsTimers)

Contact vGarageDoor "Garage Door is [%s]" (gDoorsSensors)
DateTime vGarageDoor_LastUpdate "Garage Door [%1$tm/%1$td %1$tH:%1$tM]" (gDoorsLast)
Switch vGarageDoor_Timer { expire="1h,command=OFF" } (gDoorsTimers) 

they looks good

It seems to be a wrong Syntax in item definition (at least in 2.5 M5). Should be chaned in the top of the post!


Switch vGarageOpener1_Timer { expire="1h,command=OFF" } (gDoorsSensors)


Switch vGarageOpener1_Timer (gDoorsSensors) { expire="1h,command=OFF" }

Well, it’s only an example, you’ll probably need to work your own up. But -

This wrong format. The (groups) need to be placed before the {bindings}.
It’s always been that way, but I expect the Items file parser has incorporated more checks since 2017

Switch vGarageOpener1_Timer (gDoorsSensors) { expire="1h,command=OFF" }

EDIT - okay you found it. @rlkoshak for info

1 Like

One more small mistake with whitch iэму spent half an hour (((
Better to correct, so other can use the pattern.

And in Rules also

This construction

val lastChange=gDoorsSensors.members.findFirst[ t | == "DoorEntranceLastChange" ] as DateTimeItem
logInfo("Door", "val opened at:  " + lastChange + "check: " +  DoorEntranceLastChange.state)

seems not ot work on OH 2.5M5 or i made smth wrong?

i get

2019-12-05 18:22:16.089 [INFO ] [.eclipse.smarthome.model.script.Door] - val opened at:  nullcheck: 2019-12-05T18:22:14.957+0100

Why null, item has DateTime and not null at this time

DateTime  DoorEntranceLastChange "Door Entrance Last Change [%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS]"

Thank you for pointing out the errors. As rossko57 points out, the example never was intended to be copied from the posting and used. It shows how to use the operations discussed but you would be expected to apply those operations to your own Items and Groups.

In some cases, there are better ways to achieve the same thing, sometimes without Rules at all, e.g. there is a Profile to update the LastUpdate Items.

Please don’t double post. I’ve answered on the other thread.

I thought that other members can save time using Pattern out of box)

Of course! Sorry. Sitting with it foк 4 hours, a little bit tired(((

See Design Pattern: What is a Design Pattern and How Do I Use Them.

A DP is a general and reusable solution to a common problem. But the solution is not complete. Instead it is a template that one can apply to a lot of different situations with minor modifications and customizations.

This particular DP is also a little different and probably should never have been a DP in the first place. Frankly, what it is is some reference documentation missing from the Xtend docs for how to manipulate collections. It’s not so much a DP as it is a reference guide. As such, the example is just that, and example. It was never intended to be something someone copies and uses line for line. Some of the other DPs do in fact have examples that could be copied and used line for line though, assuming that the example is useful to you. But even then it is unlikely to be useful “out-of-the-box”. The examples are there to illustrate how to solve a common problem. Rarely will you find any example on the forum, DP or otherwise, that you can just use without modification.

The example above though is mainly for illustrative purposes, to show some of those operations in context. That’s probably why the errors you found haven’t been found by others in the three + years this DP has been posted.

I thought I’d share my code for simulating a time of day type of expire based on groups. Essentially, any member of this group will stay on for an hour if it is turned on between 6am and 11pm; they will only stay on for 30 minutes from 11pm to 6am, though (family is more likely to have fallen asleep…)
I pieced this together mostly from other blocks of code in this thread so credit goes to those guys for blazing the trail. If you have a sizable number of items this beats having lots of rules that all basically do the same thing. It would be trivial to extend this to another group that would have different times as well.

First – groups and items::

Group grp_TODExpire "Expire by Time of Day"

Dimmer Wallplug_Den_BigLamp "Den Lamp" <dimmablelight> (grp_TODExpire)  { channel="zwave..." }
Dimmer WallSwitch_Den_CeilLight "Ceiling Light" <dimmablelight> (grp_TODExpire) { channel="zwave..." }

Now the rule:

import java.util.Map

val Map<String, Timer> todTimers = newHashMap

rule "TOD Expire rule"
   Member of grp_TODExpire changed
   logInfo("RULE","Item "" turned "+triggeringItem.state+".");
   if (triggeringItem.state==ON || triggeringItem.state>0) {
      if ((new LocalTime().getLocalMillis()) >= (new LocalTime(23, 0, 0, 0).getLocalMillis()) || (new LocalTime().getLocalMillis()) <= (new LocalTime(6, 0, 0, 0).getLocalMillis())) {
          logInfo("RULE","Item "" turned on.  Will turn it off in 30 minutes");
          todTimers.put(, createTimer(now.plusMinutes(30)) [|
             sendCommand(triggeringItem, OFF)
        ] )
      } else {
          logInfo("RULE","Item "" turned on.  Will turn it off in 60 minutes");
          todTimers.put(, createTimer(now.plusMinutes(60)) [|
             sendCommand(triggeringItem, OFF)
        ] )