I have battery-powered sensors from different manufacturers, some reporting battery levels in the range from 0.0 to 1.0, some from 0 to 100.
I now configured all of them as Number:Dimensionless with the result, that the display state for those with 0 to 100 now shows 10000% when the battery is full. What’s the best practice to deal with those items?
It depends on the channels
General advise: Take care of the unit within the channel. This is also true for the unit of the Item itself (part of metadata).
Can you tell us which binding you are using? Some bindings return a PercentType, some a DecimalType, and some return (properly) a Number:Dimensionless… the former probably should have an issue raised.
- id: XXXXXX_lock_battery_percent
channelTypeUID: mqtt:number
label: Battery
description: Value representing the current charge status in %
configuration:
unit: "%"
min: 0
stateTopic: nuki/XXXXXX/batteryChargeState
max: 100
It’s from the Homematic Binding (Homematic HmIP-STH). The channel by default is configured as Number:Dimensionless and provides values from 0-100. The display state is automatically 0%-100000%.
My Netatmo Weather station reports humidity as Number:Dimensionless in its channel configuration with values from 0.0-1.0 where the display state is correctly showing 0%-100%.
There are four places where the unit of the State of an Item is controlled. If in any of these places the unit is not specified the system default is assumed which, for Number:Dimensionless, is a simple ratio one. 76% == 0.76 one`
At the Channel which specifies the unit of the value published by the Channel. Be careful to read the docs for the binding. Most bindings I know that allow one to set the unit just tack the unit on to the value. They do no conversions. So if your binding reports values from 0.0 to 1.0 and you tack “%” on to that, your Item will never be over 1 %.
At the Item with unit metadata which specifies the unit the Item keeps as it’s state and what it stores in persistence. If this unit differs from 1, the value is converted to this unit before the Item’s state is updated. If no unit is supplied in the update, this unit is assumed. If no unit metadata is defined, the system default unit is assumed.
At the Item with the State Description pattern which controls how the Item’s state is displayed in most places. The State Description pattern need not match 1 or 2 and if it differs the value will be converted to this unit before display.
In a rule when you get the state of an Item as a QuantityType there is a toUnit() method that lets you convert what ever state the Item is carrying (as specified in 2) to what ever unit you want to use in your rule. For example, if the Item is carrying Watt Hours and you want Joules for some reason in your rules you’d use (in JS Scripting) items.MyEnergy.quantityState.toUnit('J'). I highly recommend always calling toUnit in your rules to make the code self document the units and to avoid the rule breaking if for some reason the units carried by the Item change in the future.
Given the above:
“some reporting battery levels in the range from 0.0 to 1.0”: the unit on the Channel should be one and the unit on the Item should be %
“some from 0 to 100”: the unit on the Channel should be % and the unit on the Item should be %
The Item lacks unit metadata (see 2 above) so the value actually carried by the Item and stored in persistence is one. You do have State Description pattern defining the unit % (see 3 above) so that one value gets converted to % for display. But your charts are still charting in unit one because that’s what gets saved. Define unit metadata on the Item to be % and the Item’s state and display will all be the same unit.
Note, in OH 4.1/4.2 a lot of work has been done such that when you create an Item through MainUI there is always going to be a unit metadata created for the Item and you get to set it before the Item is created. And the binding is allowed to suggest what the unit should be though you have the opportunity to override it.
There can be some additional confusion here caused by the fact that bindings can push a State Description Pattern. So you can have situations like “My Netatmo Weather station reports humidity as Number:Dimensionless in its channel configuration with values from 0.0-1.0 where the display state is correctly showing 0%-100%.”. The binding is reporting 76% which gets converted to 0.76 one as the state of the Item and what gets saved to persistence. But the binding pushed a state description along the lines of %.0f % so for display it gets shown as 76 %. But your charts will all be wrong because it’s one that gets saved.
One of the big breaking changes between OH 3.4 and 4.0 was that in OH 3.4 there was no unit metadata. The Item’s State Description pattern served double duty in both specifying how the Item’s state was displayed and in what unit states sent to the Item got converted to. OH 4 separated those two behaviors.
All the channels are created on the fly. This means that there is no thing.xml neither where channel types and respective units of measure would be pre-declared, nor where channel state description would be pre-declared.
The channels are (therefore) all simple Number values i.e. without any units of measure or state description.
From looking at the Java code I would say that it would be quite a difficult task to retrofit proper UoM to this binding in order to fix the issue at thing/channel source level … but perhaps @gerrieg can comment on that?
So the (IMHO only) workaround is indeed to try to fix the UI display at target item level via some of the techniques that others have mentioned here in this thread.
Thanks you so much for the extensive reply @rlkoshak. I ran into the UoM before and this helps wrapping my head around it.
One thing that might help others, is to keep in mind, that a change to the ‘unit’ attribute, only takes effect when the value is updated. If you have a sensor that only updates once in a while, it might take some time before the change shows up in the UI.
So for my case the right combination was Number:Dimensionless + unit = %.
Regarding the unit in my specific case. I did have the unit set to ‘%’ in the MQTT channel configuration. Why did I have to set it again in the item?
The unit you set on the Channel defines what unit the binding publishes. The unit on the Item defines what unit you want to use.
Let’s say you have a sensor that reports temperature over MQTT as °F but you want °C. You set °F on the MQTT Channel to indicate that the value published is °F. You set °C on the Item so the °F gets converted to °C before the Item is updated.
Now remember that the default unit for an Item is controlled by the system and the default unit for a Number:Dimensionless is one. So you set % on the Channel to indicate that the Channel is publishing a %. But if you don’t define unit on the Item it uses the system default, which is one. So if you just set the Channel your % value published by the binding will get converted to one before the Item updates.
To summarize:
Setting the unit on the Channel indicates what the binding publishes. It does not do any conversions. It’s an additional piece of information added to the value published by the Channel.
Setting the unit on the Item indicates what you want to use and store in persistence. Conversion does take place here where necessary. If unit is not set, the system default unit is used.
Setting the unit on the State Description indicates what you want to see in the UIs. Conversion takes place before display where necessary but the Item’s state doesn’t change from the unit defined in 2. It’s only what’s shown that undergoes conversion where necessary.
We can ignore rules for now.
Remember, it’s a small minority of bindings that let you set the unit at the Channel level. That’s usually hard coded because the binding usually knows what the unit it on it’s own and publishes the values with the unit. It’s only in those cases where the binding can’t know on it’s own what unit a value is in that you have the option to set the unit at the Channel.
Most of the time you’d only need to set the unit on the Item, and even then only when the system default unit isn’t what you want. Though with Number:Dimensionless, you almost never will want to use the system default of one so you’ll pretty much always set the unit for Number:Dimensionless Items.
It’s generally a good habit to just set the unit on all Number:X Items whether or not the system default is what you want. It helps document what the Item state will be and it completely avoids surprises.