Loop through all members in a group with OH3

In OH2 I used to have a rule looping through all members of a group. In an .items file I had the group and the respective items defined like:

Group:Switch:OR(ON, OFF) 	galive	

Switch device1 (galive) {channel="network:pingdevice:af15f8e1:online"}		
Switch device2 (galive) {channel="network:pingdevice:af15f8e2:online"}
...

Then I used a rule to loop through all items in the group like

when	
	Item galive changed to OFF
then
		galive.members.forEach[ item | 	
			if (item.name.state == "OFF") {
				do something with item.name
			}
		]

In OH3 this does not work any more. I have the group defined having the correct group members (items), but an error occurs “[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID ‘notifications-10’ failed: ‘state’ is not a member of ‘java.lang.String’;”

How can I loop through the group members in OH3?

Thank you!

Your ‘when’ clause is wrong. See this

Why? I have defined galive as item and it is shown as an item in the list of items in the OH3 item interface. The error points me to the if-condition, I suppose item.name.state is wrong somehow. When I comment out everything relating to the if-condition, no error occurs.

In the help file, please scroll down to the chapter named ‘Member of Triggers’. In short the “when” should be “Member of changed”…

And the syntax of the “if” condition should be as follows…

if (item.state == OFF)
or
if (item.state.toString() == “OFF”)

I guarantee this never worked in any version of OH. item.name.state is essentially nonsense. item is an Item. An Item has a name which is a String that is the Item’s name (e.g. “device1”). An Item also has a state. The Item’s name doesn’t have a state. You want to use item.state.

That will address the error in the log. But you probably have a second error here. A Switch Item’s state is ON or OFF, not the strings "ON" and "OFF". So the comparison would likely always return false and do something will never run.

And of course “do something with item.name” is kind of nonsense code too. Please don’t truncate, edit, or otherwise try to make our lives easier by posting anything except the actual code, character by character, that is producing the error. If you understood the problem you wouldn’t have posted here in the first place. And by not posting the actual code that generates the error you prevent us from understanding the code that is producing the error and it ends up wasting everyone’s time.

Finally, not an error but a better way to write this would be:

    galive.members.filter[ i | i.state == OFF ].forEach[ i |
        // do something with each Item that is OFF
    ]

That didn’t work in OH2 either.
item.name is a string
item.name.state does not exist and throws the error.

Hmmm, interesting. Thanks to the feedback from all of you it appears to work now:

rule "alive test"
when	
	Member of galive changed from ON to OFF
then
	Thread::sleep(2000)
	galive.members.filter[ i | i.state == OFF ].forEach[ i |
        //do something
	]
end 

Thank you! :grinning:

Why sleep for two seconds? In OH 2.5 that was a really bad idea. In OH 3 it’s a less bad idea (at least for now unless the rule thread pool is reintroduced) but still not a great practice. What problem are you hoping to solve with this sleep?

As written the following will happen in OH 3:

  • a member of galive changes from ON to OFF
  • the rule is triggered
  • the rule sleeps for two seconds
  • another member of galive changes from ON to OFF
  • the rule is locked, only one instance of it at a time can run in OH 3, so this second event waits for the rule to exit before triggering again
  • the two seconds are done, loop through the group and “do something”
  • the rule exits and then immediately triggers again, repeat the steps above.

Ok, makes sense in this case. But I don’t understand the explanation fully. There may be rules where I want to avoid that the rule is triggered twice during a certain time span. I have for example a motion sensor which provides me “1” when motion is detected. Upon motion detection I turn the light on for 5 minutes. Any additional motion should either be ignored during that time or (even better) reset the timer. If I understood you correctly, just having a Tread-Sleep would pause the rule but all the other events “1 … 1 … 1 …” will accumulate in the meantime? In other words, the rule would be resumed for the other triggers as soon as the thread wakes up again?

I somehow circumvented that issue using a creatTimer which is reset with each motion

if motion detected ...  {
			sendCommand(lightbulb, ON)
			if(timer!==null) {
        		   timer.cancel			
			   timer = null
						}
				timer = createTimer(now.plusMinutes(5), [ |
				sendCommand(lightbulb, OFF)
				])

Just walking by the sensor will create a lot lot lot of "1 1 1 1 1 1 " in a short time frame which will restart the rule every time and there will be sent hundreds of sendCommand(lightbulb, ON) in case you are dancing in front of the sensor for a few minutes. :smirk:

Correct.

Not resumed, run again.

That’s the better way to accomplish this. Check for the existence of the timer and if it exists exit the rule without doing anything.

And how can I limit the number of commands sent per minute? Think about a mechanical switch that is triggered every second, it won’t survive to long…

Again, with a timer. Create a timer on the first event. As long as the timer exists, ignore subsequent events.

A fairly obvious way is with if() … if it’s already state ON, don’t command it ON again. Or to put it another way,if it’s not-ON then do the ON command. That assumes you have accurate feedback from the target device.

:+1: Thanks!!