Design Pattern: Working with Groups in Rules

Hi all,

I have a group of lights that is controlled by a number of different options, OH switch, Alexa, physical wall switch.

I’m writing a few rules to work out if any of the lights in the group are on to then determine if the rule should run or not, they are Phillips Hue spots so I can’t search by ON / OFF, I have to do it by Brightness > 0.

I’ve got different options on how I can code the rule, my question to help determine the best way is…is there a way to determine if:

all lights in the group are brightness = 0
or
any lights in the group are brightness > 0
or
what lights in the group are brighness of either = or > 0

???

You can make a Group just for this purpose, a Switch type with aggregate function OR(ON,OFF) should do.

1 Like

To add on: here’s an example that I use everyday - scroll down to zigbee.items to see the setup, and then just below the screenshot of my sitemap.

To do your first two queries you would just compare against the dimmer group. The last one will have to be done by individually checking each light.

1 Like

Of course, I’m always trying to make things too complicated. Good old KISS theory, keep it simple stupid as my old maths teacher used to say…

EDIT:

Right, I’ve just found out that after some scratching of head and some very random results in using a switch, it would appear the Hue spots that I have don’t accept on / off commands in the way you’d hope, they turn the lights on but only to a brightness of 1…they need to be brightness 100…so the group switch is out, but for anyone else reading this, the concept of the group switch certainly works.

Hey guys,

Thanks to this topic, I managed to have the items of a group named and sent to me via telegram that have a certain status.

``````rule "Abfrage"
when
Item Abfrage received command ON
then
sendTelegram("bot1", gWindow.members.filter[i | i.state==OPEN].map[ label ].reduce[ result, label | result = result+", " + label ] + " ist offen" )
end
``````

Unfortunately, I get a “null” message when none of the items in the group have the requested status.

Does anyone know how I can prevent this?

Greetings

Expand your rule. Get your list of Items into a variable first. Then test to see if it has length zero before doing anything with it.

Hi I created a short test to understand the behaviour of Group but I receive always a “null” message

``````> Items
``````
``````> Group PIPPO "test group"
> Switch PIPPO_1 "membro 1" (PIPPO)
> Switch PIPPO_2 "membro 2" (PIPPO)
> Switch PIPPO_3 "membro 3" (PIPPO)
> Switch MQTT_Remote  "membro 4"  (PIPPO)
``````

Sitemap
` Switch item= MQTT_Remote`

Rule (I tried both the logInfo)

``````rule "testgroup"
when
Member of PIPPO received command
then
logInfo ("testgroup ", "MQTT_Remote")
//	logInfo ("testgroup ", MQTT_Remote)
val prova=triggeringItem
logInfo ("testgroup 1", prova)
end
``````

Where am I wrong?
Thanks

`logInfo(X,Y)` wants to be given two strings. Your `prova` is an Item object, not a string.
Examples-
`logInfo ("testgroup 1", "Testing " + prova.name)`
`logInfo ("testgroup 1", "State " + prova.state.toString)`

There is a handy way to see objects using {} in the text and an extra parameter.
`logInfo ("testgroup 1", "trigger Item was {}", prova)`

Thanks, I was so sure that the problem was the use of Group that I did not see the most evident error!

Hello all,

I have posted here before because I would like to receive a Telegram message that says what windows are open.

This had also worked wonderfully. I have now switched to OH3 and yesterday I noticed that this query no longer works. Have I built in a bug after all? Or is this no longer possible under OH3?

This is my rule:

``````rule "Abfrage Fenster"
when
Item Abfrage received command ON
then
val telegramAction1 = getActions("telegram","telegram:telegramBot:652b25b")
logInfo("Status", "Abfrage wurde gedrückt")
if (gWindow.members.filter[m|m.state==OPEN].size !==0) {
telegramAction1.sendTelegram(gWindow.members.filter[i | i.state==OPEN].map[ label ].reduce[ result, label | result = result+", " + label ] + " ist offen" )
}
end
``````

And this is the Error-Log:

``````[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'telegram-8' failed: Couldn't invoke 'assignValueTo' for feature param result in telegram
``````

I hope for your help!

Lights in my home automation setup may be controlled multiple ways: by a physical switch hardwired to the light, by a button on a UI screen, or by a rule in response to some other events.
To keep it simple, I combine the design patterns for Proxy Items, for Groups and for Associated Items. I define rules for the desired behavior at the level of a group, and then assign the lights to that group.

With this setup, the proxy item will always correctly reflect the status of the light, independent of what caused that status (command from a rule, gesture on a physical control, gesture on a UI element).

A more detailed description can be found here.

Hi Guys,
I used Groups a lot in OH3 (DSL rules), but I am currently migrating to MainUI rules including Blockly.
For making this work, I am missing triggerinItem:
If a rule is triggered by a member of a group (e.g. one of 4 smartphones) and I would like to know which one triggered it.

Any suggestion how to solve this in blockly would be greatly appreciated.

Hi all,

I’m trying to work with a group rule and be able to create a timer. The issue I have is that I cannot figure out how to create the timer’s name dynamically based on the triggering items name.

Example: I have 6 items in this group. They are zwave sensors, which I grab their zwave_lastwake time form the API using the HTTP binding. If that doesn’t change in 3 hours I send alerts that the sensors battery is likely dead (because they always report 100% battery).

So In my normal (not group) rule I successfully do this like this for 1 item:

``````var Timer frontdoorSensor_Recent_Timer = null
rule "Front Door Sensor Last Wake check"
when
Item frontdoorSensor_LastUpdate changed							//triggered when a new ZWAVE_LAST_WAKEUP is detected
then
logInfo("Front Door Sensor","!!!!!!!!!!!!!!!!!!! New Wakeup Time Received")
postUpdate(frontdoorSensor_Lowbat,"OK")							//set lowbat to indicate battery is OK
if (frontdoorSensor_Recent_Timer !== null) {					//create timer to trigger battery low if not reset before it runs out.
frontdoorSensor_Recent_Timer.reschedule(now.plusMinutes(180))
logInfo("frontdoorSensor_Recent_Timer","!!!!!!!!!!!!!!!!!!! Reset 3 hour battery timer for front door sensor")
} else {
frontdoorSensor_Recent_Timer = createTimer(now.plusMinutes(180)) [|
if (doorsensor_Lowbat_Notify.state == ON) {
pushoverActions.sendMessage("The Front Door Sensor appears to be dead, please check the battery", "openHAB")
ODTNotification.sendCommand("{\"title\": \"openHAB\", \"text\": \"The Front Door Sensor appears to be dead, please check the battery\", \"level\": \"critical\"}")	//Send to Desktop Computer (ODT)
}
logInfo("Front Door Sensor","!!!!!!!!!!!!!!!!!!! Wakeup Check timed out")
postUpdate(frontdoorSensor_Lowbat,"Check")
]
logInfo("frontdoorSensor_Recent_Timer","!!!!!!!!!!!!!!!!!!! Created 3 hour battery timer for front door sensor")
}
end
``````

This works great, however right now I repeat this same rule 6x for each of my 6 sensors. I am trying to move this to a group rule. My issue is that I cannot seem to figure out how to create a timer with a dynamic name.

``````import org.openhab.core.model.script.ScriptServiceUtil
var Timer frontdoorSensor_Recent_Timer = null
var Timer sidedoorSensor_Recent_Timer = null
var Timer backdoorSensor_Recent_Timer = null
var Timer upporchdoorSensor_Recent_Timer = null
var Timer sidedoorDoorbell_Recent_Timer = null
var Timer frontdoorDoorbell_Recent_Timer = null

rule "ZWave Sensor Last Wake check"
when
Member of gzwave_lastwake changed						//triggered when a new ZWAVE_LAST_WAKEUP is detected
then
val Itemname = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name.toString.replace("_LastUpdate",""))
val timerItem = ScriptServiceUtil.getItemRegistry.getItem(Itemname.name.toString + "_Recent_Timer")
val lowbatItem = ScriptServiceUtil.getItemRegistry.getItem(Itemname.name.toString + "_Lowbat")

logInfo("Zwave Sensor Check","!!!!!!!!!!!!!!!!!!! New Wakeup Time Received for " + triggeringItem.name.toString)
postUpdate(lowbatItem,"OK")								//set lowbat to indicate battery is OK
if (timerItem !== null) {								//create timer to trigger battery low if not reset before it runs out.
timerItem.reschedule(now.plusMinutes(180))
logInfo("Zwave Sensor Check","!!!!!!!!!!!!!!!!!!! Reset 3 hour battery timer for " + triggeringItem.name.toString)
} else {
timerItem = createTimer(now.plusMinutes(180)) [|
if (doorsensor_Lowbat_Notify.state == ON) {
pushoverActions.sendMessage("The Sensor \'" + Itemname + "\' appears to be dead, please check the battery", "openHAB")
ODTNotification.sendCommand("{\"title\": \"openHAB\", \"text\": \"The sensor \'" + Itemname + "\' appears to be dead, please check the battery\", \"level\": \"critical\"}")	//Send to Desktop Computer (ODT)
}
logInfo("Zwave Sensor Check","!!!!!!!!!!!!!!!!!!! Wakeup Check timed out for " + triggeringItem.name.toString)
postUpdate(lowbatItem,"Check")
]
logInfo("Zwave Sensor Check","!!!!!!!!!!!!!!!!!!! Created 3 hour battery timer for " + triggeringItem.name.toString)
}
end
``````

When this rule gets triggered I get the following error:

``````2022-01-24 10:58:16.160 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'battdet-1' failed: Item 'backdoorSensor_Recent_Timer' could not be found in the item registry in battdet

``````

I (now) know its because my code is trying to find the Timer item in the registry but its not there since it isn’t defined in a .items file (and cannot be).

So, Is there a way to do what I am trying to do here and create a timer with different names based on the triggering item of a group rule?

1 Like

Example above

The trick is to store many Timer handles in a Map “array” indexed by any-string-you-like, often conveniently an associated Item name.

1 Like

Thanks @rossko57! I was searching through this thread but since the syntax is so different than I had thought I missed it. Appreciate the help.

1 Like

@JJ_Reynolds

I’m a little late to the party, but what you posted here a little over 2 years ago is exactly what I’m looking to do. One question for you on this:

Typically in my rules dealing with timers, I check to see if the timer is NULL, if it is then I create a timer. If it is not NULL then I reschedule the timer. like this:

``````
rule "Front Door Sensor Last Wake check"
when
Item frontdoorSensor_LastUpdate changed						//triggered when a new ZWAVE_LAST_WAKEUP is detected
then
logInfo("Front Door Sensor","!!!!!!!!!!!!!!!!!!! New Wakeup Time Received")
postUpdate(frontdoorSensor_Lowbat,"OK")						//set lowbat to indicate battery is OK
if (frontdoorSensor_Recent_Timer !== null) {					//create timer to trigger battery low if not reset before it runs out.
frontdoorSensor_Recent_Timer.reschedule(now.plusMinutes(180))
logInfo("frontdoorSensor_Recent_Timer","!!!!!!!!!!!!!!!!!!! Reset 3 hour battery timer for front door sensor")
} else {
frontdoorSensor_Recent_Timer = createTimer(now.plusMinutes(180)) [|
if (doorsensor_Lowbat_Notify.state == ON) {
pushoverActions.sendMessage("The Front Door Sensor appears to be dead, please check the battery", "openHAB")
ODTNotification.sendCommand("{\"title\": \"openHAB\", \"text\": \"The Front Door Sensor appears to be dead, please check the battery\", \"level\": \"critical\"}")	//Send to Desktop Computer (ODT)
}
logInfo("Front Door Sensor","!!!!!!!!!!!!!!!!!!! Wakeup Check timed out")
postUpdate(frontdoorSensor_Lowbat,"Check")
]
logInfo("frontdoorSensor_Recent_Timer","!!!!!!!!!!!!!!!!!!! Created 3 hour battery timer for front door sensor")
}
end
``````

Am I reading your rule correctly in that you basically cancel the timer on entry to the rule then schedule the timer you want?

``````				todTimers.get(triggeringItem.name)?.cancel;
``````

``````				todTimers.put(triggeringItem.name, createTimer(now.plusMinutes(30)) [|
sendCommand(triggeringItem, OFF)
todTimers.remove(triggeringItem.name)
] )
``````

Appreciate the help. I’m not familiar with these hash maps and the syntax they use.

That’s correct. Please be aware of the ?.

``````todTimers.get(triggeringItem.name)?.cancel;
``````

is: get from hash map todTimers the object with name triggeringItem.name. If the object is not null (that’s the meaning of ?) call method .cancel.
So, any existing timer for the triggering Item will be cancelled. Then it’s safe to create a new timer and to put the object, referring to it to the hash map.

1 Like

Thank you @Udo_Hartmann - I very much appreciate the explanation!

I recently upgraded to OH3 and found my rules no longer worked – here is an updated version that works, and may be a bit easier to read.

``````import java.time.ZonedDateTime
import java.util.Map

val Map<String, Timer> todTimers = newHashMap

rule "TOD Expire rule"
when
Member of grp_TODExpire changed
then
logInfo("RULE","Item "+triggeringItem.name+" turned "+triggeringItem.state+".  Current Hour="+ZonedDateTime.now().getHour()+".");
todTimers.get(triggeringItem.name)?.cancel;
var Number minutes;

if (triggeringItem.state==ON || triggeringItem.state>0) {
if (ZonedDateTime.now().getHour() >=23 || ZonedDateTime.now().getHour() <=6 ) minutes=30 else minutes=60;

logInfo("RULE","Item "+triggeringItem.name+" turned on.  Will turn it off in "+minutes+" minutes");
todTimers.put(triggeringItem.name, createTimer(now.plusMinutes(minutes)) [|
logInfo("RULE","Item "+triggeringItem.name+" turned has been on "+minutes+" minutes; turning it off now.");
sendCommand(triggeringItem, OFF)
todTimers.remove(triggeringItem.name)
] )
}
end

``````

Hello,
i am trying to get all group members (the group is Rollershutter type and the group’s name is “gPanjurlar”) and send commands to all group members over the gatekeeper.
I am working with VS Code.
But I am getting the below error related to this code

``````import org.openhab.core.model.script.ScriptServiceUtil
var Number Log_Flag = 1
var Timer RSTimer = null
var Long duration = 100000000

rule "Panjurlar Up"
when
Item gPanjurlar received command
then
// get info for the current RS

logInfo("currRollershutter =" + currRollershutter.name)
var pID = 1
// querying to Item name and item type; the item names can be I_x_x_S or I_x_x_L
/***16***/     val curr_RS = gPanjurlar.members.filter[ var query_RS_temp = (ScriptServiceUtil.getItemRegistry.getItem ("I_"+ 1 + "_" + pID + "_" +  "S" )) | query_RS_temp.getType == "Rollershutter" ]
logInfo("currRollershutter =" + curr_RS.name)
if (curr_RS == (null || 'UNDEF')){
logInfo("currRollershutter  NULL or UNDEF" )
pID = pID + 1
/***21***/         val curr_RS = gPanjurlar.members.filter[ var query_RS_temp = (ScriptServiceUtil.getItemRegistry.getItem ("I_"+ 1 + "_" + pID + "_" + "S" )) | query_RS_temp.getType == "Rollershutter" ]
}
logInfo("currRollershutter second try =" + curr_RS.name)

end

``````
``````2023-08-13 16:42:19.361 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'GK.rules' has errors, therefore ignoring it: [16,136]: no viable alternative at input '|'
[21,133]: no viable alternative at input '|'
``````

I couldn’t understand what my mistake or problem was…
thank you for your time and advice/help.

maybe those can be useful,

additional information for my construction is below;

group item detail

group item members

items

Rollershutter item detail