UoM default units and consequences

If the binding has no knowledge of the unit of the value provided by the device, it should use a simple Number for the channel. So a raw value without any know unit.

The problem arises if I want to push the raw value to a uom item and unit of the raw value is different than the unit of the item.
Then it’s necessary to set a unit to the raw value and currently that’s not possible, hence my suggestion with the profile.

As you explained for some binding/devices it’s not possible because the unit might be different per user/device (e.g. mqtt).

That’s why I can (kinda) configure the unit directly in the binding with “incomingTransformation”. And this would be more architectural better “to configure it at the root” - here directly in the channel, if every binding offered some kind of “incoming unit” configuration for their channels.
Another way was to have some kind of “proxy” in between. But that’s definitely not “OH3.4 Release” related anymore. You could use e.g. Node-Red to capture the input, and either let it add the Unit or calculate it yourself.

@lolodomo is correct. Using "Item Label [%0.1f °C]" in a .items file is nearly identical to nesting the data under stateDescription metadata (though metadata is more flexible and able to provide min, max, and step). the GenericItemProvider is also a StateDescriptionFragmentProvider, and parses the label looking for patterns, returning them as StateDescriptionFragments: openhab-core/GenericItemProvider.java at 41ba3ff854797f98293c12ee0f388c1972c75bd1 · openhab/openhab-core · GitHub

1 Like

Note that with MQTT and Homie devices, you can directly configure what unit the device is sending on the channel, and it does not have to match the unit on the linked item:

If the item’s unit does not match the MQTT’s channel, it will be converted to the item’s unit as appropriate before being stored.

I’m not sure why this isn’t exposed for Generic MQTT Things. I sense a PR in my future… :wink:

2 Likes

oh. That’s 3.4 I guess? That’s great! Didn’t have the time to upgrade yet.

I’m in a similar situation and I haven’t fully explored it yet how things will work now. But as I see it the options are:

  • Add a transform profile to append °F to the state before it gets to the Item
  • Add a transform profile to convert it to °C before it gets to the Item
  • Don’t use units for this Item at all and have it in a plain old Number instead of a Number:Temperature
  • Of course, using a Rule and a proxy Item is always an option.

The Zwave binding is usually really good at adding units to the values. Are you sure the binding is just sending a plain number and not a number with units? It might be something that can be addressed in the Zwave database to apply the proper unit or expose the config parameter so it reports °C.

This didn’t used to work as expected. There was a really long forum thread and an issue opened (I’m not sure what ever happened to the issue though). This was all before these default units and other changes were made. I have a couple of devices that report °C over MQTT but my default is °F. I set the unit in the MQTT Channel config to °C and the state description to °F and I would get the °C value with °F units.

I need to test this again but a JS transformation to convert to °F first was required to make it work right.

It’s there under “show advanced”.

It’s been around since the 2.x days I think. I don’t remember exactly when it was added but it’s been a long time.

don’t want to hijack this thread (again), but my pure-MQTT channel in 3.3 doesn’t show units for configuration. Perhaps it’s “homie-device-only”.

I’m pretty sure I’ve been using units for longer than the last six months but could be wrong. I only use Generic MQTT Things.

ah. Guess I was blind. found it.

PS: Thanks @mstormi for moving this into a new thread.

Indeed, I in fact had the menu open to do this myself and got distracted.

I’d think it’s time for a wrap up first.
Now with defaults to apply for every UoM item, taking the unit from the display pattern should no longer be needed or relevant.
If the new defaults apply first (then there always is a unit so the pattern will be ignored), I believe we should be fine and won’t need any fix in OH4.
Only if there’s situations where a unit isn’t set yet (such as after persistence restoreOnStartup) AND the pattern applies BEFORE new defaults apply, this might result in a problem and it would be a good idea to now remove that old behavior. I wouldn’t know of any modification that could apply before the new default does, but I don’t know the code well enough to verify or falsify my assumption.
Maybe @rossko57 you could setup a small test bed with 3.4 and try to figure that out ?

Also as a reminder to complicate any analysis even further, @J-N-K (who implemented this) mentioned there had been UoM defaults for some types/dimensions before this 3.4 change, temperature I believe being one of them. So probably not the best idea to try and test with right that.

I’m not sure that covers all the use cases and I fear getting into a situation where something is messed.

What’s the unit of the Item under these circumstances?

  1. no unit provided
  2. unit provided, same as system default
  3. unit provided, different from system default
Unit Provided No SD SD Same SD Different
None Default NA NA
Provided Default? with or without conversion? Provided SD? with or without conversion?

It’s that bottom right cell that causes problems. When the units provided from the binding is different from the units in the State Description. In the past, it was completely wrong (IMO) and it would apply the SD units without conversion, so my 22 °C becomes 22 °F.

I think it’s clear that it should do the conversion to SD at some point, but where? Before assigning the state to the Item? At display time only?

And of course persistence is a wholly different kettle of fish but there’s already an issue open to address that so let’s ignore that for the moment.

I do like the profile idea, at least as a stopgap until a particular binding can be fixed to either send the correct units, or be configured for flexible data to send the correct units.

Shameful plug here that technically JSR223 languages can be used to write profiles in 3.4 without having to wait for a new profile to be added to core. Using my own (as-of-yet-unreleased) version of the helper libraries with JRuby (see documentation at https://ccutrer.github.io/openhab-jrubyscripting/main/OpenHAB/DSL.html#profile-class_method), this could be implemented as:

profile :set_uom do |event, callback:, state: nil|
  next true unless event == :state_from_handler # only handle state updates from handlers
  next true unless state.is_a?(DecimalType) # if it sent a QuantityType, process as normal. Could also possibly process this to discard the unit, and re-interpret -- not convert -- as a different unit.

  callback.send_update(state | "°F") # forward the update, but as a QuantityType with unit °F
  false # let it know we already processed the event, and it doesn't need to be handled again
end
Number:Temperature MyTempWithNonUnitValueFromBindig "I prefer Celsius [%d °C]" { channel="something"[profile="ruby:set_uom"] }

It’s been working fantastically for me for at least a few months. It was definitely buggy in 3.2, and probably 3.3 as well. The whole MQTT-to-openHAB-then-exposed-via-HomeKit should work quite well with UoM in 3.4 now though.

:person_facepalming: there it is. Beautiful.

Simple - the Item state would assume the default unit for this Item.
(and so no conversion or scaling is ever done)

Follow-on - what is the default for this Item?
If present, derived from this Items ‘pattern’ currently.
If not present, use system-wide default for this type.
(This system default may be locale dependent e.g. C/F)
Prior to 3.4 - no system default for some types - indeterminate results.

Same case really (assuming valid units)
The updating value is auto-converted to this Items default unit before applying to Item state.
If the units are the same. its a “null” no-op conversion obviously.

See above for “what is this Items default unit”.

I think that’s all fine and needs no functional change.
3.4 has tidied away the one indeterminate case where there might be no default. Hurrah.

I am not at all convinced about this. The implication is that every Item state could only ever be expressed in that type’s default unit.

Let’s say the default for Number:Length is “m”.
My particle physics binding has a channel reporting in nanometres, “nm”
A value 5nm ends up in the Item as 0.000000005m
That’s technically correct, but looks rubbish.
That’s okay you say, use the ‘pattern’ to display in m!
My logs and rules are still full of the rubbish.
My chart scales look crap.
My rrd4j persistence is having increasing rounding errors dealing with those tiny numbers rather than 4 or 5

What I would like is to be able to specify a default unit on a per-Item basis.
This is already possible, hurrah! (and the priority about which to use is already established)

But
that per-Item default unit is derived from a purely display ‘pattern’. We’re breaking good design practice by making a presentation feature serve extra duty as a data design feature.
Does that matter?
Well, I cannot change my temperature display from C to F without trashing years of historic data recorded using that ‘pattern’ defined default.

What I’d like to see is a dedicated means to assign a default unit to individual Items, instead of deriving it fron presentation 'pattern.
With the metadata that was not available in OH2, this should not be too difficult now.

This can be implemented in a non-breaking way, in the algorithm that derives unit
first, use “new” definition
if none, use ‘pattern’ derived
if none, use system type default
At some later date (OH5) you could remove the legacy ‘pattern’ lookup.

The implication here is that you set the Item state unit in one place, which will govern its behaviour in logs/rules/persistence, anywhere that raw state is used - including binding channels.
That need not be the same as the UI presentation unit.
And neither are constrained to be the system default unit.

This would take care of people who may have extensive historic records in some non-system-default unit, because the Item’s default unit is what is used when retrieving.

3 Likes

Having a pattern for presentation separate from the item’s unit would also be useful for something have a large range of values - like power and energy (mW, W, kW; mWh, Wh, kWh, and up to MWh). I plan on writing a script transform at some point so that the display for such values auto-ranges to an appropriate unit. But I can’t do that if I need to use the pattern to convince openHAB of the proper unit I want values stored in.

2 Likes

I’m not sure the defaults are documented anywhere. It should be apparent though once you start using them.

There’s another untidy case that has been done away with too. Number Items will strip off the units if one is passed to them. So if it’s a plain number, it won’t magically start carrying units because some binding decided to publish units or the like.


dancing with the stars celebrity GIF

Because we know it’s going to happen. What if I want to use it in my system as one unit but display it in a different unit? I can’t imagine why someone would want that but I’m 100% sure someone will be mad because they can’t. They may just have accept their use case can’t be handled.

Or, since we are talking OH 4 and breaking changes are OK, drop the State Description pattern from the chain entirely and let the break happen. I think that would be cleaner overall. Why wait for OH 5?

There might be something else we can do to help with this, such as scripts to normalize the historic data and the like.

Note: I got a chance to set it and it does appear to work as expected when using the unit in the MQTT Channel config. I set °C as the unit and it converts it to °F on the Item which has %.0f °F for the State Description pattern. Woohoo! This is the only transformation I have in my system. :smiley: Unlike the rest of the world, I try to eliminate transformations and profiles instead of rules. :rofl:

1 Like

Not at all.
You don’t ever change the Number itself. You only change the representation for display,

I’m not getting why you would need this.
Note the default unit only applies once, initially, when you have no unit yet and assign a value but no unit through either a binding or a rule. Subsequent assignment without unit will reuse the item’s existing unit, NOT the default again.

Well, it also happens on persistence restore, but that’s a little bit different discussion.
For that I’ve agreed with @J-N-K to not put it into 3.4 but discuss in a separate issue first and introduce in OH4 as it comes with a breaking change.
I’m there asking to have a base unit for every type and every value would need to be converted to that base before storing it to persistence. That base unit is usually defined by physics. For your nm example the base would be m so the persistence value stored is 0.0000005.
For temperatures we would need to define it as either C or K I think.

Yes here there will be a one time breaker when you have been using and persisting a value as a non-base unit version such as nm or °F and now introduce a base m or K. That jump in scale cannot be avoided but that ONLY happens for persistence of non-base units and is no valid reason not to take this step forward.
You can convert your old data if you are so really keen on keeping and displaying it in one chart together with your new data.

But in all other cases, in rules you can convert as needed (.toUnit()) and afaik in charts, too, you can set the scale to whatever you want to see the chart in so it looks fine.

I think that a “UNIT” profile is very easy, it should

  • from handler to item: add the defined unit to the DecimalType, making it a QuantityType
  • from item to handler: convert the value (QuantityType to the defined unit and strip it (DecimalType)
1 Like

Heh. :slight_smile:

I try to eliminate transformations for core data paths, but am okay with them for display purposes (i.e. I use a string item to represent “A/V Source”, with value like “1.3” meaning “coming from room 1, input 3”. the transformation will translate into a human-readable value of “Living Room - SHIELD”).

I try to eliminate proxy items if they can be handled by some other native functionality, like proper UoM handling or profiles.

I tried to eliminate a profile usage (okay, I hadn’t even though of using profiles until later) to “veto” commands by using rules, but my PR got shot down ;).

1 Like