MQTT Client Plugin

I did make a small change to L_SensorMqtt1.lua to re-subscribe to subscribed topics after reconnecting to the broker if the connection was lost for any reason. I was having a problem with my broker disconnecting me. I ditched the broker and built my own instead but I still think this is a useful change. 3 lines added after the “connectToMqtt()”


– Publish an MQTT message


function publishMessage(topic, payload)

    log_debug("Publish topic: " .. topic .. " message:" .. payload)

    -- If we aren't connected for some reason, then connect first
    if not connectedToBroker() then
            connectToMqtt()
            if connectedToBroker() then
                    Subscriptions.retrieve()
            end
    end

    -- Try to publish.  Mqtt standard is fire and forget on publishing.
    local ok, result = pcall(mqttClient.publish, mqttClient, topic, payload)
    if ( not ok ) then
            log_warn("Unable to publish, connection down.  Discarding message: " .. payload)
            setConnectionStatus()
    end

end

I also created a simple Reactor group that watches the connected status and initiates the equivalent of a sensor trip once per minute until the connection is restored. Since the sensor trips are published a reconnect will be attempted each time. This helps minimize any ‘outage’.

1 Like

I have the plugin running on openLuup without any error messages. I have created 4 virtual switches, each with the MQTT device as the parent device. Each switch subscribes to a different topic (all topics are individual tasmota switch statuses) on my MQTT broker, Mosquito. I can see in the log that there is a recognized payload: the payload for each switch is simply “ON” or “OFF”.

However, I cannot get the virtual switch (which represents the tasmota switch), to toggle on or off when a payload arrives.

The bit of example code included in the plugin readme, that represents a “lua formula”, has me completely baffled:

urn:upnp-org:serviceId:SwitchPower1,Status=payload.value and ((payload.value=="alarm") and "1" else "0")

I have tried:

urn:upnp-org:serviceId:SwitchPower1,Status=payload.value and ((payload.value=="ON") and "1" else "0")
urn:upnp-org:serviceId:SwitchPower1,Status= if (payload.value=="ON") then "1" else "0" end

or changing the variable type… then 1 else 0 end etc. Nothing seems to work, and I don’t understand the syntax of the formula in the context of what the readme says the formula is supposed to do: ie turn the virtual switch on or off.

Can anyone help here.

The fragment you have provided is not valid Lua, so I’m presuming that these are parameters to a function call?

This bit:

payload.value and ((payload.value=="alarm")

…is simply a conditional which is true if the value of the payload is “alarm”, but this will never be so since your payload value is “ON” or “OFF”.

Can we see a bit more of the code to fully understand what you need to do here?

AK

Hey AK,

Yes, these are parameters to a function call. However, it’s not clear what the function does, the function’s syntax, and any formatting that is required. The function is described in the readme as follows:

You can subscribe to a topic by creating a child device :

  1. Add a new device and choose a type (e.g. “D_BinaryLight1.xml” or “D_TemperatureSensor1.xml”).
  2. Reload LUUP engine.
  3. Change the attribut “id_parent” with the id of the MQTT plugin device.
  4. Reload LUUP engine and refresh your browser.
  5. You should see variables “mqttTarget” and “mqttTopic” in your newly created device.
  6. Set the topic you want to subcribe to and the target (format: service,variable=(formula in LUA)).
  7. Reload LUUP engine.

If the payload of the received message is in JSON, the plugin will try to decode it and put it in the variable “payload” in the context of the LUA formula.

Examples :

Topic  : Test/#
Target: urn:upnp-org:serviceId:SwitchPower1,Status=payload.value and ((payload.value=="alarm") and "1" or "0")

On a message from topic ‘Test/Something’ with payload ‘{“value”:“alarm”}’, the switch will be powered on.

Topic  : Test/+/Sensor
Target: urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=payload.temperature and (tonumber(payload.temperature) or "0")

On a message from topic ‘Test/Something/Sensor’ with payload "{"temperature “:“15.2”, “hygrometry”:“80”}”, the temperature will be set to 15,2.

The function seems to be stored in a table called subscriptions in the “L_SensorMqtt1.lua” file at around line 460:

 Subscriptions = {

	addToIndex_ = function( subscription, subIndex, subTopics )
		if ( #subTopics == 0 ) then
			if subIndex["*"] then
				table.insert( subIndex["*"], subscription )
			end
			return
		end
		local subTopic = table.remove( subTopics, 1 )
		if ( subTopic == "" ) then
			return
		end
		if ( subIndex[ subTopic ] == nil ) then
			subIndex[ subTopic ] = { ["*"] = {} }
		end
		if ( ( subTopic == "#" ) or ( #subTopics == 0 ) ) then
			table.insert( subIndex[ subTopic ]["*"], subscription )
		else
			Subscriptions.addToIndex_( subscription, subIndex[ subTopic ], subTopics )
		end
	end,

	retrieve = function()

		log_debug("************************************************ Subscriptions Settings ***************************************")

		mqttSubscriptions = {}
		mqttIndexTopics = {}
		local topics = {}
		local strError
		for deviceId, device in pairs( luup.devices ) do
			if ( device.device_num_parent == DEVICE_ID ) then
				local subscription = {
					deviceId = deviceId,
					topic    = getVariableOrInit( deviceId, SERVICE_ID, "mqttTopic", "" ),
					target   = getVariableOrInit( deviceId, SERVICE_ID, "mqttTarget", "" )
				}
				-- service,variable=formula
				-- eg for topic /test/, payload {"value":"alarm"}
				-- urn:upnp-org:serviceId:SwitchPower1,Status=payload.value and ((payload.value=="alarm") and "1" or "0")
				subscription.service, subscription.variable, subscription.formula = subscription.target:match( "^(.*),([^=]*)=?(.*)$" )
				if ( subscription.service == "" ) then
					subscription.service = nil
				end
				if ( subscription.variable == "" ) then
					subscription.variable = nil
				end
				if ( subscription.formula == "" ) then
					subscription.formula = nil
				end
				log_debug( "Device #" .. tostring( subscription.deviceId ) .. ", topic=" .. subscription.topic .. ", service=" .. tostring(subscription.service) .. ", variable=" .. tostring(subscription.variable) .. ", formula=(" .. tostring(subscription.formula) .. ")" )
				if ( subscription.formula ) then
					-- Try to prepare the LUA code of the formula
					subscription.luaFormula, strError = loadstring( "return " .. subscription.formula )
					if ( subscription.luaFormula == nil ) then
						log_error( "Error in target LUA formula: " .. tostring( strError ) )
					else
						-- Put the LUA code in a sandbox
						subscription.jail = { tonumber = tonumber, tostring = tostring }
						setfenv( subscription.luaFormula, subscription.jail )
					end
				end
				table.insert( mqttSubscriptions, subscription )
				if ( subscription.topic ~= "" ) then
					table.insert( topics, subscription.topic )
				end
				-- Add to index
				Subscriptions.addToIndex_( subscription, mqttIndexTopics, string_split( subscription.topic or "", "/" ) )
			end
		end
		subscribeMqttTopics( topics )
	end,

	getFromIndex_ = function( subIndex, subTopics )
		if ( #subTopics == 0 ) then
			return subIndex["*"] or {}
		end
		local subTopic = table.remove( subTopics, 1 )
		local subscriptions = {}
		if subIndex["#"] then
			table_append( subscriptions, subIndex["#"]["*"] )
		end
		if subIndex["+"] then
			table_append( subscriptions, Subscriptions.getFromIndex_( subIndex["+"], subTopics ) )
		end
		if subIndex[ subTopic ] then
			table_append( subscriptions, Subscriptions.getFromIndex_( subIndex[ subTopic ], subTopics ) )
		end
		return subscriptions
	end,

	get = function( topic )
		return Subscriptions.getFromIndex_( mqttIndexTopics, string_split( topic or "", "/" ) )
	end
}

What I cannot see is how to structure the function syntax to turn its host object (virtual switch) off and on. I suppose I could just create my own variable with the function, and then use that variable in a reactor sensor to do the on/off work with a set target action, but this seems clumsy. It’s a nice plugin, with even greater potential, but as is often the case with plugins, specifics are learned the hard way.

Any help is appreciated.

This is what I use to control a virtual switch. This is what is in the mqttTarget variable of the virtual switch

urn:upnp-org:serviceId:SwitchPower1,Status=payload.value

I have a Mqtt client app on my phone that will send a message of either {“value”:“1”} or {“value”:“0”} depending on the state of a button on the app. This is about the simplest use case.

To make use of the state of the virtual switch I can use a variable watch in a scene or a reactor group that does the same thing. Using a trigger in a scene will not work.

If the Mqtt message uses (for example) “ON” and “OFF” instead of “1” and “0”, the function in mqttTarget gets a little more complex.

urn:upnp-org:serviceId:SwitchPower1,Status=((payload.value=="ON") and "1") or ((payload.value=="OFF") and "0") or "0"

For the virtual switch the function must result in a “1” or a “0”. Note these are strings and not numbers. Numbers won’t work. I learned that the hard way.

You mentioned that your payload is either ON or OFF. Does that mean that your actual Mqtt message is {“value”:“ON”} or {“value”:“OFF”}? Or maybe just {“ON”} or {“OFF”}? Or maybe just “ON” or “OFF”?

The plugin examples all follow the first pattern, which is a representation of a JSON object. I have not tried the second or third patterns, though I may give it a try to see what happens.

I have a few other Mqtt fed virtual sensors or devices also and I can show working examples if you’re interested. A few of these required some scene lua also.

mqtt virtual switch (of course)
mqtt virtual temperature sensor
mqtt virtual humidity sensor
mqtt multi-string container
mqtt multiswitch

Hope this helps you out! --D

Hey, thanks, that definitely helps.

Here’s the log entry for the payload:

2019-11-12 12:37:34.710 luup.variable_set:: 227.urn:upnp-sensor-mqtt-se:serviceId:SensorMqtt1.mqttLastReceivedPayload was: ON now: ON #hooks:0

2019-11-12 12:37:34.710 luup_log:227: SensorMqtt: Receive topic: stat/power_main_vera/POWER payload:ON

When I look at the payload in MQTT.fx (an MQTT inspector), there are no field names associated with the payload value, so I believe that the value of “ON” or “OFF” is all that is being sent by Mosquitto.

I tried your revised conditional formula, but it does not seem to pick up on the payload as the “Status” variable remains unchanged at “0”.

Hi,

I am using the MQTT plugin for some time and there was absolutely no issues with the plugin. However I have faced one issue yesterday. I am not able to find out how to change it.

Situation in general:

I am using the “watchdog variables” from Vera plugin (monitoring the hard disk space, free, used, total) in order to sent data to broker and later on input it to Influx for Grafana purposes. So far so good.
However if there is no any changes in the values, nothing is sent towards broker. Normally I am OK with this, but it is crashing all the graphs in Grafana. No new input, no lines in graph. There is only 1 point on the graph and no second point to create the graph.

Is it possible to configure that periodically (for example - 1 time per some 12h) update will be sent, even if there is no update.

Thanks for any hints and solutions.

A simple general solution to this problem, which should work for any plugin with this issue, is to set up a scheduled scene which reads the variables in question, and modifies them (without changing their numerical value.)

However, this does depend on how the plugin determines that the value has not changed.

How to change the variable without changing its value? Since all device variables are, in fact, strings, then you can just add a blank space to the tail of the variable. Try this out manually, first, and see if it triggers an MQTT update.

So @Buxton, it appears the message from your Mqtt client is a simple string and not a string representation of a JSON object. That’s fine. I tested the following and it works with my virtual switch

urn:upnp-org:serviceId:SwitchPower1,Status=((payload=="ON") and "1") or ((payload=="OFF") and "0") or "0"

The way this works is; the first condition is evaluated and if the payload string is ON, the and “1” is evaluated resulting in “1”. Since the first and condition is satisfied the remaining or expressions are not evaluated and “1” is returned from the expression. If payload string is OFF the first and expression fails and the next and expression after the first or is evaluated. Since the payload is OFF, the and “0” is evaluated resulting in “0”. Since this and condition is satisfied the remaining or expression is not evaluated and “0” is returned from the expression. If payload is neither ON or OFF the final or expression results in “0” getting returned from the expression.

In reality, since the switch is either ON, or it isn’t, the logic can be greatly simplified as follows:

urn:upnp-org:serviceId:SwitchPower1,Status=payload=="ON" and "1" or "0"

Hope this helps! --D

Awesome, both expressions work. So the problem was that I was referencing a field named “.value” — that didn’t exist because the payload variable had only a simple text string.

And thanks for clarifying how the function evaluates the input side. I’m hoping that the plugin author can provide some guidance on do’s and dont’s as regards the syntax in the function call as I can foresee quite a large range of MQTT subscriptions that could be used with this plugin…

I appreciate the second look you took here.

Now if the publish side of the MQTT exchange could be contained in the child device, that would close the loop on using the virtual device for MQTT actions… though this can be done with a reactor sensor acting on the vswitch state.

As well, I hope to see QOS level 1 implemented in the underlying MQTT lua module. It seems like this could be done with simple callbacks and delays to ensure accurate message transfer.

Thanks again.

Hi akbooer,

Great idea, so simple… ::slight_smile:

Introducing “nil” solve this issue :slight_smile:

Grafana is ignoring “nil”, so all good.

Thanks!

1 Like

Hello,
I’ve just updated my Vera Plus to the latest firmware and now the plugin seems not to work anymore. Strange thing I’m not anymore able to see it in the list of installed apps and not in the apps we can install!
Does someone had the same problem ?
Thanks in advance

I reply myself to my post. Seems that the firmware upgrade has erased the dependencies added in /usr/lib/lua
Copied again the dependencies and rebooted the vera and the MQTT messages are back

I had the same issue and can confirm that the dependencies were missing after the FW upgrade. Reinstalled the files and the plugin is working again.

I’m glad I found this plug-in which is successfully running on a VeraEdge and meant to be used for integration with my Home Assistant Mosquitto broker. Using MQTT explorer I get the following result:
topic:Vera/Events/120

value:
{
“ServiceId”: “urn:upnp-org:serviceId:TemperatureSensor1”,
“CurrentTemperature”: 0.5,
“DeviceId”: 129,
“DeviceName”: “dT-FloorHeating”,
“Time”: 1584366108,
“RoomName”: “Central Heating”,
“OldCurrentTemperature”: 0.3,
“Variable”: “CurrentTemperature”,
“RoomId”: 9,
“DeviceType”: “urn:schemas-micasaverde-com:device:TemperatureSensor:1”
}

The thing I’m struckling with is how to get the variable CurrentTemperature displayed as an entity within Home Assistant. I have tried the following to no avail:

  • platform: mqtt
    name: “CurrentTemperature”
    state_topic: “Vera/Events/191”
    value_template: ‘{{ value_json.CurrentTemperature[0].value }}’

Any guidance would be highly appreciated.

I have a similar issue to what has been described here. When I start up Home Assistant broker, all values from VERA are initially UNKNOWN. Only after some time are values being reported. Should I assume that the mqtt client only reports by exception, i.e. at change of current value? If this is the case I can assume all is working fine, otherwise I have to dig deeper and identify why! Any feedback would be appreciated.

Yes and you configure which variables are watched for changes under the “Watchdog” panel.

Have you tried posting this to the Home Assistant forums? You have the Vera MQTT publishing working and I’m sure the Home Assistant guys will be able to help you configure the subscription and how to display the values in Home Assistant.

Also, the MQTT device variable mqttVeraIdentifier allows you to adjust the MQTT publishing in case that is of value. For example, mine is configured as Vera/(SerialNumber)/(ServiceName)/(DeviceId) because I have 3 Veras reporting into a shared broker that then uses Node-red to pump the data into InfluxDB for analysis using Grafana and I wanted convenient way route data by Vera unit. You can see the available variables here

1 Like

Thanks Bruce for attending to my issues, all solved now. As far as mqttVeraIdentifier issue is concerned, very interesting which needs some careful studying given my limited understanding of the plug in capability thus far. Will not hesitate to come back if anything crops up.
Just for clarity sake, are there two or just one version of this plug in, yours and Schattenman?

The link I sent is to @SchattenMann’s repo - he is the original author and he granted write access to me so I could contribute to the plugin.