[UoM] Unit changes only on item state update

While working on implementing UoM in my binding, i noticed an odd behavior: If the unit is changed (by altering the Item label, e.g. [%.1f °C] to [%d °F], the old value and unit is returned when querying the Items state.

Example:

Number:Temperature Temperature_Today "Temperature [%.1f °C]" {channel="smhi:forecast:home:day_0#t"}
http://localhost:8080/rest/items/Temperature_Today/state -> 25.0 °C

Edit Item:

Number:Temperature Temperature_Today "Temperature [%.1f °F]" {channel="smhi:forecast:home:day_0#t"}

Still get

http://localhost:8080/rest/items/Temperature_Today/state -> 25.0 °C

It seems the unit is only associated with the State when the State updates, which means the binding have to update the channel again if someone changes unit.

When displaying the Item on a sitemap it works correctly however, both the value and the label are updated.
When querying the rest api the same value are returned unless the item gets updated. Same thing when querying the Item from the console (smarthome:status Temperature_Today)

Is this planned to change in OH3 (or maybe already is) or would it be too much of work?

Working as intended. The [state presentation] is primarily used to format the Item state for use in a UI display, and does not alter the state.

Bindings can specify units. If they do not get a unit from the remote device, they can assume a default. They can look to see if the Item has a [state presentation] - if it does, aha, that is the unit you want! - and use that.
(if there was not a [state presentation] it would use system default units)

So yes, binding can update Item with different units if you change [state presentation]
All seems to be working as intended.

It does seem to affect when the Item gets updated. If i update the Items state from the rest api I get different log entries depending on the unit.

Sending a PUT request to /rest/items/Temperature_Today/state with body 25:

If i have the state presentation as [%.1f °C], i see in the log:

15:06:02.768 [INFO ] [smarthome.event.ItemStateChangedEvent] - Temperature_Today changed from 24.9 °C to 25.0 °C

If I change to [%.1f °F] and sends the same request I get:

16:59:29.901 [INFO ] [smarthome.event.ItemStateChangedEvent] - Temperature_Today changed from 25.0 °C to 25.0 °F

I see the same thing happen when the binding updates the channel. So it does use the unit more than just for presentation.

I believe this is done in the setState mehod of NumberItem:

    public void setState(State state) {
        // DecimalType update for a NumberItem with dimension, convert to QuantityType:
        if (state instanceof DecimalType && dimension != null) {
            Unit<?> unit = getUnit();
            if (unit != null) {
                super.setState(new QuantityType<>(((DecimalType) state).doubleValue(), unit));
                return;
            }
        }

        // QuantityType update, check unit and convert if necessary:
        if (state instanceof QuantityType) {
            Unit<?> itemUnit = getUnit();
            Unit<?> stateUnit = ((QuantityType<?>) state).getUnit();
            if (itemUnit != null && (!stateUnit.getSystemUnit().equals(itemUnit.getSystemUnit())
                    || UnitUtils.isDifferentMeasurementSystem(itemUnit, stateUnit))) {
                QuantityType<?> convertedState = ((QuantityType<?>) state).toUnit(itemUnit);
                if (convertedState != null) {
                    super.setState(convertedState);
                    return;
                }

                // the state could not be converted to an accepted unit.
                return;
            }
        }

        if (isAcceptedState(acceptedDataTypes, state)) {
            super.setState(state);
        } else {
            logSetTypeError(state);
        }
    }

But when getting the state, it uses getState from GenericItem, which is just:

    @Override
    public State getState() {
        return state;
    }

Note: I know that OH 2.5 uses ESH and OH core is WIP for OH 3, so it might have changed, but I think it’s the same behavior.

Yes. You send a number with no unit, it has to guess what you mean.
Try sending a quantity type with unit via REST.

Yes. That’s what I said.

Misread your post the first time, sorry about that.

But wouldn’t it make sense to also check the stateDescription when getting the state as well?

Don’t see why, the state is the state. With units included.

Well, if I use a UI that gets its values from the rest api (e.g. Habpanel, and I believe the new UI for OH3?) and want to change the unit for some reason (not that it should happen very often), I would have to wait for the Item to receive a new update before I can get the correct state. PaperUI even changes the unit when the Item is edited, but the value is still the same.

Well no, because the UI would use the [state presentation] to format it. That’s what it’s for. Same as if the Item state is 2.173157843 and you format it with %.1f to see 2.2, but the actual state is still 2.173157843
If the state is 22 km, but you specify [%d m], it’ll convert in flight to 22000 m

But that what’s NOT happening when querying the item state via the rest api, not until the item receive a new update. If the state is 25 °C, I change the state description to °F, I still get 25 °C when it should be 77 °F, unless the item’s state gets updated.

Edit: The UI could see that it should use °F, but would still get 25, not 77 (like paperUI currently does)

Yes, that’s the state. Fiddling with [state presentation] will not change the state, that is all as it should be. The state is the state, and will not change until somebody updates it.

Have a look at this demo
events.log of a Number:speed Item being updated by binding.
The Item has a presentation [%.0f mph]
The weather service supplies in km/h, I believe.
The binding notes my “desired unit” and converts to mph for the update

2020-06-10 21:03:14.528 [vent.ItemStateChangedEvent] - nms_CurrentWindSpeed changed from 8.052964209024480583392984967788117 mph to 10.28989871153128074544659190328482 mph

sitemap, same Item different units

Text item=nms_ForecastWindSpeed label="Demo mph [%.0f mph]"
Text item=nms_ForecastWindSpeed label="Demo kph [%.0f km/h]"
Text item=nms_ForecastWindSpeed label="Demo cmh [%.0f cm/h]"

resulting display
oh-uom-demo

just to prove auto-conversion without changing Item state.

I actually have no idea how the UI gets the converted format to display, what the REST call is. It may be via the register-for-update mechanism, outside of the regular ad-hoc REST calls.

That alarm Item illustrates a different but related UI “thing”, it’s a Number Item using a MAP for display via presentation [MAP(alarms.map):%s]
It’s state is always a number, of course.
But you can get both the “real” and “transformed” states via REST

{
  "link": "http://localhost:8080/rest/items/vnm_Intruder",
  "state": "1",
  "transformedState": "disarmed",
  "stateDescription": {
    "pattern": "MAP(alarms.map):%s",
...

I’ve no idea why that transformedState entry is not also invoked for ordinary format/units version, that would be what I expect.

As I said before, this works in sitemap-based UIs, but not rest-based UIs.I would expect both to behave the same.

The transfomedstate is only present when there’s a transformation service involved, not for regular UoM Numbers:

{
  "link": "http://localhost:8080/rest/items/Temperature_Today",
  "state": "73.04 °F",
  "stateDescription": {
    "pattern": "%.1f °C",
    "readOnly": true,
    "options": []
  },
  "editable": false,
  "type": "Number:Temperature",
  "name": "Temperature_Today",
  "label": "Temperature",
  "tags": [],
  "groupNames": []
}

For sitemaps, (using the /sitemaps rest endpoint) I get this for the widget:

            "widgetId": "0000",
            "type": "Text",
            "visibility": true,
            "label": "Temperature [77.7 °F]", // Converted state
            "icon": "number",
            "mappings": [],
            "item": {
              "link": "http://localhost:8080/rest/items/Temperature_Today",
              "state": "25.4 °C",
              "stateDescription": {
                "pattern": "%.1f °F",
                "readOnly": true,
                "options": []
              },
              "type": "Number:Temperature",
              "name": "Temperature_Today",
              "label": "Temperature",
              "tags": [],
              "groupNames": []
            },

So it does some conversion before sending the label.

Can you provide an example? I do not use HABpanel, I know there is a “use server provided format” option or similar.
That may be a problem if it relies on the “transformedState” in the JSON because …

Yes, as said, we agree on that.
I think its a deficiency - the JSON field was added before UoM was added, but now it would be useful to use this when say a unit conversion was requested.

or maybe it’s the UI.that needs enhancement to use the event-driven feed with “displayState” field, that’s very recent. I guess that means it won’t get into OH2 either.

Woah, that’s magic!

I agree that’s probably the best solution. Thanks for opening the issue!

I tried to look through the source code to see where and how this takes place, but couldn’t find it anywhere. It’s probably buried in som xtend magic that I can’t make sense of.

Do you get this when subscribing to the /rest/events sse-endpoint? Didn’t try there. Or is it something new for OH3 only?

I have no idea. BasicUI gets it from somewhere, my limited understanding is it subscribes to an events push service, gets notified of Item updates, and I presume that includes the “finished” state display somewhere.

Saw in the comment on your issue that that’s probably new for OH3.

BasicUI/ClassicUI uses different endpoints entirely, there are dedicated /sitemaps/events/subscribe and /sitemaps/events/{subscriptionid} endpoints that only the sitemap-based UIs use (see the rest docs).

What are you actually trying to do here?

I think we’re clear now, that changing [state presentation] in flight is not expected to change the Items current state in any way.

But it might influence a later updater, a binding.

And obviously change the display presentation, which might involve units conversion by the framework. Getting hold of that presentation product is difficult for a casual REST user…

It’s not actually a problem for me, was just something I noticed when trying out UoM in my binding, and
wanted to get to the bottom of it, which I believe I have now with your help :smile:

I’m completely with you on this one, I didn’t expect the internal state to change, just what I get when requesting the state (like the transformedState when using transformations, or however the sitemaps handles it). And it seems this will be solved in OH3 now, so I’m content!

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.