MQTT - Send command till confirmation received

I have my OpenHAB set up mostly using ESP8266 nodes dotted around.

Recently I have added a Sonoff AC relay module in the garden shed which turns the garden up lighters ON and OFF.
I am not sure if this is a little too far away from my home router, but sometimes it doesnt listen to commands (especially OFF commands, after being turned on for a while), I will have to work out if this is a issue with my WiFi signal or another issue.

My question is, when OpenHAB sends a OFF command to Topic /Garden/relay/1/com/, rather than OpenHAB just assuming that the message was picked up by the Sonoff, OpenHAB waits till it receives a confirmation command of OFF to topic /Garden/relay/1/state/ before it turns the GUI switch off.

Make sense? Is it possible? I have googled it but not found any info.

Perhaps this is what i am looking for

autoupdate=“false”

Researching how to use it now.

You will probably want to use a Proxy Item and separate Items for commanding and receiving the update. I’ll call them Proxy, Out, and In.

  • Proxy: you use this in your Rules and on your sitemap. As far as anything else is concerned, Proxy represents this Device
  • Out: bound to /Garden/relay/1/com/ (NOTE: the initial ‘/’ is considered deprecated and brokers like Mosquitto may stop supporting topic names that start with ‘/’ that at some point). This Item is responsible for sending the commands to the device
  • In: bound to /Garden/relay/1/state/. This Item is responsible for receiving the new state from the device

Now you need some rules to make these three Items work together. You need:

  • A rule that triggers when Proxy receives a command that forwards the command to Out and sets a Timer. The Timer sets the Proxy back to the previous state if it goes off.

  • A rule that triggers when In receives a command that cancels the Timer and sets the Proxy to the current state of the device.

val Timer proxyTimer = null
rule "Proxy received command"
when
    Item Proxy received command
then
    if(proxyTimer == NULL || proxyTimer.hasTerminated) {
        proxyTimer = createTimer(now.plusSeconds(5), [|
            Proxy.postUpdate(if(receivedCommand == ON) OFF else ON)
        ]
    }
    else {
        proxyTimer.reschedule(now.plusSeconds(5)
    }
    Out.sendCommand(receivedCommand)
end

rule "In received update"
when
    Item In received update
then
    if(proxyTimer != null) {
        proxyTimer.cancel
    }
    proxyTimer = null
    Proxy.postUpdate(In.state)
end

Behavior is on your sitemap the Switch will bounce back to its previous state if the device didn’t receive the command and change.

An alternative that doesn’t use a Timer would be:

  • A rule that triggers on Proxy received command, resets Proxy back to its old state and forwards the command to Out

  • A rule that triggers when In receives a command that sets Proxy to the current state of the device.

val Timer proxyTimer = null
rule "Proxy received command"
when
    Item Proxy received command
then
    Proxy.postUpdate(if(receivedCommand == ON) OFF else ON)
    Out.sendCommand(receivedCommand)
end

rule "In received update"
when
    Item In received update
then
    Proxy.postUpdate(In.state)
end

autoupdate=“false” will, if both the incoming and outgoing MQTT binding configs are on the same Item, cause the incoming messages to only postUpdate the Item instead of sendCommand (which would result in an infinite loop). But it would not solve your problem.

1 Like

Thank you for your detailed reply, I will study it and try to work out how it works.

After posting the questions I have worked out a way to do it which seems to work.

Item (added , autoupdate=“false”)

Switch Garden_Lights “Garden Lights” (OS_Garden, OutLights) {mqtt=">[broker:/Garden/relay/1/com/:command:on:1],>[broker:/Garden/relay/1/com/:command:off:0],<[broker:/Garden/relay/1/state/:state:default]", autoupdate=“false”}

Rule

import org.openhab.model.script.actions.Timer
var Timer timer
var int counter = 0
    
rule "Turn off lights if failed"
when
    Item Garden_Lights received command OFF
then

    Thread::sleep(2000)

    if(Garden_Lights.state==ON) {
        logInfo("GardenLight", "Commanded OFF, but still ON. Retry OFF in 10 Secs")
     
        timer = createTimer(now.plusSeconds(10)) [|
            counter = counter + 1
            logInfo("GardenLight", "Attempting Turn OFF Garden_Lights, attempt " + counter)
            sendCommand(Garden_Lights, OFF)
        ]
            
        if (counter==20){
            logInfo("GardenLight", "Failed to turn off after " + counter " attempts")
            timer.cancel
            timer = null
            counter = 0

        }
            
    } else {
        if(timer!=null) {
            timer.cancel
            logInfo("GardenLight", "Lights have been successfully turned off, Timer Cancel")
            sendNotification("Email@gmail.com", "Garden Lights OFF " + counter)

            timer = null
            counter = 0
        }
    }
end

This does seem to work when testing on a spare node on the workbench, although it doesnt fix my issue with the garden light, there must be something fishing about the arduino code running on it which i will have to debug.