Add MQTT Publish & Subscribe as an option in Rule Machine

I would really like MQTT Subscribe and publish in Rule Machine'?

To trigger a Rule and

To push a value via MQTT.

I realise there is probably a convoluted way of doing some this with say a virtual dimmer with a custom driver doing the publish, using a custom action in a rule.

I would love to be able to trigger rules from one of my MQTT subscribed devices.

Here's an idea using a Tiny Web Server to convert an HTTP GET call to an MQTT publication

            if (Request.Action.ToLower().StartsWith("mqtt"))
            {
                //e.g http://localhost:82/mqtt.svs?topic=test&value=123

                if ((Request.ArgArray[0] != "topic") || (Request.ArgArray[2] != "value"))
                    throw new Exception("Invalid paramter");

                string topic = Request.ArgArray[1];
                string value = Request.ArgArray[3];

                /// create client instance
                MqttClient client = new MqttClient("localhost");

                string clientId = Guid.NewGuid().ToString();
                client.Connect(clientId);
                
                // publish a message on "/home/temperature" topic with QoS 2
                ushort resp = client.Publish(topic, Encoding.UTF8.GetBytes(value), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);

                string sjson = JsonConvert.SerializeObject(client);
                
                //Send back last value usefull
                HTTPserver.Response(sjson);

                //client.Disconnect();

            }
2 Likes

Handling publish is much easier than subscribe from within RM.

As you mentioned .. 'custom action' ....My MQTT app exposes a 'publish' command via RM . Provide it a topic and payload as arguments.

It does also offer 'subscribe' but that is much harder to manage within RM as you have a message object or several separate parts of information returned like the payload which may or may not be easy to parse (e.g. json payloads) and the topic that the subscribe is coming from, of which there will likely be several.

RM is not strong on handling string values (MQTT payloads and topics) and so you will not find it easy within RM to recover the exact data you would want following a subscribe.

TBH I don't think RM will even get these values back after a subscribe as it requires an event with topic and payload data back into RM as a trigger.

Instead you could arrange for a topic to be automatically monitored for payload changes via a device attribute and then trigger on change of that attribute back into RM to kick off an RM rule. The topic will then be known. The MQTT app will handle complex json payloads too extracting specific values.

Putting the last bit a different way - create a virtual MQTT device and set the attribute topic to match the topic name on MQTT and the attribute value will remain synched to that MQTT payload for use within RM

You can also update a RM variable directly from the MQTT app (with the content of a payload)

Just thinking about subscribe if you had an RM feature that linked a predefined RM variable to the payload of a specific predefined topic (so you didn't need to parse the topic later) I can see that being quite tidy from within RM. Trigger on variable change.

Thanks for the response

I suspected it wouldn't fit in with your architecture well actually, its always a mistake in my experience to try to put too many colours in the box.

So a driver is still the best option then? My MQTT Arduino devices seem to communicate reliably with HE using that method.

If I wanted to publish a string via MQTT/Driver what method on the Driver would work best you think?

P.S. I'm enjoying tinkering with your product, it's controlling my HVAC and ventilation like a bought one.

BTW the whole hub seems to be turbo since the last update, much faster thanks

Love your work

Ok so I have wondered about setting reading RM global vars in a driver, it that what you mean?

It's interesting you mention HE and strings because I have been making tiles for some of my devices

which I make in the driver as HTML

    state.ExhaustStaleTemp = (float)( state.StaleTemp - state.ExhaustTemp).round(2) 
    state.ExhaustStaleHumidity = (float)( state.StaleHumidity - state.ExhaustHumidity).round(2)      
    sendEvent(name: "ExhaustStaleTemp", value: state.ExhaustStaleTemp)
    sendEvent(name: "ExhaustStaleHumidity", value: state.ExhaustStaleHumidity)
    sendEvent(name: "ExhaustStaleTile", value: "Exhaust - Stale Air<br>$state.ExhaustStaleTemp °C<BR>$state.ExhaustStaleHumidity %")

Earlier in the year this approach caused HE to slowly die, now it works well.

Have you considered Allowing JavaScript?extensions to capability,on the hub CPU or would the overhead be too great for the CPU?
Could do it in tiles running in the Browser on the host?

Nice for funky dashboards?

Something has to handle MQTT for connection state, publishes and parsing payload updates, Especially important if you have wildcard subscriptions. That's not something RM should be doing so yes a driver is the place for this. Whether that's mine or another is a choice you make. Another could be lighter in things it needs to handle. Mine was not written as a standalone driver.

You can read and set them within RM

Oh yes but what about direct access to RM globals inside the driver's Groovy code?

My app handles the variables - the driver is for MQTT interactions

Thanks

What is "My MQTT app exposes a 'publish' command via RM" - can we use it, f.e.?

Yes I assume you can (I don't use RM) - you will see it in the device driver

image

It takes two strings as arguments, the topic and the payload. You will need to connect the broker too. One broker for the driver so if you're using the MQTT app it will go to the same broker which should always be connected - you can monitor the 'presenceSensor ' in MQTT to see this, on a dashboard for example.

image

You can also subscribe and unsubscribe but handling the data back from these needs to be done in the parse method of the driver, it is not useful in RM

1 Like

See following in log:
app:1532021-01-21 08:57:52.381 errorjava.lang.NullPointerException: Cannot get property 'displayName' on null object on line 2210 (updated)
app:1532021-01-21 08:57:52.294 infoMQTT: Resynch MQTT device states and HA discovery topics
dev:972021-01-21 08:57:52.283 infoMQTT> MQTT subscribing to: homie/u8prime/+/+/+/set
dev:972021-01-21 08:57:52.268 infoMQTT> MQTT subscribing to: homie/u8prime/+/+/set

What information can I provide to debug ?

P.S.
app:1532021-01-21 08:57:52.217 warnMQTT: Unknown property value ultravioletIndex
app:1532021-01-21 08:57:52.190 warnMQTT: Unknown property value pressure
app:1532021-01-21 08:57:52.129 warnMQTT: Unknown property value pressure

Based on being in the RM topic I assume you are doing a subscribe ... from RM ?

How are you handling the returned payloads from MQTT - have you modified the parse method in the device driver as mentioned above ? Please PM me with the modified code.

I will look at the error but it looks like you might additionally need some extra handling in the app too for the returned ultravioletindex and pressure values.

What device type in HE did you want to update these attributes in ? Bear in mind if it's a HE sensor or virtual sensor created directly in HE it can't be set from MQTT. If it is a virtual sensor created within the MQTT app it can

If a lot of these assumptions are wrong can you PM me or use the MQTT app topic with the details

Download the Hubitat app