I have a number of light switches that I want to act as 2-way light switches. So, if one switch turns on, the other switch should turn on and vice versa. I have a rule that works perfectly for one pair of switches. I add the switches to a group and if one of the switches changes state, all the members in the group are updated.
Now, I would like to extend the same rule for multiple groups. I need to have the name of the group that triggered the rule. I hope there is something like a “TriggeringGroup” that returns the name of the group, but I cannot find it here
For example
rule "Switch 2-way"
when
Member of LightGroup1 changed or
Member of LightGroup2 changed
then
...
implicitVariableWithGroupName
...
end
So if a member of LightGroup1 changes state, is there a way to get the GroupItem of the member?
Unfortunately not. Is there any overlap between the two Groups (i.e. same Item is a member of both)? If not you can get the list of Groups the triggeringItem belongs to and figure out which Group it was from that.
If so I’m not sure how this is supposed to work.
val groupName = if(triggeringItem.groupNames.contains("LightGroup1")) "LightGroup1" else "LightGroup2"
If you plan on having more you’ll need to use a switch statement or if/else if statements.
If these are the only Groups with “LightGroup” in their names you could be a little clever:
val groupName = triggeringItem.groupNames.findFirst[ item | item.name.contains('LightGroup') ]
Or if these lights will only ever belong to one Group
A pity there is no option for the “TriggeringGroup”. Thanks for the suggestion. There is no overlap between the two groups, so it could work. I would like 5+ groups, so this adds quite some additional code. I think the following pattern is a bit cleaner. I isolate the common code in a shared function, like this:
val syncGroup= [ GroupItem group, String name, String state |
logInfo("light.rules", String.format("Item '%s' changes to '%s'", name, state))
group.members.filter[j|j.name != name].forEach[i|
if (i.state.toString() != state) {
i.sendCommand(state)
}
]
]
rule "Switch 2-way: GF Garage"
when
Member of GF_Garage_Main_Light changed
then
syncGroup.apply(GF_Garage_Main_Light, triggeringItemName, newState.toString())
end
rule "Switch 2-way: GF Bathroom"
when
Member of GF_Bathroom_Light changed
then
syncGroup.apply(GF_Bathroom_Light, triggeringItemName, newState.toString())
end
Did you read my full reply? One additional line of code is a lot?
Why loop through the Group? One of the main points of a Group is if you send it a command it forwards that command to all of it’s members. It shouldn’t matter if the Item that triggered the rule gets another ON command, assuming it’s a Switch instead of a Dimmer.
rule "Switch 2-way"
when
Member of LightGroup1 changed or
Member of LightGroup2 changed
then
val groupName = triggeringItem.groupNames.findFirst[ item | item.name.contains('LightGroup') ]
sendCommand(groupName, newState.toString())
end
Add as many Groups as triggers as you need.
Or if you really do need to loop through the members
import org.eclipse.smarthome.model.script.ScriptServiceUtil
rule "Switch 2-way"
when
Member of LightGroup1 changed or
Member of LightGroup2 changed
then
val groupName = triggeringItem.groupNames.findFirst[ item | item.name.contains('LightGroup') ]
val group = ScriptServiceUtil.getItemRegistry.getItem(groupName)
group.members.filter[ i | i.state != newState ].forEach[ i | i.sendCommand(newState) ]
end
Yes, I tried your second suggestion having a group name with an identifiable part. triggeringItem.groupNames.findFirst gave the groupName and not the group. I wasn’t aware about ScriptServiceUtil. That was the missing part, thanks! So, the code below works nicely now (note the import path has changed)
import org.openhab.core.model.script.ScriptServiceUtil
rule "Switch 2-way"
when
Member of S2W_GF_Garage_Main_Light changed or
Member of S2W_GF_Bathroom_Light changed
then
val groupName = triggeringItem.groupNames.findFirst[ item | item.contains('S2W') ]
val group = ScriptServiceUtil.getItemRegistry.getItem(groupName)
syncGroup.apply(group, triggeringItemName, newState.toString())
end
You could probably simplify this even more, by creating another group called Multi_Way_Switches. Then add all those switch items to this group also. Then you just need to trigger when Member of Multi_Way_Switches changed. The rest of the rule can probably remain the same.
This way when you added a new group, you won’t have to change your rule triggers.
While we are talking alternative solutions, put all the Items into one Group and use Item tags to group the Items together.
rule "Switch 2-way"
when
Member of All2WayLights changed
then
val tag = triggeringItem.getTags().findFirst[ tag | tag.contains('S2W') ]
All2WayLights.members.filter[ i | i.getTags().contains(tag) && i.state != newState ]
.forEach[ i | i.sendCommand(newState) ]
end
That saves a little effort from creating multiple Groups. But it adds a little complexity in needing to keep the tags consistent. It’s harder to mess up Group membership, at least when using the UI. But this is shorter.
Thanks for the suggestions! A very elegant solution. I wasn’t aware about Tags until now
A small typo in the suggestion (which took me long enough to figure out…)
So, to be concise:
rule "Switch 2-way"
when
Member of All2WayLights changed
then
val tag = triggeringItem.getTags().findFirst[ tag | tag.contains('S2W') ]
All2WayLights.members.filter[ i | i.getTags().contains(tag) && i.state != newState ]
.forEach[ i | i.sendCommand(newState.toString()) ]
end