OH 3 to OH 4 : most percentage values are now divided by 100

Maybe the misunderstanding is, that number:dimensionless is not only used for percentage number, but also for other numbers, their is not % per default.

You maybe get more attention to this if you log it in github

Actually, there was an issue on a very similar subject so I posted a comment in it:

First section of breaking changes listed for OH 4.

A deliberate decision was to not allow this. The Thing can push State Description patterns to the Item which controls how it appears, but to control the unit of the actual state of the Item is 100% in the end user’s control. If the user chooses to not define the unit, the system default is used.

With or without the unit, the actual state of the Item will be converted to the unit defined for the Item.

In the case of Dimensionless, the system default is a simple ratio, not percent. So any update sent to such an Item will be converted to a simple ratio.

Since OH 4.1, every place you can create an Item in MainUI, you have the option to set the unit right when you create the Item. If you are using .items files, well you know what you are doing. The field gets pre-populated with the system default so you know what it will be if you choose not to set it.

It’s primarily confusing because the way UoM worked in OH 3 and before was not well defined nor well structured. Sometimes the unit comes from the binding. Other times it comes from the State Description pattern which should be only for formatting the display of the state, not controlling the unit of the Item’s actual state. You could end up with a plain Number Item that is carrying a unit. restoreOnStartup behavior was, to put it bluntly, indeterminate.

UoM in OH 3 and before was just one step short of a disaster.

Now in OH 4 you can always know what unit the Item is carrying and you have full control over it. There is a clear separation between what the user wants and what the binding delivers. An no more is it ever unclear where the unit comes from or what unit is being used.

The main confusion here stems from the fact that the default unit for Number:Dimensionless is ONE and not %. So if you don’t set the unit metadata, any update sent to this Item will be converted to ONE.

1 Like

And that’s where the life of users would be much simpler if all humidity channels defaulted to % and %0.f%% because remembering this is a real pain point. I’m not even sure all users took the time to fix this but simply went “meh, it’s not working”.
Sure, openHAB cannot know that automatically but tools should be given to binding developers to provide the information to the system, hence the importance of the issue mentioned above.

I can see an argument for changing the default unit for Number:Dimensionless to % but it seems like way too little gain to invent yet another API for binding authors to inject more stuff into Items, then you have to provide a way for end users to override it, etc.

But it’s also the case that end users don’t have to remember to change it. They would have to actively ignore it when creating their Items.

It’s presented as an option same as name, label, et. al. in all the places where the Item can be created in MainUI. And it’s given a more prominent position than the category or even the semantic model and Group membership.

Changing the default value to % would most likely cause issues with other bindings using dimensionless for other purposes and we would be back to the start.
That being said, I can’t see any use case where “one” is a sensible default value, let alone that it confuses non native speakers like me who wonder “one what?” as one is most exclusively associated with the number and not anything else.

However, I maintain that the suggested “unit-hint” seems quite sensible to me, as it would provide the default value I’m looking for while leaving the current UI untouched where the user can change the “hinted” value.
The same would be needed for the state description pattern and I’d be sorted.

Or, another idea “out of the hat” is that there might be a need for a Number:Percentage specifically for things like Humidity, Battery Level… But this one would require quite a change in a lot of places and so might be out of the realm of possibilities.

Not just non-native speakers. There really isn’t such a unit called ONE but they needed to call a simple ration something. I think calling it ONE is what the upstream library choose to call it. The upstream library is also who chose this as the default unit. I think OH though does have some ability to change that.

Bindings can already push State Description patterns. And frankly I causes as much if not more problems than the default for Number:Dimensionless not being %. There are all sorts of weird interactions when the binding silently (there’s no evidence in the UI that the binding has supplied a state description) pushes options or command options or readonly or the like. Suddenly I’ll change the state description pattern and nothing happens because there are hidden options there that are taking over. And don’t even try to change the type of the Item through a profile. If you try to use the timestamp profile on a link from a binding that pushes state description all sorts of interesting and wrong stuff happens. And because it’s all hidden the user is left stumped and the solution is to open the code tab and enter a space for options (or what ever else is a problem) to override the stuff silently pushed from the bindings.

You see all this stuff as helping save users work. In my experience it causes all sorts of problems.

Probably less changes that creating a whole new API to provide a path for bindings to suggest a default unit and show it in the UI when creating an Item.

And it should be a simple matter to search to see if any binding is using the ONE unit at all. I’d be astonished if there is a single case. Changing the default unit for Number:Dimensionless to % would probably be the least amount of work over all. Bindings using other Number:Dimensionless units like db already have to deal with the default not matching so this wouldn’t change anything for those.

I am having the same (or a similar?) problem in OH4, but even although I have added the unit tag:

Number:Dimensionless battery_level "Battery Level" <oh:batterylevel> (batteryInformation,eBattery) ["Measurement", "Level"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-level", unit="%"}

The output I see is 1%, not 100%:

What am I missing?

You have unit defined so that’s good. I didn’t use items files though. Maybe you need to escape the unit: i.e. %% ?

Also, double check if this is just a display issue or the Item’s actual state is wrong. You can really see this in events.log.

No using %% does not seem right, then it also shows up that way in the UI.

My events.log shows entries such as:
Item 'battery_level' changed from 1 % to 0.978 %

But are these values before or after OH has processed them?

Since there’s is no such thing as a %% unit, I don’t see how that can change how it appears in the UI that way. So that’s weird.

These are after OH processes them.

One thing you can do is link this channel to just a Number Item and see what the Channel is delivering. I’m willing to bet it’s sending a number between 0 and 1 and you need to apply an Offset Profile to multiply that by 100 and tack on the unit before it gets to OH.

Without the unit, which modbus doesn’t add on it’s own, OH is just going to spend the unit to what ever number it’s sent.

Well, it shows %% in the field Unit. Beats me… Might be a bug.

I agree that the Thing is likely sending a value between 0 and 1. When I define a second, plain Number item, the situations looks like:

But I thought that adding unit="%" would have that value multiplied by 100 automatically. Would that not be the sensible thing to do for a unit type?

I misunderstood. I thought the Item’s state showed %%. I That would be weird. But I would expect the unit field to show exactly what it’s set to.

That’s not how units work. If an Item with a unit (e.g. %) is updated with a number without a unit (e.g. 0.5) the defined unit is assumed (i.e. 0.5 %). It only does anything to the number if both the update and the Item have units, in which case the update is converted to the units of the Item.

If you want to convert 0.5 to 50%, you have to apply the offset profile and either multiply by 100 and set the unit to %, or leave the number unmodified and set the unit to ONE.

1 Like

Got it. I will give this a try, thanks!

Hmm, somehow I am still failing at this. The offset profile does not yet seem to support multiplication (at least according to the docs). I instead tried a JS toItemScript but that also does not yield the desired result:

(function(i) {
    console.log("value: "+ i );
    return i*100;

I guess I somehow need to do something more fancy than just multiplying the value by 100?

From the modbus docs:

This profile is meant for simple scaling and offsetting of values received from the Modbus slave. The profile works also in the reverse direction, when commanding items.

In addition, the profile allows attaching units to the raw numbers, as well as converting the quantity-aware numbers to bare numbers on write.

Profile has two parameters, gain (bare number or number with unit) and pre-gain-offset (bare number), both of which must be provided.

When reading from Modbus, the result will be updateTowardsItem = (raw_value_from_modbus + preOffset) * gain. When applying command, the calculation goes in reverse.

See examples for concrete use case with value scaling.

If you set the pre offset to 0 and the gain to 100 you should get your answer.

what result does it give? As written, since i is a String, I’d expect an error.

This sounds like it should do the trick but now it’s turning a raw value of 0.052 into 52000%. What is going on here?

My current definitions are:

Number:Dimensionless battery_level "Battery Level" <oh:batterylevel> (batteryInformation,eBattery) ["Measurement", "Level"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-level"[profile="modbus:gainOffset", preOffset="0", gain="100"], unit="%"} 
Number battery_level_num "Battery Level" <oh:batterylevel> (batteryInformation,eBattery) ["Measurement", "Level"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-level"}

Regarding the JavaScript code: Is i really receiving a string from the binding? I thought it would be a number.

Got it to work! The gain had to be 0.1 apparently, see here:


1 Like

All transforms, including JS, receive Strings and produce Strings. If it’s not a String before it gets to the transform, it’s converted to one. It’s it’s not a String returned by the transform, it’s converted to one.

Later on the Item will pase it into what ever type the Item requires.

1 Like

Oh I see. Good to know! Thanks!

This reminds me again how much of a fan I am of statically typed programming languages.