Inconsistent percentage behaviour

Is there no parsing error for your items file? the 2 double quotes after your stateDescription also look wrong to me.

Edit: forget my remark on this, see [OH3] Add metadata to Items via configuration files
That looks like the way to do it. I don’t use textual configuration, so was not aware.

I still think the pattern (twice in the definition) is wrong and should only contain the dot, not the comma.

1 Like

Dot plus comma changes the visualization for Germany (dot for thousand, comma for values lower than 0). I use this in several hundred items without error.

Sorry for the bad advice, indeed, you are right, it should work.

no problem! :wink:

The behavior is deterministic.

Here is how it works.

The unit metadata of the Item determines the unit of the state of that Item. Any update sent to the Item gets converted to that unit if it’s not already that unit.

  • if there is no unit in the update, the unit is assumed (e.g. send 57 as the update and the unit is %, the Item state will become 57 %)
  • if the unit is in the update but it’s different from the unit, the value is converted (e.g. send 57 % as the update and the unit is ONE, the Item state will become .57 ONE)
  • if the unit is in the update but it’s not compatible with the unit/dimension of the Item, an error is generated

When you have a Number:X type Item without unit metadata, the system default unit is assumed. For Number:Dimensionless the system default is ONE.

Persistence doesn’t save the unit, just the raw number. When restoreOnStartup occurs, the unit metadata is what is assumed to be the unit of the value.

This is why it can cause problems if you change the unit of an Item. The values stored in persistence might be in unit ONE and the Item changed to unit % which means the value in persistence gets restored with the wrong unit. For example, if 0.57 ONE was stored in persistence, then 0.57 % would be what the Item gets restored to, not the correctly converted value of 57 %.

In post 12 above, the unit for the Item is ONE which means that if you send it a %, that value will be divided by 100 to convert from % to ONE.

Similarly, this line will multiple the value by 100 to convert from ONE to %.

It’s really hard to follow what state the rule and Items are in now but to minimize conversions you should:

  • define the unit metadata of the Item as % if that’s what you really want this Item to represent
  • inside the rule keep using the QuantityTypes for the calculations, don’t convert to primitive numbers
  • verify that the values stored in persistence are % and not ONE values. If they are ONE values, particularly the most recent value which would be used for restoreOnStartup, convert it to %
  • keep the state description pattern showing %

These changes will make it always show and always use % as the unit. No conversions between % and ONE and back again will take place.

Hi,

read but not sure what this means. I have defined metadata and not added them recently. Hence, even when restoring from influx db, the correct value should be there?

Best
Matthias

Your Item has the unit ONE based on the logs above.

Let’s assume your persistence strategy is everyChange.

  1. An update of “57 %” is sent to the Item
  2. The Item updates to “0.57 ONE”
  3. Persistence saves “0.57”

Now let’s say you change the Item’s unit to % and reload your .items file. restoreOnStartup gets triggered.

  1. restoreOnStartup sends “0.57” as an update to the Item as that’s the value saved in 3 above
  2. The Item’s unit is % and the update doesn’t have a unit because the unit isn’t saved by persistence so the Item updates to 0.57 %, not 57 %.

I do think there is something wrong indeed with the handling, specifically of unit ONE.

I created an Item Test with unit % and pattern %,.2f %%.

I tested with the javascript below (result is similar with DSL). Notice I tried updating the result of the division 4 times, once straight, twice after conversion to a compatible unit (I even used dB), and once where I explicitly appended one to the result. The timeouts are there to wait for the update to happen before logging.

It looks like, as the one is lost in the result somewhere, it assumes the configured unit for the update, and therefore fails the expected conversion. This is a bug.

var logger = log('Test');

var bkw    = Quantity('5 W')
var zwl    = Quantity('5 W')
var result = bkw.divide(bkw.add(zwl))   // I want to preserve the QuantityType for logging

logger.info('Result of calculation is: {}', result.toString())
logger.info('Unit is: {}', result.unit) // I would actually expect this to show `ONE`
logger.info('Dimension is: {}', result.dimension)
logger.info('Result %: {}', result.toUnit('%').toString())

logger.info('Test 1, no unit applied: {}', result.toString())
items.Test.postUpdate(result)
setTimeout(() => {
  logger.info('State: {}', items.Test.state)  
  logger.info('Test 2, % unit applied: {}', result.toUnit('%').toString())
  items.Test.postUpdate(result.toUnit('%'))
  setTimeout(() => {
    logger.info('State: {}', items.Test.state)
    logger.info('Test 3, dB unit applied: {}', result.toUnit('dB').toString())
    items.Test.postUpdate(result.toUnit('dB'))
    setTimeout(() => {
      logger.info('State: {}', items.Test.state)
      logger.info('Test 4, one appended: {}', result.toString() + ' one')
      items.Test.postUpdate(result.toString() + ' one')
      setTimeout(() => {
        logger.info('State: {}', items.Test.state)  
      }, 1000)
    }, 1000)
  }, 1000)
}, 1000)

Here is the log output:


==> /var/log/openhab/openhab.log <==
2024-04-02 18:17:51.042 [INFO ] [g.openhab.automation.openhab-js.test] - Result of calculation is: 0.5
2024-04-02 18:17:51.044 [INFO ] [g.openhab.automation.openhab-js.test] - Unit is: One
2024-04-02 18:17:51.046 [INFO ] [g.openhab.automation.openhab-js.test] - Dimension is: one
2024-04-02 18:17:51.049 [INFO ] [g.openhab.automation.openhab-js.test] - Result %: 50 %
2024-04-02 18:17:51.051 [INFO ] [g.openhab.automation.openhab-js.test] - Test 1, no unit applied: 0.5
==> /var/log/openhab/events.log <==
2024-04-02 18:17:51.056 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Test' changed from 50 % to 0.5 %
==> /var/log/openhab/openhab.log <==
2024-04-02 18:17:52.056 [INFO ] [g.openhab.automation.openhab-js.test] - State: 0.5 %
2024-04-02 18:17:52.060 [INFO ] [g.openhab.automation.openhab-js.test] - Test 2, % unit applied: 50 %
==> /var/log/openhab/events.log <==
2024-04-02 18:17:52.069 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Test' changed from 0.5 % to 50 %
==> /var/log/openhab/openhab.log <==
2024-04-02 18:17:53.070 [INFO ] [g.openhab.automation.openhab-js.test] - State: 50 %
2024-04-02 18:17:53.074 [INFO ] [g.openhab.automation.openhab-js.test] - Test 3, dB unit applied: -3.01029995663981149813768617048017673474732841360630 dB
2024-04-02 18:17:54.083 [INFO ] [g.openhab.automation.openhab-js.test] - State: 50 %
2024-04-02 18:17:54.086 [INFO ] [g.openhab.automation.openhab-js.test] - Test 4, one appended: 0.5 one
2024-04-02 18:17:55.093 [INFO ] [g.openhab.automation.openhab-js.test] - State: 50 %

Test 1 makes sense. result is unit ONE.

But the ItemStateChangedEvent doesn’t make sense. It’s like it’s ignoring that the update is of unit ONE. I’ve not looked at the code that handles the updates but I wonder if it converts and processes the QuantityType as a String and since ONE doesn’t normally show the unit it can’t tell the difference between ONE and a number without units.

Test 2 and the resulting ItemStateChangedEvent make sense.

Test 3 looks OK too. There’s no state change because the db is converted back to %.

Test 4 looks right too, further supporting my guess that the lack of a printed unit for ONE is not usually shown.

Indeed I would guess this is a bug but it may not have an easy fix beyond always showing the one as the unit in toString(). I don’t know if that would be a breaking change though. Probably not.

There might be something that can be done in postUpdate to test if it’s a QuantityType but it’s been a really long time since I traced through the postUpdate code. If the update is converted to a String first though, as always happens in the JSR223 rules languages since events.postUpdate is used and that only accepts two Strings as arguments, the problem will persist so that’d only be a partial solution.

In the mean time, I think the work arounds are:

  • avoid using unit one in rules, always convert to % (or what ever makes sense) prior to calling postUpdate
  • manually append the one unit in the call to postUpdate

That’s indeed what happens, and I agree this may not be an easy one to fix.

What would be the correct item description to cover correct reload after Startup?

What’s saved?

Ultimately the value stored in the database needs to be the same units as the Item. The best approach is to actually set the unit metadata to the unit you want to use. Then everything will use that unit.

Maybe we have a misunderstanding: as I thought I am already defining the unit in the meta data as described in the item file in the first post. Anything wrong on this?

The output of the unit was the test script, not my item definition. I can test tonight as well.

I believe your definition is already correct for persistence to work in the right unit.

As my test script shows, there is indeed a bug (which may not be easy to fix) now identified that updates the state wrongly on an item with dimension ONE, when the unit is set to anything but the default and you update with a unit of ONE.
The way to avoid this is to update with any other unit (so convert to % and update will do).

I created a bug report for this: QuantityType<Dimensionless> with unit one wrong update to item with different unit set · Issue #4166 · openhab/openhab-core · GitHub

Just to possibly clear up any lingering confusion.

  1. the unit metadata of your Item needs to be set to %, which appears to be the case
  2. The stateDescription pattern also needs to define %% as the unit, which appears to be the case
  3. Any values stored in the database by persistence that were saved before 1 above was implemented is wrong. These values were saved with unit ONE and not % but when you restore these values OH is going to think they are %.

@rlkoshak I am beginning to share your opinion: we really really really need to have a fix for unit:dimensionless:percent on OH core and/or in OH UI. :slight_smile:

The only viable fix is to set % as the default. Since that has been rejected out of hand I see no solution proposed so far that doesn’t have the same or similar problems than what we have now. They move the problem, don’t solve the problem.

1 Like

This specific problem is caused by the fact that there is special handling in core for unit ‘one’. In the toString() method of the QuanityType, ‘one’ is dropped from the string. That is a special case explicitely done in core. By consequence all logic that depends on the unit being there fails.
A solution could be to remove this special handling. Without a state description, the visualisation would be less nice (showing ‘xxx one’ instead of just ‘xxx’), but that could be solved with a state description. I am just not sure it doesn’t create any other issues somewhere else.

Secondly, having % as a default would indeed be nice, but as it got rejected (and I also understand why), having a unitHint defined on the channel type would come close for anyone configuring through the UI. That core PR (Unit hint in thing channels by mherwege · Pull Request #4079 · openhab/openhab-core · GitHub) is still open.

I actually think this would not solve the problem we have here. Let’s assume the default unit is %. And you want to update with a fraction from a calculation (like the example in this thread). As core drops ‘one’ from the calculated value string, it will assume it is % and happily update the item to a value which is 1/100 of what it should be. So same problem. It is actually what happens in the example here (because the unit was explicitely set to %).