Using groups in rules

Hello,
This is my second attempt at making some rules for openhab.
I first did this rule using the item names. But of course you quickly consider the hassle that doing it that way would cause. So I looked into how to better manage items that need the same treatment. and of course I came across Groups.

Now to keep this simple and practical I started with a rule for a doorbell. I have some IOboards that a bound thru SNMP. I grouped some of those items as door-buttons and some as ringers. Of course there a more fancy ways to get notified about some one at your door but this is a start.

Unfortunately my rule does not appear to be executed as there is nothing apearing in the logs when the button is pushed. Though I can see that openhab registers the pressing of the button in the sitemap.

I get the following error in the openhab.log

2018-04-17 22:33:08.265 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'test.rules' has errors, therefore ignoring it: [9,5]: no viable alternative at input 'DoorButtons'

I figure I have a mistake in the syntax but i have thus-far not found an example of triggering on a group.
What I did manage to find was a comment that “since a group is an item it can trigger a rule the same way an item does”.
But when I did “Item DoorButtons changed” it pruduces the following error.

2018-04-17 22:56:40.468 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'test.rules', using it anyway:
There is no context to infer the closure's argument types from. Consider typing the arguments or put the closures into a typed context.

What am I missing in my rule?

items

// Functional perspective
Group:Number Sensors
Group:Switch Actuators
Group:Switch Ringers (actuators)
Group:Number Temperatures (Sensors)
Group:Number DoorButtons (Sensors)

Switch DO1 "DO1" (Ringers) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:0]"}
Switch DO2 "DO2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:0]"}
Switch DO3 "DO3" (Ringers) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:0]"}
Switch DO4 "DO4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:0]"}
Switch DO5 "DO5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:0]"}
Switch DO6 "DO6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:0]"}
Switch DO7 "DO7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:0]"}
Switch DO8 "DO8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:0]"}
Switch DO9 "DO9" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:0]"}
Switch DO10 "DO10" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:0]"}
Switch DO11 "DO11" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:0]"}
Switch DO12 "DO12" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:0]"}
Number AI1 "AI1" (DoorButtons) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.1:1000]"}
Number AI2 "AI2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.2:1000]"}
Number AI3 "AI3" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.3:1000]"}
Number AI4 "AI4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.4:1000]"}
Number AI5 "AI5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.5:1000]"}
Number AI6 "AI6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.6:1000]"}
Number AI7 "AI7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.7:1000]"}
Number AI8 "AI8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.8:1000]"}

//snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.1]"

Number DI1 "DI1" (DoorButtons) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.1:1000]"}
Number DI2 "DI2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.2:1000]"}
Number DI3 "DI3" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.3:1000]"}
Number DI4 "DI4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.4:1000]"}
Number DI5 "DI5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.5:1000]"}
Number DI6 "DI6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.6:1000]"}
Number DI7 "DI7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.7:1000]"}
Number DI8 "DI8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.8:1000]"}

Number BOARDTEMPPERATURE "SystemTemp" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.3.4.10:1000]"}

rules

// Imports

// Global Variables
var Boolean ringersactive = false
var Boolean commandsent = false

rule "test doorbel"
when
    Item group DoorButtons received update
then
  commandsent = false
  logInfo("a door button changed", "a door button changed")
  DoorButtons.forEach[ doorbutton |
  logInfo(doorbutton.name, doorbutton.name)
  if ((!ringersactive && doorbutton.state > 10)) {
    logInfo(doorbutton.name + " received update" + doorbutton.state, doorbutton.name + " received update " + doorbutton.state)
    logInfo("turning on Ringers", "turning on Ringers")
    gRingers.members.sendCommand("ON")
	ringersactive = true
	commandsent = true
  }
  else if((ringersactive && doorbutton.state < 10)) {
    logInfo(doorbutton.name + " received update" + doorbutton.state, doorbutton.name + " received update " + doorbutton.state)
    logInfo("turning off Ringers", "turning off Ringers")
    gRingers.members.sendCommand("OFF")
	ringersactive = false
	commandsent = true
  }
  ]
end
// Imports
rule "DIState"
when
	Item DI received update
then		
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x01)==0x01) DI1.postUpdate(ON) else DI1.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x02)==0x02) DI2.postUpdate(ON) else DI2.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x04)==0x04) DI3.postUpdate(ON) else DI3.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x08)==0x08) DI4.postUpdate(ON) else DI4.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x10)==0x10) DI5.postUpdate(ON) else DI5.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x20)==0x20) DI6.postUpdate(ON) else DI6.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x40)==0x40) DI7.postUpdate(ON) else DI7.postUpdate(OFF)
	if((DI.state as DecimalType).intValue.bitwiseAnd(0x80)==0x80) DI8.postUpdate(ON) else DI8.postUpdate(OFF)
end
rule "AIState"
when
	Item AI received update
then		
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x01)==0x01) AI1.postUpdate("ON") else AI1.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x02)==0x02) AI2.postUpdate("ON") else AI2.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x04)==0x04) AI3.postUpdate("ON") else AI3.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x08)==0x08) AI4.postUpdate("ON") else AI4.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x10)==0x10) AI5.postUpdate("ON") else AI5.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x20)==0x20) AI6.postUpdate("ON") else AI6.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x40)==0x40) AI7.postUpdate("ON") else AI7.postUpdate("OFF")
	if((AI.state as DecimalType).intValue.bitwiseAnd(0x80)==0x80) AI8.postUpdate("ON") else AI8.postUpdate("OFF")
end
rule "DOState"
when
	Item DO received update
then		
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0001)==0x0001) DO1.postUpdate(ON) else DO1.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0002)==0x0002) DO2.postUpdate(ON) else DO2.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0004)==0x0004) DO3.postUpdate(ON) else DO3.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0008)==0x0008) DO4.postUpdate(ON) else DO4.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0010)==0x0010) DO5.postUpdate(ON) else DO5.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0020)==0x0020) DO6.postUpdate(ON) else DO6.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0040)==0x0040) DO7.postUpdate(ON) else DO7.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0080)==0x0080) DO8.postUpdate(ON) else DO8.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0100)==0x0100) DO9.postUpdate(ON) else DO9.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0200)==0x0200) DO10.postUpdate(ON) else DO10.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0200)==0x0400) DO11.postUpdate(ON) else DO11.postUpdate(OFF)
	if((DO.state as DecimalType).intValue.bitwiseAnd(0x0200)==0x0800) DO12.postUpdate(ON) else DO12.postUpdate(OFF)
end

rule "TempState"
when
	Item BOARDTEMPPERATURE received update
then		
	BOARDTEMPPERATURE.postUpdate(ON)
end

That’s an odd use of SNMP but if it works I bet a full writeup with the hardware and software config would be appreciated by the community. Most people use MQTT to publish or use REST API calls or the like. If the board supports SNMP it probably supports something like this.

OK, so that error means what it says. You have a syntax error on line 9 column 5 of test.rules so OH is rejecting the whole file. The error goes on to say that “DoorButtons” is not a valid symbol to exist at that position on that line of code.

Your best bet to find and solve syntax errors that are not immediately apparent is to use VSCode with the openHAB extension. https://docs.openhab.org/configuration/editors.html#openhab-vscode

You didn’t give the Group an aggregation function. What is it supposed to do with all of the members to generate the Number that DoorButtons gets set to? https://docs.openhab.org/configuration/items.html#group-type

Where did you get this syntax? https://docs.openhab.org/configuration/rules-dsl.html#event-based-triggers

There is no such thing as an Item group trigger. It is just:

Item DoorButtons received update

This is the root source of your syntax error. But the lack of an aggregation function will be a problem as well. By default OH will use EQUAL as the aggregation function which I’m pretty sure will result in an error because true/false are not valid states for a Number Item.

That is because a Group is an Item. There is nothing special you need to do to use it as an Item. It just has some extra stuff it can do.

Top class reaponse.

I have another thread where the configuration is mentioned in more detail. I indeed intent to write up a proper post dealing with the subject. daenetip1-4-snmp-binding-help-i-am-missing-something
It is a curious use of SNMP but that is what this somewhat older model board supports (well it does HTTP GET requests as well but I noticed the boards thend to crash if you fire a lot of GETs at them)

This is where I get in a bit of a bind. I am not actually aggregating anything just merely using a group as a convenient way to bundle items that need to trigger the same rule and for the rule to neatly command multiple items. The numbers can be in the range of 0 to 1023 (in the case of an analoge input)
I guess i could just sum them all but that seems more like a hack since I don’t need the sum.

I am still trying to get my head around the rest of your comments, OH is new to me and I am having some difficulty with the lack of a debugging option to just poke around at run-time.

Thx This porved really useful.
it is more or less working now. the rule is rough and I have a hunch it is being run more then what is needed so the behavior is somewhat random at the moment.

.items

// Functional perspective
Group:Number Sensors
Group:Switch Actuators
Group:Switch:OR(ON, OFF) Ringers (actuators)
Group:Number:MAX Temperatures (Sensors)
Group:Number:MAX DoorButtons (Sensors)

Switch DO1 "DO1" (Ringers) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.1:0]"}
Switch DO2 "DO2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.2:0]"}
Switch DO3 "DO3" (Ringers) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.3:0]"}
Switch DO4 "DO4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.4:0]"}
Switch DO5 "DO5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.5:0]"}
Switch DO6 "DO6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.6:0]"}
Switch DO7 "DO7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.7:0]"}
Switch DO8 "DO8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.1.2.8:0]"}
Switch DO9 "DO9" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.1:0]"}
Switch DO10 "DO10" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.2:0]"}
Switch DO11 "DO11" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.3:0]"}
Switch DO12 "DO12" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:1000:MAP(daenetip4.map)] >[OFF:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:1] >[ON:172.16.0.122:glados:.1.3.6.1.4.1.32111.1.4.2.4:0]"}
Number AI1 "AI1" (DoorButtons) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.1:1000]"}
Number AI2 "AI2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.2:1000]"}
Number AI3 "AI3" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.3:1000]"}
Number AI4 "AI4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.4:1000]"}
Number AI5 "AI5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.5:1000]"}
Number AI6 "AI6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.6:1000]"}
Number AI7 "AI7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.7:1000]"}
Number AI8 "AI8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.8:1000]"}

//snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.5.1.1]"

Number DI1 "DI1" (DoorButtons) {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.1:1000]"}
Number DI2 "DI2" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.2:1000]"}
Number DI3 "DI3" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.3:1000]"}
Number DI4 "DI4" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.4:1000]"}
Number DI5 "DI5" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.5:1000]"}
Number DI6 "DI6" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.6:1000]"}
Number DI7 "DI7" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.7:1000]"}
Number DI8 "DI8" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.6.5.8:1000]"}

Number BOARDTEMPPERATURE "SystemTemp" {snmp="<[172.16.0.122:glados:.1.3.6.1.4.1.32111.1.3.4.10:1000]"}

.rules

// Imports

// Global Variables
var Boolean ringersactive = false
var Boolean commandsent = false

rule "test doorbel"
when
    Item DoorButtons changed
then
  commandsent = false
  logInfo("a door button changed", "a door button changed")
  DoorButtons.getAllMembers.forEach[ doorbutton |
  logInfo(doorbutton.name, doorbutton.name)
  if ((!ringersactive && doorbutton.state > 10)) {
    logInfo(doorbutton.name + " received update" + doorbutton.state, doorbutton.name + " received update " + doorbutton.state)
    logInfo("turning on Ringers", "turning on Ringers")
    Ringers.members.forEach[r| r.sendCommand("ON")] 
	ringersactive = true
	commandsent = true
  }
  else if((ringersactive && doorbutton.state < 10)) {
    logInfo(doorbutton.name + " received update" + doorbutton.state, doorbutton.name + " received update " + doorbutton.state)
    logInfo("turning off Ringers", "turning off Ringers")
    Ringers.members.forEach[r| r.sendCommand("OFF")] 
	ringersactive = false
	commandsent = true
  }
  ]
end

But without a valid aggregation function the Group’s state will never change. That aggregation function is what calculates the state of the Group based on the states of its members. Without out it or with an invalid aggregation function then your Rule will never trigger.

Whether the value of the Group is meaningful is besides the point. You need it to change though which requires a valid aggregation function.

But you do need the change in the sum to create the update events that trigger your rule. Or you can use MAX, or MIN or just about anything else that makes sense for Number Items. But you need something to create the updates on the Group’s state.

It’s not a hack if that is the designed way that Groups work.

However, there is a better way short term and an even better way long term.

Short term just list each door button (you only have two after all) as a trigger to the Rule. You can then rewrite your rule a bit because you will know which door button was pressed because that Item will be set to triggeringItem. Then you don’t have to loop through all your buttons and can just deal with the ringers for that specific button.

One 2.3 is released you can use the new Member of Rule trigger that will populate triggeringItem with the member of the Group that caused the Rule to trigger.

So your Rule can become:

Group:Number:MAX        DoorButtons
Group:Switch:OR(ON,OFF) Ringers
rule "test doorbell"
when
    Member of DoorButtons changed
then
    logInfo("doorbell", "a door button changed: " + triggeringItem.name) // the first argument is the logger name, it should be the same for all logs in a given rule, if not the same for all logs in a given file
    
    var command = if(triggeringItem.state > 10) ON else OFF

    if(Ringers.state != command) Ringers.sendCommand(command) // Note the Group name is Ringers, not gRingers
end

Theory of operation: DoorButtons changes whenever any of its members change state. Ringers will be ON if one or more of its members are ON. The Rule triggers whenever any member of DoorButtons changes its state. We log the name of the button for tracking purposes. Then we determine whether we need to change the Ringers to ON or OFF. Finally we send the new state to Ringers but only if the new state is different from the current state.

Three lines of code is all you need.

Once it works though, I’d recommend eliminating the log statement because I suspect this rule is going to be hammered with noise as the analog reading floats around quite a bit.

Edit: I just realized this won’t work as expected. Make the aggregation function for DoorButtons be MAX and change the Rule to:

    var command = if(DoorButtons.state > 10) ON else OFF

Therefore if either button is greater than 10 then the ringers will ring.

I did get the group to properly trigger.
Indeed by adding a aggregate function and solving another number of issues that the visual studio plugin made clear to me.

The rule still needs work still. Basically one push of a door button should ring the bell for x amount of time regardless of how long you pressed it.
sure for just two buttons and a couple of bells this is over engineered, why even use openhab if it’s that simple right. It is of course a bit of training to get into how openhab works.
I can imagine adding smoke detectors and sirens. Basically any scenario where you have multiple sensors which need to trigger multiple of another and you don’t really want to add explicit code for ever single one.

That 2.3 feature triggeringItem is indeed really useful. Going back to the smoke detector scenario, you would really want to know which one triggered.

That is a valid concern.
The logging is mostly there for debugging, Just one message of a person at the door is sufficient logging.
The main reason for using analogue instead of digital inputs is due to the board not supporting SNMP traps on digital inputs. The jitter is minimal since it is just being switched. (For my temperature readings that is a challenge indeed.) Using the digital inputs you need to hold the button for about a second before OH has polled the new value. So if you just quickly pres the button it often doesn’t register. With the SNMP trap the IOboard registers the change as it can poll once per millisecond and sends a trap which OH will then act upon.