MQTT Connections

Do you need to declare state in any way? I just start adding values to it.

if (state.connected==true){
    interfaces.mqtt.publish(topic, payload, qos, retained)
}
else {
    log ("Dropping message - no MQTT connection","WARN")
    return
}

Have you tried commenting out the two debug lines containing the state map ${state} or removing ${state} ?

Just when state updates is a bit unknow .. based on this statement.. (and atomicState is not useable in drivers), maybe you're reading it back too quickly later ?

In a continually looping driver the driver never completes execution persay.. but I think it means method completion or specifically initialize() has completed, and the driver is just waiting on events.

I just commented out all state logging, except I changed refresh() to log only state.connected. I set state.connected as before, and added runIn (15, "refresh") to the end of mqttClientStatus(String).

Same result.

Just trying something here.. you might be right in that my state.connected is actually being updated from elsewhere and not taking it's value from within mqttClientStatus().

So.. yes seems like a namespace issue. You found a hidden bug in my code....

I'll flag @chuck.schwer in case it's a bug.

You can work around it with something like this.
Put this method call within the mqttClientStatus () callback

tryingThis ( message )  
tryingThis ( "true" )       //   using a string not boolean to match method def

and include this method

void tryingThis (String message) {
    state.mqttMsg = message
    return
}

This also makes me think how does a driver that connects to multiple brokers report status that I can identify back to which connection ?

I suppose people would say a driver should be for one device only - I'll have to rework my code as and when I want to support multiple brokers.

I tried this, but as suspected it did not work. I added the following functions and called them from the callback:

def setConnectedState(boolean newState) {
    state.connected = newState
}

def setLastMQTTClientStatus(String message) {
    state.lastMQTTStatus = message
}

Thinking this is a threading issue, I tried to see if I can get the data out through a runIn. That did work. Values appeared correctly when logged from refresh:

    runIn(0, "setConnectedState", [data: ["value": true]])

and

def setConnectedState(Map data) {
    state.connected = data["value"]
}

Not sure if this is a bug in the MQTT library with the callback, a documentation opportunity, or my newbie Groovy skills shinning through.

In case what's a bug? state can be updated from from mqttClientStatus(), here is code to test with:

metadata {
    definition (name: "Test Mqtt", namespace: "test", author: "Test") {
        command "printState"
        command "openConnection"
    }

    preferences {
        // put configuration here
    }

}

// Parse incoming device messages to generate events
void parse(String description) {}

void printState() {
    log.debug state
}

void uninstalled() {
    log.info "disconnecting from mqtt"
    interfaces.mqtt.disconnect()
}

void openConnection() {
    try {
        //open connection, use a unique client id
        interfaces.mqtt.connect("tcp://test.mosquitto.org:1883", "aij190fajpoa", null, null)
        //give it a chance to start
        pauseExecution(1000)
        log.info "connection established"
        interfaces.mqtt.subscribe("/test/hubitat")
    } catch(e) {
        log.debug "initialize error: ${e.message}"
    }
}

void mqttClientStatus(String message) {
	log.info "Received status message ${message}"
    state.lastMessage = message
}

Create a device with that driver then click "Open Connection" followed by "Print State"

I'm unsure why you are trying to store the state of the connection? Can't you just ask the mqtt interface if it is connected? https://docs.hubitat.com/index.php?title=MQTT_Interface#isConnected

Took your driver code, changed the MQTT clientId, and it worked. Very strange, I'm not seeing how yours is different. I'm going to try and figure out the difference, so that I don't stumble into this again accidentally. Thanks for the working sample.

I am familiar with that. I started out keeping another status for failed/disconnected/connected/retrying, plus a retryCount to back off reconnect attempts. When I started running into trouble I began boiling it down to the minimum that didn't work, which ended up being my own connected state, and the mqttClientStatus message.

So I spent a little time, started with your working example, and step by step made changes towards my code until it broke.

What breaks it is opening the connection from inside the configure function.

If I add

        capability "Configuration"

and then

void configure() {
    log.debug "Configure"
    state.lastMessage = "<<reset in configure>>"
    openConnection()
}

to your working example, it behaves like mine when I click configure. After that I can click Open Connection and it logs properly.

If instead I call runIn(1, "openConnection") inside configure, it goes back to working as I expect.

Is there something special about configure causing this? A reason this is happening that I don't understand, that I should watch for with other calls?

Thanks!

@mcarland

Hi again, did you ever resolve this any further ? I am now experiencing major inconsistencies with state.variable values in my MQTT driver. These ones are unrelated to MQTT connection status. Just random state variables that return different values in different methods - not just that callback one.

I'm going to look over that last post of yours again and see if that helps me but it does appear there might be multiple namespaces for state variables.

I'm also wondering a little about driver concurrency if somehow multiple driver instances are running and interacting in the same state namespace

I started with the code @chuck.schwer posted, and that worked. I step by step changed it into my code until it stopped working, and thought I had it figured out. When I changed the state inside configure() when I used the Configuration capability, it wasn't working, as I said above.

I believe later I ran into more trouble. My current driver, which seems to have been working since then, sets state values wherever it likes, but it doesn't directly call the MQTT interface from configure(), and doesn't call MQTT from inside an MQTT callback.

So my configure() sets some state, sends some events, and ends with runIn(1, "openConnection"), which is my method to open a connection. Apparently I had trouble setting state and calling MQTT, because openConnection looks like

void openConnection() {
    state.connecting = true
    runIn(0, connectToMQTT)
}

I am able to set state inside mqttClientStatus(String message). But when I get a callback for a successful connection, I am calling runIn(1, subscribe) which does the subscriptions. If I subscribed directly there, I lost the state I set.

So I'm not sure exactly what the issue is, but if I don't change state and then call another routine (possibly only MQTT based, maybe not) I stopped having problems. Maybe the way state is passed gets clobbered with multiple calls on the same stack? Dunno.

If you want my entire driver code, let me know. Good luck!

OK - I think something isn't quite right here and I'm wary as to whether I can rely on state variables in this driver. Sometimes they work in my code and sometimes they just don't which is not good. I get them working and then they just revert to failing. For example I have an incrementing state.sequence number and sometimes it just drops back to starting from 0 again or another value, and then might revert.

So I'm now actually using attribute values to store important things and then reading them back which does work reliably and has the added benefit of being available for subscriptions from my app. It's likely much slower though.

Not at all comfortable with this behaviour but I can work around it too