Design Pattern: Working with Groups in Rules

That gives you a list. A list is neither true nor false, so far as an if() is concerned.

Maybe you could see if the list length > 0

It works
Thanks
Lorenzo

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

???

Thanks in advance.

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?

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!