Triggering a rule through a command

I have the following rule:

rule "Switch off bureau light common after 2 minutes"
when
Item Sw_Bureau_B received command
then
if (receivedCommand==ON) {
if (timer==null) {
timer = createTimer(now.plusSeconds(120)) [|
sendCommand(Sw_Bureau_B, OFF)
timer.cancel
timer = null
]
}
logInfo(“Bureau light”, “Will switch off bureau light common within 2 minutes”)
} else {
if (timer!=null) {
timer.cancel
timer = null
logInfo(“Bureau light”, “Prematurely switched off bureau light timer”)
}
}
end

When I trigger the switch Sw_Bureau_B through the web or smartphone interface, I see the log text and the light is indeed switched off after 2 minutes.

However, when I press the physical button, the rule doesn’t get triggered. The UI of both the web and the smartphone is updated accordingly, but the rule is not triggered. I don’t see any log text appearing.

This is the items part for the switch:

Switch Sw_Bureau_B
{mqtt=
">[geertvc:home/verdiep/Switch/Sw_Bureau_B:command:ON:default]
,>[geertvc:home/verdiep/Switch/Sw_Bureau_B:command:OFF:default]
,<[geertvc:home/verdiep/Switch/Sw_Bureau_B:state:default]"
}

This is the sitemap part for the switch:

    Text label="Bovenverdieping" icon="firstfloornew" {
        Frame label="Verlichting Bureau" {
            Switch item=Sw_Bureau_A label="Lamp bureau - wand"
            Switch item=Sw_Bureau_B label="Lamp bureau - algemeen"
        }
    }

This is the openHAB log:

2016-10-12 19:54:12.499 [INFO ] [runtime.busevents ] - Sw_Bureau_B state updated to ON
2016-10-12 20:05:10.396 [INFO ] [runtime.busevents ] - Sw_Bureau_B state updated to OFF

What am I possibly missing to get the rule also triggered when I press the physical switch?

When you physically toggle the switch and the device reports its state back to OH, OH uses postUpdate to update the state of the Item. That is what the state verses command part of the MQTT binding config you are using means.

And that is correct because you don’t want it to use sendCommand in this case because that will cause the binding to turn around and resent the same command back to the device. This could easily result in an infinite loop if the device reports this command back.

Change your rule trigger to received command or changed and your rule will get triggered as you want.

Hi Kjetil,

Although you removed your post, I still got your response in my e-mail box. Thanks for that.
You were saying at the end of your reply that you removed the out-bindings as you presumed I was actually not able to update the state of my physical switches.
Well, yes, I am. I have physical switches with small LED’s inside to have some visual feedback to know if I pressed the switch or not.
Using the openHAB app on my browser or smartphone, it’s as if I press the switch physically. That is, when I change the state of the switch on my browser or smartphone from, say, off to on, I can get the LED in the switch blinking. Just as would happen if I press the switch itself
That’s the nice thing about this whole openHAB set-up, which I like very much…

Hi Rich,

Unless I don’t interpret your answer/proposal correctly, but I do have received command into my current rule. Did you mean something else maybe?

I’ve changed my rule into this now (changed to 10 seconds to speed up the testing):

rule "Switch off bureau common light after 2 minutes"
when
    Item Sw_Bureau_B changed from OFF to ON
then
        if (timer==null) {
            timer = createTimer(now.plusSeconds(10)) [|
                sendCommand(Sw_Bureau_B, OFF)
                timer.cancel
                timer = null
            ]
            logInfo("Bureau light", "Will switch off bureau light common within 2 minutes")
        }
end

Now, when I press the physical switch, the rule indeed gets triggered and both the web and the smartphone UI change accordingly.
The other way round works too: manipulating the switch on the web or smartphone UI triggers the timer and modifies the physical switch accordingly (that is, blinking a LED inside the switch when set to ON, switching off the LED otherwise).

Is this the correct approach now?

Yes, I meant use received update or changed, not received command.

The correct approach is one that works. :wink: So long as you are OK with this rule only triggering when the light was OFF and it turned ON all is good. If you need it to trigger any time the state changes you can remove the from OFF to ON.

Hi Rich,

I’ve followed your advice and adapted my rule like so:

rule "Switch off bureau light common after 10 seconds"
when
    Item Sw_Bureau_B changed (or 'received update', behaviour is the same...)
then
    if (Sw_Bureau_B.state==ON) {
        if (timer==null) {
            timer = createTimer(now.plusSeconds(10)) [|
                sendCommand(Sw_Bureau_B, OFF)
                timer.cancel
                timer = null
                logInfo("Bureau light", "Time elapsed, switched off light")
            ]
        }
        logInfo("Bureau light", "Will switch off bureau light common within 2 minutes")
    } else {
        if (timer!=null) {
            timer.cancel
            timer = null
            logInfo("Bureau light", "Prematurely switched off bureau light timer")
        }
    }
end

When the rule is ran, I have the following output:

    2016-10-15 14:50:45.193 [INFO ] [runtime.busevents             ] - Sw_Bureau_B received command ON
    2016-10-15 14:50:45.198 [DEBUG] [.mqtt.internal.MqttItemBinding] - Publishing command ON to home/verdiep/Switch/Sw_Bureau_B
    2016-10-15 14:50:45.213 [INFO ] [runtime.busevents             ] - Sw_Bureau_B state updated to ON
    2016-10-15 14:50:45.219 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 8 to topic 'home/verdiep/Switch/Sw_Bureau_B'
    2016-10-15 14:50:45.243 [INFO ] [nhab.model.script.Bureau light] - Will switch off bureau light common within 10 seconds
    2016-10-15 14:50:55.251 [INFO ] [runtime.busevents             ] - Sw_Bureau_B received command OFF
    2016-10-15 14:50:55.253 [DEBUG] [.mqtt.internal.MqttItemBinding] - Publishing command OFF to home/verdiep/Switch/Sw_Bureau_B
    2016-10-15 14:50:55.260 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 9 to topic 'home/verdiep/Switch/Sw_Bureau_B'
    2016-10-15 14:50:55.267 [INFO ] [runtime.busevents             ] - Sw_Bureau_B state updated to OFF
    2016-10-15 14:50:55.276 [INFO ] [nhab.model.script.Bureau light] - Time elapsed, switched off light
    2016-10-15 14:50:55.345 [INFO ] [nhab.model.script.Bureau light] - Prematurely switched off bureau light timer

However, I don’t understand why I get the last line (Prematurely switched off bureau light timer). I shouldn’t get this.

When looking to the rule, the code should go into the else block of the main if construction when the state of Sw_Bureau_B is OFF (that is, after the 10 seconds).

But I have a “timer guard” to check if the timer is null or not into that else block.

Since just after the sendCommand(Sw_Bureau_B, OFF) I’ve put the timer to null when my 10 seconds timer elapsed (and I also canceled the timer first in that block of code but that might not be needed in this case…), I’m expecting that the if condition in the else block is failing.

So, I don’t understand why the logInfo("Bureau light", "Prematurely switched...") is still executed in that case… That should only happen if the switch is put to OFF within 10 seconds after it’s put to ON, I assume…
You have any clue?

I also sometimes have this output:

2016-10-15 14:53:40.985 [INFO ] [runtime.busevents             ] - Sw_Bureau_B received command ON
2016-10-15 14:53:40.990 [DEBUG] [.mqtt.internal.MqttItemBinding] - Publishing command ON to home/verdiep/Switch/Sw_Bureau_B
2016-10-15 14:53:41.000 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 12 to topic 'home/verdiep/Switch/Sw_Bureau_B'
2016-10-15 14:53:41.019 [INFO ] [runtime.busevents             ] - Sw_Bureau_B state updated to ON
2016-10-15 14:53:41.290 [INFO ] [nhab.model.script.Bureau light] - Will switch off bureau light common within 10 seconds
2016-10-15 14:53:41.311 [INFO ] [nhab.model.script.Bureau light] - Will switch off bureau light common within 10 seconds
2016-10-15 14:53:51.322 [INFO ] [runtime.busevents             ] - Sw_Bureau_B received command OFF
2016-10-15 14:53:51.325 [DEBUG] [.mqtt.internal.MqttItemBinding] - Publishing command OFF to home/verdiep/Switch/Sw_Bureau_B
2016-10-15 14:53:51.328 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 13 to topic 'home/verdiep/Switch/Sw_Bureau_B'
2016-10-15 14:53:51.342 [INFO ] [runtime.busevents             ] - Sw_Bureau_B state updated to OFF
2016-10-15 14:53:51.365 [INFO ] [runtime.busevents             ] - Sw_Bureau_B received command OFF
2016-10-15 14:53:51.367 [DEBUG] [.mqtt.internal.MqttItemBinding] - Publishing command OFF to home/verdiep/Switch/Sw_Bureau_B
2016-10-15 14:53:51.374 [DEBUG] [o.i.t.m.i.MqttBrokerConnection] - Publishing message 14 to topic 'home/verdiep/Switch/Sw_Bureau_B'
2016-10-15 14:53:51.380 [INFO ] [runtime.busevents             ] - Sw_Bureau_B state updated to OFF
2016-10-15 14:53:51.489 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Switch off bureau light common after 2 minutes': cannot invoke method public abstract boolean org.openhab.model.script.actions.Timer.cancel() on null
2016-10-15 14:53:51.546 [INFO ] [nhab.model.script.Bureau light] - Time elapsed, switched off light
2016-10-15 14:53:51.560 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Switch off bureau light common after 2 minutes': cannot invoke method public abstract boolean org.openhab.model.script.actions.Timer.cancel() on null

Any reason why those errors are triggered? AFAICS, I’ve put all the necessary timer guards I need. I don’t cancel a timer when it’s null. At least, that’s not my intention…

And I also see twice the message Will switch off bureau light common within 10 seconds. I can’t explain this neither…

As usual, thanks for your valuable feedback.

The switch turns on which creates a timer. The timer goes off and switches the switch off and then sets the timer to null. This off command retriggers the rule and your else clause runs.

Try setting the timer to null before the sendCommand in the body of your timer. This will keep ensure the timer is null before the rule triggers again when the switch turns off.

Cancelling the timer is not needed insured the timer.

The error is caused by a race condition. You send Command that triggers the rule. At this point both your timer and this new instance on the rule is running. So when the rule checks to see if the timer is null, it isn’t yet. However, after the rule checks to see if the timer is null, the body of the timer sets it to null. Then the rule tries to cancel the timer by now it is null and you get an error.

Once again, if you set the timer to null before you sebdCommand in the timer body this problem should go away. I’m not sure about the double log message. The rule is being triggered twice for some on event.

Hi Rich

You are absolutely right. Before I got your anser, I tried a “messy” solution by adding a Thread::sleep(50) just before the if (timer!=null) check in the else block and that seemed to work too. But obviously, your solution is much more elegant.

About the double log: I only get this when I use the received update in the rule. When I use the update in the rule, I don’t get the double log message.

But all is clear now to me. Thanks very much for your assistance!