MQTT Binding and Units of Measurement

I’m not sure if this is a bug in the binding, a bug in the core, or a misunderstanding of what the “Unit of Measurement” field of the Number Channel config does (which could be called a bug in the docs).

Here’s the scenario. I have an Arithings WavePlus. I haven’t yet figure out how to pass a Bluetooth device to my docker container so I can’t use the binding yet (though that’s my ultimate goal). In the mean time I found GitHub - Drolla/WavePlus_Bridge: Airthings Wave Plus Bridge to Wifi/LAN and MQTT which will connect to the WavePlus and publish the sensor readings over MQTT. It works great (after I fixed a minor bug).

The temperature is published in °C but I live in the backwards part of the world that uses °F. I simply don’t have the intuitive feel for what °C mean and am probably too old to learn at this point so I really want °F.

The description of the “Unit of Measurement” field states

Unit of measurement (optional). The unit is used for representing the value in the GUI as well as for converting incoming values (like from ‘°F’ to ‘°C’). Examples: “°C”, “°F”

That leads me to believe that the following should work:

  1. Since the value is published as °C, add “°C” as the unit to that field.
  2. Set the State Description Pattern for the Item linked to that Channel to “%0.f °F”

The °C on the binding tells OH what units the raw number represents. The °F tells OH that I want to see the value in °F. That’s kind of the point of UoM after all.

However, what happens is as follows:

  1. If I change the pattern to “%.0f °F” the current value of the Item is converted to °F as expected in MainUI. However, on the next update, the °C will be treated as if it were °F. For example, if the current temp is 20 °C, when I change the pattern I’ll see 68 °F as expected. But when the Item updates again I’ll see 20 °F.

  2. If it’s showing 20 °F erroneously and I remove the pattern metadata, the Item will actually change and report -6.66 °C until the next update.

  3. In both 1 and 2, based on events.log, the Item is actually changing state. The State Description is not just changing how the Item is shown to me, it’s actually changing the Item’s state. ???

What the heck is going on here?

Posting for ideas before I go and file issues all over the place hoping one sticks.

NOTE: all the rest of the units are working quite well, even Bq/m³ for the radon readings. Though I’m not trying to convert those to some other units.

As a work around for use in the mean time while exploring this behavior I’ve implemented the following:

  1. Remove the “°C” from the Channel config.
  2. Set the State Description Pattern to “%.0f °F”.
  3. Set a JS transformation on the Channel with the following:
(function(i) {
  var QuantityType = Java.type("org.openhab.core.library.types.QuantityType");
  return new QuantityType(i +" °C").toUnit("°F").floatValue();
})(input)

This has bugged me for a while, and has been logged a couple of times on GitHub 6751 and 10338, and those issues have links to related posts in this community. I logged a pull request to resolve it here. Unfortunately other underlying code has changed substantially since I logged the pull request. I made a brief unsuccessful attempt to update it a few weeks ago and haven’t had time to look at it subsequently, I will see if I can have another crack at it. I think the comments in the second issue explain the behaviour you are seeing (initial correct conversion for system UoM to item UoM, subsequent misapplication of UoM applying item UoM directly to value).

2 Likes

Amen. I would love to see this get some traction. Especially if the homie binding populates the UoM config on the channel automatically.

Thanks for the links and the attempts to fix it. However, I think there is something more going on here and it’s not clear that it’s happening in the binding.

After the Item receives an update with, in this case °C, if I change the State Description Pattern to °F something actually changes the state of the Item to store °F. What ever that something is has to have a part to play in all of this.

There are ifs and buts. I had a poke into this, I think in connection with the github issues already linked.

Let’s say you have your Item already populated with 10°C, pattern “°C”

Now post just a number to the Item, “20” - which I think is what MQTT binding currently does, and does not use channel units= parameter here.
But this a Quantity Item, we must have a unit. Let’s look for default.
We’ll use that pattern “°C”
Item content now 20°C, hurrah.

You come along and change ‘pattern’ to °F
The item still holds 20°C, but the UI will show 68°F, because pattern applies auto-conversion for you at point of display. That’s its primary job.
So far, so good.

Then another MQTT update comes along, “20”, still just a number.
But this a Quantity Item, we must have a unit. Let’s look for default.
We’ll use that pattern, “°F” now.
Item content now 20°F, hurrah … oh wait …

The underlying failure is unitless updates via channel.
To make it work today you have to use a transform in channel or profile to add the correct unit to the reported number before it gets out as an Item update.

Really we’re sort of abusing the ‘pattern’ intended for display to use it as a default unit indicator as well, that’s not its job. But it doesn’t matter if we get units at source and don’t need to conjure up a default.

1 Like

But that’s not my system default. °F is my system default so the number should be treated as °F if nothing else overrides that. However, if I put °C into the Channel config, I get the value as °C despite my system default being °F.

And when I change the State Description Pattern to °F, I get the value as °F regardless what the Channel units setting is.

So it seems like the units are applied but overwridden by the State Description, not just for display but also for actually storing the state.

That’s the expected behavior but that’s not what I’m seeing. It’s not just the UI. The actual Item changes state to 68 °F independent of any messages from MQTT (I’m manually publishing the messages so I have more control). I can see it in the persistence data and most importantly I can see an ItemChangedEvent in events.log as soon as I hit save on changing the State Description Pattern. In a rule if I call getUnit() it gives me “°F”.

That’s the primary weird and unexpected part of this to me. What’s causing that ItemChangedEvent? It might be new behavior too as I know there has been some work on getting Persistence to work with UoM so perhaps this is new since the last time you looked into it. There are always changes in the UI too. I do know that the stateDescription is being used in unexpected places outside of the UI, which gives me a little bit of concern too.

Good. Individually set per item default should override system default.

That is different, and in my view incorrect.

Have a play with an Item not linked to MQTT to establish baseline behaviour.

Per Channel in this case. The unit setting on the Channel is overriding the system default which means that the units from the binding are somehow being taken into consideration since they override the system default.

The precedence seems to be: State Description → Channel config → System default.

For the Item’s actual state I would expect: Channel config → State Description → System default.

The setting closest to the source of the value should take precedence for what units the Item’s state is stored as.

I’m about to leave for the weekend but next week I’ll try to set up a comprehensive set of tests to explore all the edges of the behavior.

I disagree. You’ve got a device posting in C, but you want to deal with it in F. The decision is yours, not the devices.

But the decision is still yours. You can still use the State Description to see the value in the units that you want to. You can use toUnit in a rule to get the value in the units you want to deal with.

But if I have a value received by a binding. And the binding says that this value is °C, I shouldn’t be able to say “no, that 18 °C is really 18 °F”. That’s just flat out wrong. It’s actually 65 °F. But if the precedence for the units actually stored in the Item comes from the State Description we are stuck in the current situation where the UoM is pointless. I can’t actually use the State Description to convert that 18 °C to 65 °F for display. I have to do that by hand in a transform.

Take care, I’m not saying what units take precedence when displaying the Item. I’m talking about which units are used to store the value in the Item’s state. And this is exactly the way you described how it works.

The only way for this conversion to occur is if the °C defined in the Channel/by the binding takes precedence over the State Description Pattern for how the state is stored in the Item. If the State Description takes precedence, the units change but the number does not and we end up with 20 °F instead of 68 °F.

I quite agree.
I haven’t seen any demo of that happening yet.
I’m working on the assumption that the unit=C in the MQTT channel is still dysfunctional for inbound traffic.
So you receive “18” over MQTT, the channel posts “18”.
Must have a unit, the default of the day is used.
You won’t see that in the logs because the unit is applied before the Item state event is created.

A test might be to also link your MQTT channel to a String and/or plain Number type Item, where the framework will not insist on a unit. What’s really coming out of the channel?
You could even use a JS transform profile on your real channel-Item link, to log out the update and pass it along otherwise unmolested.

I faced similar issues with data coming from a tire pressure sensor via MQTT in units of kPa, while the systems assumes hPa as default. In Germany I like to see tire pressure in bar. I tested several combinations and had only sometimes desired results by accident.

I am not able to follow all your aspects in this thread, because it seems to be quite complicated. Nevertheless it looks similar to my issue. Especially the faulty unit value after an item update fits with my experience.

I’ll try to recreate some scenarios the next days, weeks…

I am using OH 3 stable and latest on a separate machine.

Regards Christoph

Just to circle back around on this, for other readers. I posted a somewhat comprehensive set of tests I’ve done with temperature, but it should be the same for any units.

[mqtt] Faulty display of an linked item using Units of Measurement. · Issue #10338 · openhab/openhab-addons · GitHub (scroll down to the end).

Based on those tests:

That is what is happening when I change the State Description pattern on DegreesC to “%.0f °F” it’s doing just that. The Channel is configured as °C, the state description is °F and the °C is thrown away.

Except the behavior of DegreesC doesn’t bear that out. My system default is °F. When units are not provided either in the Channel config nor in a State Description the Item does in fact get updated with °F as the units.

However, if I set the unit to °C on the Channel and do not set any units in the State Description, the units used are °C. So it can’t be the case that the MQTT binding isn’t passing the units along at all. The only place that °C is specified in my entire system is in the MQTT Channel. I shouldn’t be able to get °C at all in that first test if the default were used in all cases.

I’ll need to devise a test like you suggest to log out the what the Channel is actually outputting.

Okeydoke, that is new binding behaviour and is indeed the desired behaviour.