Combining similar rules

Use the implicit variable triggeringItem

rule "myrule"
when
    Item item1 changed or
    Item item2 changed
then
    var location = triggeringItem.state.toString
    var itemName = triggeringItem.name.toString
    if (itemName = item1) {
        ....
    }
    ....
end
1 Like

That’s it?
Looks doable :joy:
Will try it soon.

It could be even simpler if the Items are in a Group.

rule "myrule"
when
    Member of MyGroup changed
then
    var location = triggeringItem.state.toString
    ...
end
1 Like

I didn’t know this existed…learned something new today.
I just cut down a whole group of rules from 363 lines to 160. I could probably shave another 10 or so lines of by doing the group thing… 1 Step at a time. I should make sure this all works now that I’ve monkeyed with the whole thing!

Thanks!!

hmm, something not working…

rule:

rule "whatever"
when
    Item lrF1 received command or
    Item lrF2 received command or
    Item lrF3 received command
then
    var itemName = triggeringItem.name.toString
    logInfo("triggering item: ",itemName.toString)
    if ( itemName = lrF1) {
        logInfo("who triggered: ","Fan 1")
        if ( lrF1.state == 1 ) {
            fan1.sendCommand(ON)
        } else {
            fan1.sendCommand(OFF)
        }
    }

I’ll only get the first loginfo and the name will match but then comparing itemname to the item doesn’t work. I get no second loginfo…

triggeringItem is an Item.

lrf1 is an Item

triggeringItem.name is a String

The string “lrF1” does not equal the Item lrF1

NOTE: Because triggeringItem.name and itemName are already a String, it is redundant to call toString on them,

rule "whatever"
when
    Item lrF1 received command or
    Item lrF2 received command or
    Item lrF3 received command
then
    val itemName = triggeringItem.name
    logInfo("triggering item: " , itemName)
    if(itemName == "lrF1") {
        logInfo("who triggered: ","Fan 1")
        fan1.sendCommand(if(receivedCommand == 1) ON else OFF)
    }
    else if(triggeringItem.name == "lrF2") {
...

Some further notes on what I changed:

  • use val instead of var for constants like itemName
  • don’t call toString on Strings
  • use quotes around “lrF1” to compare it itemName
  • use receivedCommand instead of lrF1.state because the Rule will be triggered before the Item get’s updated so lrF1.state is not guaranteed to equal the command that triggered the Rule
  • I use the trinary operator to reduce the conversion from lrF1.state to a command to fan1 down to one line.

You can make this Rule generic by applying Design Pattern: Associated Items and Design Pattern: How to Structure a Rule. Then the complete rule could be something like

rule "whatever"
when
     Member of lrFs received commands
then
    val name = triggeringItem.name
    val fanName = "fan" + name.charAt(name.length() - 1)
    val newState = if(receivedCommand == 1) "ON" else "OFF"

    logInfo("whatever", "Sending " + newState + " to " + fanName)
    sendCommand(fanName, newState)
end

That’s all there is to it. Assuming you continue to use the same naming scheme and have no more than 10 lrFs (i.e. no two digit numbers) this rule will work for all of your lrFs and fans.

In the interim (prior to your reply) I got it working with:

	if ( itemName.toString == "lr24CLSw" ) {

I do a brief Thread::sleep prior to anything, the rule was just a quick stand in for the huge blob i’m actually using. I used “received command” Since the thing sending the command might not always know whether it’s sending an on or off (things get out of sync during reboots and system starts). Months ago before i understood the order in which things came that problem absolutely plagued me on some rules. Thanks for your tips, I’ll do some additional cleanup later this evening. Always appreciate your help @rlkoshak

And yeah, the groups thing is planned, too. one step at a time. If I change too much and something breaks it makes it harder for me to figure out where it got screwed up. Programming is not my specialty!

It’s a good approach. I didn’t post the above assuming you would just use it, though that is cool too. I mainly posted it to show you an end goal you can get to / work towards. It might inform your decisions on what to tackle learning when and give you some changes you can make early on that will help you later (e.g. use a naming scheme where you can construct the name of the fan Item using a part of the name for the triggering Item.

Good luck!

oh for sure. my naming scheme for items has changed and evolved several times since starting down this openhab path. Then Ive introduced proxies and on and on. I have quite a mix of stuff going on. It’s nice to go back and rewrite an old rule with better item names, proxies, checking to see if i actually need to send a state or not (why clutter the log?), and on and on. Another time you helped me without knowing it was mapdb for restoreonstartup. I was having terrible slowness because i was persisting a ton of stuff with rrjd4 but i had no idea it was the persistence was doing it. At some point I came across it that that was the problem and then found a thread of yours about using mapdb just for that purpose.

sorry to clog the thread up. someone may find that info useful, you just never know. They might be in here reading about triggeringitem and see the persistence slow down and say “hey, i’ve got that problem too!”

Me, too.
That will enable me to cut down code seriously I guess :-):grinning:

Don’t worry, just go ahead, I learn a lot from your discussions and I am glad, that my topic here is not just my personal issue :slight_smile:

1 Like

You should be able to collapse your rule down further, like this…

rule "whatever"
when
    Item lrF1 received command or
    Item lrF2 received command or
    Item lrF3 received command
then
    logInfo("triggering item: ",triggeringItem.name)
    sendCommand(triggeringItem.name.replace("lfr","fan"),if (triggeringItem.state == 1) "ON" else "OFF")
end

@rlkoshak So, I’m building the rule. The switch can be actuated by 3 different sources and I need to update the other two sources when a command comes in… this part I don’t know how to do (the rest I understand, mostly).
The items would be

lr24Fan1, lr35Fan1, and lrOhFan1 (a 2.4 inch nextion, a 3.5 inch nextion, and a sitemap item).

if lr24Fan1 gets a 1 or 0 i need to sendCommand to lr35f1 and postUpdate to lrOhFan1
if lrOhFan1 gets an on or off I need to sendCommand to lr35f1 and lr24f1

the ohFan I think i got:

val ohFanName = "lrOhFan + name.charAt(name.length() - 1)
postUpdate(ohFanName, newState)

What I’m not sure about is how to decide whether to send to lr24 or lr35.

wow, hah, soon my hundreds of lines of rules spread across 20 files will be condensed down to 1 file at a measly 50 lines!

Thanks for the suggestions, I can see stuff in there that I understand and will be able to use. :smiley:

For now, until I understand more I can at least condense the nextions into separate groups and come up with this:

rule "Ceiling Fans - Individual via 2.4 Nextions"
when
	Member of gLrFans24 received commands
then
	sendCommand(triggeringItem.name.replace("lr24","lr"),if (triggeringItem.state == 1) "ON" else "OFF")
	postUpdate(triggeringItem.name.replace("lr24","lrOh"),if (triggeringItem.state == 1) "ON" else "OFF")
	sendCommand(triggeringItem.name.replace("lr24Fan","lr35f"),if (triggeringItem.state == 1) "1" else "0")
end

supposing that all works correctly that takes the already condensed rules from 189 lines to 39, impressive

You don’t want to sendCommand because that will retrigger the rule and you will end up in an infinite loop.

If you have to sendCommand then this just got REALLY complicated because you need to somehow distinguish between a command received by pressing the button and a command received from the Rule.

We can short circuit this a bit and make it so the Role only gets called a couple of extra times, but can your fans handle getting commanded twice in very fast succession?

Basically what you will do is send your command and update to all three Items, but only if the command you are about to send us different from the current state of the Item. This will short circuit the loop.

Thanks a lot @rlkoshak.
It worked right away with no single error.
I just merged 3 rules doing the same - now just one starting with:

rule "Lookup Address for Geolocations"
when
    Member of G_Locations changed
then
    var itemName = triggeringItem.name.toString
	var location = triggeringItem.state.toString

// get lat lon from item
    val String lat = transform("REGEX", "(.*),.*", location)
    val String lon = transform("REGEX", ".*,(.*)", location)
	logInfo("car.rules", "lat: >" + lat + "< / lon: >" + lon + "<")

notice lr24Fan1 and lr24f1 are two different items. One is an input to openhab from the panel the other an output from openhab to the panel. lr24(35)Fan1 is the switch while lr24(35)f1 is the command sent back to the panel to update the display.

using suggestions in this topic i’ve now reduced a rule file from an original size of 363 lines to 110 and learned a great deal in the process. I’m sure this will lead to reduced execution time and, of course, greatly reduces complexity. I know I can apply this to quite a few other rules as well.

My assumption though is if you send a command to one and it changes state that results in a command sent to the other to report the change in state. If that isn’t the case then the problem may not be that big of one.

When writing like Rules like these make sure to keep an eye on how events cascade. You may be good to go. I’m glad you found the examples useful.

They’re very separate events. Definitely didn’t want things re-triggering.

Here are the rules as they stand (using @5iver’s hint):

rule "Ceiling Fans - Individual via 2.4 Nextion"
when
	Member of gLrFans24 received command
then
	Thread::sleep(100)
	sendCommand(triggeringItem.name.replace("lr24","lr"),if (triggeringItem.state == 1) "ON" else "OFF")
	postUpdate(triggeringItem.name.replace("lr24","lrOh"),if (triggeringItem.state == 1) "ON" else "OFF")
	sendCommand(triggeringItem.name.replace("lr24Fan","lr35f"),if (triggeringItem.state == 1) "1" else "0")
end

rule "Ceiling Fans - Individual via 3.5 Nextion"
when
	Member of gLrFans35 received command
then
	Thread::sleep(100)
	sendCommand(triggeringItem.name.replace("lr35","lr"),if (triggeringItem.state == 1) "ON" else "OFF")
	postUpdate(triggeringItem.name.replace("lr35","lrOh"),if (triggeringItem.state == 1) "ON" else "OFF")
	sendCommand(triggeringItem.name.replace("lr35Fan","lr24f"),if (triggeringItem.state == 1) "1" else "0")
end

Using his example or yours, how could I combine these two? I don’t know quite well enough how to manipulate item names inline. Also I had no idea so much could be done inline of the sendcommand/postupdate, very cool.

inputs to openhab from touchpanel
lr24Fan1(1-4)
lr35Fan1(1-4)

outputs to panel from openhab
lr24f1(1-4)
lr35f1(1-4)

OH switch for same
lrOhFan1(1-4)

Proxy to sonoff switch:
lrFan1(1-4)

I have groups already created to combine all of the fan switches (gLrFans) for when I understand how the rules can be combined.