Inconsistent percentage behaviour

And in the last line, it needs to be result.toUnit... instead of result.toUint....

You actually don’t need the conversion in that last line, because you can just postUpdate the quantity value from the calculation as is. Because you set the unit to be % in your item definition, it will persist as %. Because of the stateDescription string it should show as % as well. You can save in any compatible unit.

Works:

2024-04-02 14:07:37.927 [INFO ] [org.openhab.core.model.script.Test  ] - Result of calculation is: 0.21371544896466172588186647157988482453
2024-04-02 14:07:37.929 [INFO ] [org.openhab.core.model.script.Test  ] - Unit is: one
2024-04-02 14:07:37.930 [INFO ] [org.openhab.core.model.script.Test  ] - Dimension is: one

Also this line works:

    Hausstrom_Autarkiegrad.postUpdate(((Balkonkraftwerk_ACPower.state  as QuantityType<Power>) / ((Balkonkraftwerk_ACPower.state as QuantityType<Power>) + (Stromzaehler_Wirkleistung.state as QuantityType<Power>))).toUnit('%'))

I just don’t know how long / reliable this is.

This should always work. The issue often stems from persisting in the wrong unit, or mixing pure Number and QuantityType.

And back to my suggestion:

Hausstrom_Autarkiegrad.postUpdate((Balkonkraftwerk_ACPower.state  as QuantityType<Power>) / ((Balkonkraftwerk_ACPower.state as QuantityType<Power>) + (Stromzaehler_Wirkleistung.state as QuantityType<Power>)))

should give exactly the same result. You do not need to explicitly convert to the % unit as long as you have the state description pattern set to show % in your item definition.

We are going in a loop… ;-).

stateDescription has NOT been changed but openhab changed by factor 100! This is my initial request: Not deterministic behavior.

Any changes I have overseen in the last few openhab updates?

I don’t think this is a valid state description, using both , and . It should only be .
Haus Autarkiegrad [%.0f %%]

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