More efficient Rule

Hi

I am having some rules to update state when i use the buttons to control my lights.

rule “Knapp Garderob”
when
Item Light_GF_Corridor_Wardrobe_BTN1 received update
then
postUpdate(Light_GF_Corridor_Wardrobe , Light_GF_Corridor_Wardrobe_BTN1.state.toString)
end

rule “Knapp Vardagsrum Tak”
when
Item Light_GF_Living_Ceiling_BTN1 received update
then
postUpdate(Light_GF_Living_Ceiling_BTN1 , Light_GF_Living_Ceiling_BTN1.state.toString)
end

rule “Knapp Köksbord Tak”
when
Item Light_GF_Living_CeilinTable_BTN1 received update
then
postUpdate(Light_GF_Living_CeilinTable , Light_GF_Living_CeilinTable_BTN1.state.toString)
end

rule “Knapp Källarkorridor”
when
Item Light_C_Hallway_BTN1 received update
then
postUpdate(Light_C_Hallway , Light_C_Hallway_BTN1.state.toString)
end

rule “Knapp Källarkorridor”
when
Item Light_C_Hallway_BTN2 received update
then
postUpdate(Light_C_Hallway , Light_C_Hallway_BTN2.state.toString)
end

Is there anyway to do this more efficient,
All buttons is in the same group Buttons and the naming of the buttons is always name of the lamp and the 4 last characters is _BTN?.

/Mike

Put all of the buttons in a group like you have. You don’t list it so we will call it gLightButtons.

The rule will then be something like:

rule "Buttons to light mapping"
when
    
    // Hack to get at the most recently pressed button
    Thread::sleep(100) // experiment with this value, the purpose is to give persistence time to save the state so lastUpdate will work properly. You may not need it at all but if the wrong button keeps coming up, make this sleep longer
    val button = gLightButtons.members.sortBy[lastUpdate].last

    val lightName = button.name.substring(0, button.name.lastIndexOf('_')) // test this, there may be an off by one error here
    postUpdate(lightName, button.state.toString)
end

I think the above will fail silently or with a hard to read error if lightName doesn’t exist as an Item. You can add a check by putting all of your lights in a group (gLights) and checking to see if the light exists in the group before calling postUpdate.

val light = gLights.members.filter(s|s.name == lightName).head      
if(light != null) light.postUpdate(button.state.toString)
else logError("Rule name", lightName + " does not exist!")

Finally, depending on what your Light and Button items are bound to, you might be able to bind them to the same Item which will keep them all in sync for you, though I’ve never successfully done that. You usually see this approach with MQTT type switches you can search for to find some examples.

Hi Rich

When i just try

rule “Buttons to light mapping”
when
Item Buttons received update
then
Thread::sleep(500)
val changedbutton = Buttons.members.sortBy[lastUpdate].last
end

I receive an error when I press a button

2015-12-16 22:11:46.137 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule ‘Buttons to light mapping’
java.lang.NullPointerException: null

/mike

The most common error is not having persistence setup correctly or at all. Do you have persistence setup? Are all the members of Buttons being saved to persistence? If using rrd4j, are you using an every minute strategy for all the members of Buttons?

I have rrd4j and everyChange on everything (*)

// persistence strategies have a name and a definition and are referred to in the “Items” section
Strategies {
// for rrd charts, we need a cron strategy
everyMinute : “0 * * * * ?”
default = everyChange

}

Items {

// let’s only store temperature values in rrd

  • : strategy = everyChange, restoreOnStartup

Weather_Chart*, Disk_Chart* ,Temperature* ,Humidity* : strategy = everyMinute

}

/Mike

Try adding Buttons* to everyMinute and see if tha helps. Beyond that I’m stumped. For some reason either Buttons is null, Buttons.members is null, or lastUpdate is returning null. The most likely seems to be the lastUpdate but who knows.

The persistence based setup will not work correctly if more than one item get triggered at the same time, so this solution is not really good.

You might want to look at the Jsr223 script engine, here you can do something like this:

class LightRule(Rule):
    def __init__(self):
        all = ItemRegistry.getItem("gLightButtons")
        
        self.__eventTrigger = [StartupTrigger()]
        
        for item in all.getMembers():
            self.__eventTrigger.append(ChangedEventTrigger(item.name, None, None))

    def getEventTrigger(self):
        return self.__eventTrigger

    def initTrigger(self):
        pass

    def updateState(self, item):
        print "the item triggered:", item

    def execute(self, event):
        if event.triggerType == TriggerType.STARTUP or event.triggerType == TriggerType.TIMER:
            self.initTrigger()
        elif event.triggerType == TriggerType.CHANGE:
            self.updateState(event.item)

@rlkoshak It is now working, I think that the problem was that I had not used some of the Buttons.

Working script:

rule “Buttons to light mapping”
when
Item Buttons received update
then
Thread::sleep(200)
val changedbutton = Buttons.members.sortBy[lastUpdate].last
val lightname = changedbutton.name.substring(0, changedbutton.name.lastIndexOf(‘_’)) // test this, there may be an off by one error here
postUpdate(lightname, changedbutton.state.toString)
logInfo(“Buttons”, changedbutton.name.toString + " " + changedbutton.state.toString )
end

But there is one problem, each press generates two entries

2015-12-28 11:11:27.341 [INFO ] [g.openhab.model.script.Buttons] - Light_GF_Living_Ceiling_BTN1 ON
2015-12-28 11:11:27.352 [INFO ] [g.openhab.model.script.Buttons] - Light_GF_Living_Ceiling_BTN1 ON
2015-12-28 11:11:41.119 [INFO ] [g.openhab.model.script.Buttons] - Light_GF_Living_Ceiling_BTN1 OFF
2015-12-28 11:11:41.135 [INFO ] [g.openhab.model.script.Buttons] - Light_GF_Living_Ceiling_BTN1 OFF

@smerschjo I will try Jsr223.
If I enable JSR 223 Script Engine, can I still use traditional Rules?

/Mike

Yes you can keep your existing rules. More details can be found here: JSR223 Script Engine · openhab/openhab1-addons Wiki · GitHub

This is expected behavior unfortunately. The way that the state of a Group is calculated causes it to be updated for each member of the group so, if you have two members of group Buttons then a “received update” triggered rule will be triggered twice for each button press.

This will not be fixed in OH1, and some could argue it really isn’t so much a bug as it is unexpected behavior. I don’t know how groups will work in OH 2.

I’ve gotten around this problem by writing my rules so the double trigger doesn’t really matter and where I can’t do that I list each Item in the when clause instead of triggering on Group updates. I suppose you could also implement some sort of time based check and ignore the second one.

Hi

In the code:

How do i check state of lightname.

I want to check if the state is different then changedbutton.

/mike

It isn’t straight forward.

You will need to put all you light items you want to find by name in the same group. Then you can find it by name using

val SwitchItem myLight = gLights. members. filter. [name=lightname]. head
if(myLight. state == ON) ... 

I typed this on my phone so hopefully you can decypher the mistakes.

1 Like