Group multiple if then x in dsl rules

Hi,
example:

rule "test"
when
        Item test1 changed or
        Item test2 changed
then
       if(triggeringItemName.state == "test1" && example1.state == ON ) {
         stuff.sendCommand(ON)
         }
      if(triggeringItemName.state == "test2" && example2.state == ON ) {
        stuff.sendCommand(ON)
        }
end

lets say we have 6 of these cases.
is there a way to mention stuff.sendCommand(ON) only once in this rule? rather then repeating it after every if ?

to answer my self, is this the most efficient way:

rule "test"
when
        Item test1 changed or
        Item test2 changed
then
       if((triggeringItemName.state == "test1" && example1.state == ON ) ||
         (triggeringItemName.state == "test2" && example2.state == ON )) {
         stuff.sendCommand(ON)
         }
end

another question, if the rule trigger is

Time is TestAlarm1

will the triggeringItemName.state give me “TestAlarm1” ?
or it only works for items that received command?

sorry, another question about changed to ON or changed to OFF:

rule "test"
when
        Item test changed
then
       if(test.state == ON) {
         stuff1.sendCommand(ON)
         }
       if(test.state == OFF) {
         stuff2.sendCommand(ON)
         }
end

will this work reliably? I remember I might have had some issues with these, something like the item state does not change fast enough so when the rule executes it reads the old state, so I separated it into 2 rules, one with “test changed to ON” and another “test changed to OFF”. Or am I mistaken and this should work within a single rule?

I think I prefer the first version, easier to read and follow.

There might be a trick in RulesDSL to do this more easily, but I’m more used to JRuby and this is how I would do it in JRuby:

rule "test" do
  changed test1, attach: example1
  changed test2, attach: example2
  changed test3, attach: example3
  # add as many as you like
  run do |event|
    stuff.on if event.attachment.on?
  end
end

No, this won’t work for two reasons:

  • For time triggers, triggeringItemName simply doesn’t exist, currently.
  • Even if it existed, you can’t write triggeringItemName.state because xxxItemName would be a simple string, not an item object. In RulesDSL it’s a bit messy, but doable to grab the item object from a name.

In JRuby, you could do it like this (right now already implemented)

rule "datetime item trigger" do
  every :day, at: TestAlarm1
  run do |event|
    logger.info "The triggering item state is: #{event.item.state}"
  end
end

Although, theoretically, the item’s state should be very close to the current time since that’s what caused the rule to trigger.

It might work, but technically you should use newState. See: Textual Rules | openHAB

So it should be:

rule "test"
when
        Item test changed
then
       if(newState == ON) ) {
         stuff1.sendCommand(ON)
         }
       if(newState == OFF) ) {
         stuff2.sendCommand(ON)
         }
end

In JRuby:

rule "test" do
  changed test
  run do |event|
    stuff1.on if event.on?
    stuff2.on if event.off?
  end
end

Or using the attachment trick like above:

rule "test" do
  changed test, to: ON, attach: stuff1
  changed test, to: OFF, attach: stuff2
  run do |event|
    event.attachment.on
  end
end
1 Like

I’m pretty sure that the given code in Postings 1 and 2 does not work as intended, because triggeringItemName is a string variable, so there is no method .state for this variable.
The a more efficient way would be to build two groups:

gTest {test_1, test_2, test_3, test_4, test_5, test_6}
gExample {example_1, example_2, example_3, example_4, example_5, example_6}

Now it’s possible to use another Trigger and to shorten the code:

rule "test"
when
    Member of gTtest changed
then
    val strItem = triggeringItem.name.split("_").get(1)
    val swItem  = gExample.members.filter[i|i.name.endsWith(strItem)].head

    if(swItem.state == ON)
        stuff.sendCommand(ON)
end

Keep attention to the fact, that I changed the naming slightly, as it’s way easier to get the second name from the first name when using underscores. But of course it’s possible to get the part after “test” in other ways.

Afaik Time is will not set triggeringItemName.
Please be aware that the rule isn’t triggered on a command, but on a change, that is: the state of the item changed.

In question of the third posting: You have typos in the code (more closing brackets then opening brackets). A cleaner solution:

rule "test"
when
    Item test changed
then
    if(newState == ON)
        stuff1.sendCommand(ON)
    else if(newState == OFF)
        stuff2.sendCommand(ON)
end

newState is another implicit variable which is set when the rule is triggered by a changed or received update event.
If a rule is triggered by a received command there is another implicit variable receivedCommand.

HI, thanks for all the answers and clarifications.
This code actually indeed works, even though you say it shouldn’t.
Is there any better/official way then triggeringItemName.state to get the triggering item? In case I don’t want to create group? because the items that are triggering are totally different, one is a physical button, another one is Presence. I just want to know who triggered the rule without having them in a group just for this rule…

There is no triggeringItemName.state, it’s only triggeringItemName. Maybe openHAB ignores the .state silently.

As shown in my example, it would be way more efficient to use Groups.

You can use as many groups as you want.
You can place as many Items in as many groups as you want.
The one and only rule is: only one of the groups may be part of the semantic model.
So, there is absolute no point in not using groups here.

thanks, indeed .state is obsolete here, works without it

  if(triggeringItemName == "TestSwitch")

this works.

I still don’t understand what is wrong with this approach, every time I want to know who triggered the rule, I do the above line. Why would I further complicate with creating numerous groups if this works.
Unless you are secretly planning to deprecate triggeringItemName and these are hints I should stay away from it :joy:

I’m confused, aren’t general rules that statements need {} brackets?
So, { stuff1.sendCommand(ON) } and { stuff2.sendCommand(ON) } no?
I tried the rule without brackets, and it worked but threw some error…

No. The rule is, that a conditional branch will only affect the next command. If you want to use more than one command conditionally, you have to use curly brackets to mark them as a block of commands:

if(a == b)
logInfo("message","message 1")
logInfo("message","message 2")

message 2 will be logged, regardless if a == b or not.

if(a == b) {
logInfo("message","message 1")
logInfo("message","message 2")
}

Either both or none message will be logged :slight_smile:

There is nothing wrong with it, but if you want to check 6 individual items, the rule is (at least) like this:

rule "test"
when
    Item test1 changed or
    Item test2 changed or
    Item test3 changed or
    Item test4 changed or
    Item test5 changed or
    Item test6 changed
then
    if(triggeringItemName == "test1" && example1.state == ON ) stuff.sendCommand(ON)
    if(triggeringItemName == "test2" && example2.state == ON ) stuff.sendCommand(ON)
    if(triggeringItemName == "test3" && example3.state == ON ) stuff.sendCommand(ON)
    if(triggeringItemName == "test4" && example4.state == ON ) stuff.sendCommand(ON)
    if(triggeringItemName == "test5" && example5.state == ON ) stuff.sendCommand(ON)
    if(triggeringItemName == "test6" && example6.state == ON ) stuff.sendCommand(ON)
end

or:

rule "test"
when
    Item test1 changed or
    Item test2 changed or
    Item test3 changed or
    Item test4 changed or
    Item test5 changed or
    Item test6 changed
then
    if((triggeringItemName == "test1" && example1.state == ON) || 
       (triggeringItemName == "test2" && example2.state == ON) || 
       (triggeringItemName == "test3" && example3.state == ON) || 
       (triggeringItemName == "test4" && example4.state == ON) || 
       (triggeringItemName == "test5" && example5.state == ON) || 
       (triggeringItemName == "test6" && example6.state == ON)) stuff.sendCommand(ON)
end

There are numerous ways to get the same result:

rule "test"
when
    Item test1 changed or
    Item test2 changed or
    Item test3 changed or
    Item test4 changed or
    Item test5 changed or
    Item test6 changed
then
    switch(triggeringItemName) {
        case "test1" : if(example1.state == ON) stuff.sendCommand(ON)
        case "test2" : if(example2.state == ON) stuff.sendCommand(ON)
        case "test3" : if(example3.state == ON) stuff.sendCommand(ON)
        case "test4" : if(example4.state == ON) stuff.sendCommand(ON)
        case "test5" : if(example5.state == ON) stuff.sendCommand(ON)
        case "test6" : if(example6.state == ON) stuff.sendCommand(ON)
    }
end

But you will always end in many lines of similar code which can be avoided by using Group Items and matching names.
Please be aware that there is no need to use Item names like shown above, the only requirement is that it’s (easily) possible to “calculate” the name of the second condition Item name from the first (the triggering Item) one.

understood, thanks. I know I mentioned multiple items as an example question, but in reality I needed just part of the rule to check which one of 2 items triggered it

Well in case of just two items, obviously testing with triggeringItemName is the easiest option :slight_smile: