Group rules trigger

Are there any special trigger for groups?
When yes where would this be documented or implemented so we may look it up?
When not, why are there no special triggers?

Trigger i know off:

when
     // will trigger when group switch is pressed by user or
     // rule .sendCommand()
     Item <item> received command 
     // will trigger when a member is updated, 
     // pressed by user or 
     // changed from system as group switch was pressed or 
     // last/first group member changed state or
     // group switch is pressed by user or
     // rule .sendCommand()
     Item <item> received update 
     // will trigger when group switch is pressed or
     // last group member changed state
     // rule .sendCommand()
     Item <item> changed

Use cases:

  • Trigger rule when member received a command

Example: To find out which member off a group was triggered it needs persestence and then the last changed member can be found.
But if the member was changed by pressing the group switch this will execute the rule for each member but each time the lastUpdated member will be the same.
To overcome this one additional rule which triggers only if the group switch was pressed can be added. But then the Single member rules will fire additionally.

The solution would be a trigger which only fires when a member received a command.

.items

Group:Switch:OR(OFF, ON) group

Switch member_1 (group)
Switch member_2 (group)
Switch member_3 (group)
Switch member_4 (group)

.sitemap

sitemap GroupRulesTest label="Test Group Rules"
{
        Frame label="Member"
        {
          Switch item=member_1 label="Member 1"
          Switch item=member_2 label="Member 2"
          Switch item=member_3 label="Member 3"
          Switch item=member_4 label="Member 4"

          Switch item=group label="Group"
        }
}

.rules

rule "Member pressed"
  when
    Item group received update
  then
    // give persistence time to catch up
    Thread::sleep(500)

    val member = group.members.filter[s|s.lastUpdate("mapdb") !==null].sortBy[lastUpdate("mapdb")].last as SwitchItem
    val name   = member.name.toString
    
    logInfo("GroupTest", "Member "+ name +" to " + member.state )
end

rule "Group pressed"
  when
  // using "received command" to avoid trigger 
  // when gPlugs is Updated when the last Switch is pressed
  // only trigger when switch is actually changed by user
    Item group received command
  then
    Thread::sleep(200)
    logInfo("GroupTest", "Group changed to " + group.state )

end

log when group is pressed:

[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'group' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_1' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_2' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_3' received command ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Group changed to ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_4' received command ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_1 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_2 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_3 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_4 changed from OFF to ON
[INFO ] [home.event.GroupItemStateChangedEvent] - group changed from OFF to ON through member_4
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON

I thought maybe removing the “Thread:sleep” could be used to ensure that the single rule gets fires for each member thus reducing the rules to one. But unfortunatly this will not produce riliable behavior. Sometimes persistence is too fast so only one member gets posted or some other mixed stated of things.

[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_1 to OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_1 to OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to OFF

What would be the best way to gets this working properly?
Best case would be one rule, and a way to get the member which triggered this rule. But unfortunatly there is nothing like this.
Second choice two rules, avoid single rule to trigger when group was pressed.

Any suggestions are welcome?

1 Like

As far as triggers are concerned, a Group is like any other Item. So all of the triggers supported by an Item will work for a Group. There are no special triggers for a Group.

I know this is written up somewhere in the docs but I couldn’t find it in a quick scan so I’ll point you to Design Pattern: Working with Groups in Rules where I know it is written up.

I’ll augment your description on received update a little: “Will trigger potentially multiple times for any command or update sent to the Group or any of its members.” This is because the aggregation function calculates the new state of the Group incrementally and this causes an update on the Group at each increment.

There is no way to do this with a Group trigger at this time.

There is now a triggeringItem implicit variable (since with 2.2 released) that gets set to the Item that triggered a Rule. But this does not yet work with Groups. But if you list each Item as a Trigger you can make the Rule generic without the need for the persistence hack.

So I would suggest, until such time that triggeringItem is updated to also work with Group triggers (in work I believe) to have the two rules, one triggered by the Group receiving a command to capture that case, and the other with received command triggers for each of the Items listed separately. Inside this Rule you can then use triggeringItem to discover which Item triggered the Rule and drop the Persistence hack to figure out what Item triggered the Rule.

Then I would file a feature request issue to request addition of a way to trigger a Rule using a Group that:

  • triggers the Rule once when one of the Group’s members receives a command
  • populates triggeringItem with the Item that caused the Rule to trigger, if the Group itself received the command, triggeringItem will be set to the Group Item.
rule "Member pressed"
when
    Item member_1 received command or
    Item member_2 received command or
    Item member_3 received command or
    Item member_4 received command
then
   logInfo("GroupTest", "Member " + triggeringItem.name + " to " + triggeringItem.state)
end

rule "Group pressed"
when
    Item group received command
then
    logInfo("GroupTest", "Group received command " + receivedCommand)
end

It isn’t as pretty as it could be, but like I said, work is underway to let us replace the triggers on the first Rule with the Group.

4 Likes

Hi rikoshak,

triggeringItem was exactly what i was looking for although it would make my rules/life a lot easier if it worked on groups too. When you say it doesn’t work on groups i assume you mean it won’t tell you which item invoked the rule if the rule is listening to a group? If this is what you mean, would it be possible in a rule which accepts multiple Groups, to use the triggeringItem to identify the group that invoked the rule as opposed to the individual item? This would make my rule someone what simpler.

thanks

@rlkoshak thanks as always a great help.

rule "Single Plug"
  when
    Item member_1 received command or
    Item member_2 received command or
    Item member_3 received command or
    Item member_4 received command
  then
    logInfo("Item", "Member " + triggeringItem.name + " to " + triggeringItem.state)
 end

This would allready solve all my problems if then we can replace this with the group this would be great.

As for now this feature seem to not work properly i will raise an issue. The state of the item is not updated fast enough.

[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'group' received command OFF
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_1' received command OFF
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_2' received command OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_2 to ON
"[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_1 to OFF" // state in rule is not correct
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_3 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_3' received command OFF
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_4' received command OFF
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_2 changed from ON to OFF
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_3 changed from ON to OFF
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_1 changed from ON to OFF
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_4 changed from ON to OFF

adding a sleep helped

rule "Single Plug"
  when
    Item member_1 received command or
    Item member_2 received command or
    Item member_3 received command or
    Item member_4 received command
  then
    // give system time to catch up
    Thread::sleep(100)

    logInfo("GroupTest", "Member " + triggeringItem.name + " to " + triggeringItem.state)
 end
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'group' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_1' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_2' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_3' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_4' received command ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_1 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_3 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_4 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_2 changed from OFF to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_1 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_3 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_2 to ON
1 Like

I believe what you describe is by design and not an issue. Since you are triggering on RECEIVED COMMAND, the item still has not changed state. You can either trigger on CHANGED, or use the implicit variable receivedCommand with the expectation that the item will eventually be changed to the state that it received the command for. Hopefully, this info will also close your issue.

Also, here is the PR for group triggeringItem.

@5iver this would sure trigger when the state is updated.

Then the design is somehow not as i would expect it. On Trigger RECEIVED COMMAND i expect to be able to read out the command which triggered the change. This is solved by the implicite variable, but somehow not intuitiv, IMHO.

  1. A different question comes up then: the rule gets executetd multiple times when the group triggers the change of the members. And multiple times as the members updating the group.

  2. I would have wished that a group change would just do a postUpdate of the member. So when using changed you may trigger the rule when members get updated by the group, and using received command you may trigger when actually a command was issued.

rule "Single Plug"
  when
    Item member_1 changed or
    Item member_2 changed or
    Item member_3 changed or
    Item member_4 changed or
    Item group changed 
  then
    logInfo("GroupTest", "Member " + triggeringItem.name + " to " + receivedCommand)
 end
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'group' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_1' received command ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_4 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_2 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_1 to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member group to OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member group to OFF
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member group to ON
[INFO ] [ipse.smarthome.model.script.GroupTest] - Member member_3 to ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_2' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_3' received command ON
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'member_4' received command ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_1 changed from OFF to ON
[INFO ] [home.event.GroupItemStateChangedEvent] - group changed from ON to OFF through member_1
[INFO ] [home.event.GroupItemStateChangedEvent] - group changed from OFF to ON through member_3
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_2 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_4 changed from OFF to ON
[INFO ] [smarthome.event.ItemStateChangedEvent] - member_3 changed from OFF to ON

Don’t do that… it will likely get you into a loop! When one of the items changes state, the group’s state will change based on the aggregation function used. When the group changes state, a command will be sent to all of the items to match the group’s state. Why do you need the group state change to be in the triggers for the rule? If you manually change the state of the group, the rule will already fire once for each item. Or, depending on what you are trying to accomplish, maybe it is the items that should be removed from the trigger.

Can you explain what you are trying to achieve with your rule? Or are you just trying to make sense of the relationship between group and item states? IMO, both are much easier with a real world example :wink:.

I’ll second the request for a real world example because I’m struggling to come up with a case where I would want to trigger the same rule when a Group changes (or receives a command, or receives an update) and when the members of that Group receive a command.

I would expect that one would always need to do something different for the Group from the members and therefore the Group should be handled be a different Rule. And as @5iver points out, it is really easy to end up in an unintended loop.

1 Like

Now i tested and read more so

receivedCommand - will be implicitly available in every rule that has at least one command event trigger.

So using “changed” this will not be available!

@5iver and @rlkoshak my example would be

I need one rule for the members and one for the group, but the members should not trigger when the group was triggered, as there will be a different command line to execute for all.

Or if i know who triggered i may use if-else, so one rule.

The problem lies in the fact that i can not change the group in the iu and not get the single member rule to trigger as the group will sendCommand to the member.

The only reason to do it in one rule is if there is more than a couple of lines of common code when it is triggered by the Group verses the members. Since this seems unlikely in any case I can think of, even if you could put it all in one rule, you shouldn’t do so.

That is correct. Any command to a Group will be sent to all of its members. That is the designed and expected behavior. If you don’t want that behavior, you need to use a Proxy Item on your UI, not the Group.

But why is this the expected behavior?

If it would instead of sendCommand use postUpdate you could distinguish if the member was triggered or Updated.

And use the trigger recieved command or changed to have the rule for the group trigger to this different events!

Because one of the primary reasons for Groups to exist is so one can send it a command and have it applied to all of that Group’s members. I wasn’t privy to the reasoning for why Groups were created in the first place, but I would not be surprised if this was not the primary reason they were created.

If it did that one of the most common use cases for Groups would become broken. One could no longer create, for example, a Group of Switches and control all of the Switches as one entity by sending commands to the Group. One of the top three most common uses of Groups is this use case.

Modifying this behavior might help with your specific use case but it would break a whole lot of other well-established use cases and change the well-established behavior of Groups. There is also work underway to address your use case (i.e. properly populating triggeringItem for Rules that are triggered by a Group). Finally, I’m still dubious that it is both necessary and appropriate to trigger a rule commands or changes to a Group and commands or changes on all the members of that Group in the same rule. A concrete example might help pursuade me but right now I have not come up with anything on my own.

A lock solved my problem.
As the execution of the commands can not be parallel, there is only one transmitter, a lock in euch Rule provided a mutex behavior and thus everything works as intended.

Just in case someone is reading this old post - the “Member of” trigger is what you are looking for:
https://www.openhab.org/docs/configuration/rules-dsl.html#member-of-triggers

I am reading this old post :smiley:
and actually ask myself…
can I apply a command then to the same group or does it end in a loop of death?

so lets say

when
Member of MYGROUP changed
then
MYGROUP.postUpdate(x)
end

? maybe I should dare just trying it :smiley:

edit: which actually brings me to the question: can I now use the state of that Member of the group that “changed”?
If e.g. a member of the group changed to Dimmer Value = 60 I would like that the whole group changes to 60 … hhmmm

That’s always going to be the most informative and quickest way to answer these sorts of questions. I highly encourage everyone to try it out first, then post a question to the forum only if it didn’t work or you don’t understand what happened.

But in answer to your question, the rule will trigger when Members of the Group change, not the Group itself. So if you issue a command to the Group, the command will go to each of the Items members. The rule will be triggered exactly once for each of the members that changed in response to the command.

But this part is not allowed and doesn’t make sense. You can’t really postUpdate a Group Item. The Group Item’s state is controlled by the aggregation function that calculates the state based on the states of all the members. But let’s assume that this is a MYGROUP.sendCommand instead.

That could result in a short loop but is unlikely to result in an infinite loop since you are using the changed trigger. One or more members of the Group change, a command is sent to the Group which goes out to all of the members. That command may result in one or more of the members themselves changing. As a result the rule will trigger again based on those changes. Eventually, the Items will all reach a stable state and stop changing and the rule will stop triggering.

Obviously this is horribly inefficient and not a good way to go. But the direct take away is to notice that the rule triggers based on the Members of the Group changing, not on the Group itself changing.

Now a question for the student. What happens if you trigger the rule with Member of MYGROUP received command and have MYGROUP.sendCommand(x) in the body of the rule? It isn’t good.

As written, this doesn’t really make a whole lot of sense. What does “the whole group changes” mean? That all members of the Group are commanded to the same state so that all the other Dimmers move to 60 too? Or all the members of the Group are update to 60? Or the Group Item’s state becomes 60?

If you want to command all the other members of the Group so they all stay in sync, you would probably want to:

  • send a command to the Group
  • create a timer
  • as long as the timer is running ignore (i.e. return from the Rule) any new changes

This will avoid a whole lot of unnecessary commands pounding your devices.

You can’t postUpdate to a Group. Group states (if used at all) are calculated from member states by the optional aggregation function.

Yes, there are plenty of ways to write rules resulting in loops. Careful trigger selection usually helps, e.g. change not update.

Yes thats it. Will do this.
@rlkoshak do you have a hint If there is an easy way which Item of that group caused the Trigger in above rule? I need to know this to get the State of the correct Item and send that value as a command to the group

Thanks :slight_smile:

when
Member of MYGROUP changed
then
So

triggerintItem is the name of the Item that triggered the rule. But if all you want is the new state, notice there are implicit variables for that too.

1 Like

thank you so much
this works pretty fine … even without a timer … it might fire redundantly for a second or so until “changed” stops and then everything is in sync :slight_smile: yehhaa :slight_smile:

rule "Wohnen Beleuchtung Sync"
when
    Member of gEG_LICHT_WOHNEN_DIMMER changed
then
   gEG_LICHT_WOHNEN_DIMMER.sendCommand(newState.toString)
end