Persistence history is not what I expect

Hello,
I’m new to openHAB, and I’m trying to better understand how persistence data works. I have a solar inverter, which reports a parameter called Autonomy. It is of type Number:Dimensionless, and during the day, it is usually 1.

When I run the following script (application/javascript;version=ECMAScript-2021), I get an output that seems inconsistent to me. As far as I can tell, these three values that I print to the log should all be 1. Is anyone able to help me understand where I am going wrong?

"use strict";
(function() {
  console.info('start test script');
  const powerAutonomy = items.getItem('FroniusSymoInverter_Autonomy');
  const twoMinutesAgo = new Date(new Date().getTime() - (2 * 60 * 1000));
  console.info(powerAutonomy.history.minimumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.maximumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.averageSince(twoMinutesAgo));
  console.info('end test script');
})();

openhab.log:
2022-12-08 11:33:18.644 [INFO ] [nhab.automation.script.ui.e1d457cd25] - start test script
2022-12-08 11:33:18.645 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 1
2022-12-08 11:33:18.647 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100 %
2022-12-08 11:33:18.648 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 8.779954150091026
2022-12-08 11:33:18.648 [INFO ] [nhab.automation.script.ui.e1d457cd25] - end test script

Thank you for your help!
Stuart
(edited to remove unnecessary code)

Tipp: name your rules yourself, so you can see at a glance, which rule triggered the log-entry and you don’t have to look at the generated random id.

first off: ECMA-2021 aka JS Scripting is a bit akward coming from DSL or even ECMA5, but:

  1. items.getItem('FroniusSymoInverter_Autonomy') is “the item” with all its metadata and stuff, it’s not the value
  2. items.getItem('FroniusSymoInverter_Autonomy').state is the item’s “String” state representation
  3. items.getItem('FroniusSymoInverter_Autonomy').rawState is the item’s value in actual representation (like Number, …)

be aware: 2 and 3 are with UOM, if your item uses “units of measurement”

So, what you need to do (and if you’ve got problems, plese provide us with that information):

  1. what’s your FroniusSymoInverter_Autonomy item’s config?
  2. what’s the metadata for that item? like stateDescription?
  3. then make up your mind, if you’d like to use percentages (0-100%) or decimal respresentatoin (like 0-1, meaning 50% equals 0.5)
  4. visit your rule again and try to understand how .state and .rawState work and then try to build your desired logic around that.

hint:
If you’d like to compare/calculate, you should be aware, if your item is defined with an UOM (I guess, the Fronius-API uses 0-1, but the Fronius-binding works with 0-100%?), you have to either compare to other variables with that same UOM or you strip the UOM in the first place (intValue() does that for Number-items):

  const powerAutonomy = items.getItem('FroniusSymoInverter_Autonomy').rawState.intValue();

But to finally answer your initial question:

  1. the minimumSince results from the binding sending “1” to the item (my guess)
  2. the maximumSince results from the metadate configuring the item to percentages (so 100% is highest)
  3. the averageSince results from calculation 1-100 for the last to minutes.

=> to be finally sure, have a look at your events.log, there must be some entries for that item, with “1” and “100 %” for it, I’m pretty sure.

Comment; Dimensionless Quantity types can throw up unexpected values in some circumstances, when the Item has no default unit specified.
This is because “no-unit” is a valid unit for Dimensionless, meaning a ratio or proportion or fraction (a ONE unit really)
i.e. 80% == 0.8 (in invisible units of ONE)

Another comment; openHAB persistence services do not truly use the units of Quantities. They store only the numeric state, and whenn you retrieve data re-apply the Item’s current default unit.
There’s an obvious pitfall that recorded data will be messed up if you change Item default.

A deduction; retrieving persisted Dimensionless info where the Item has no default unit gets exciting

This needs clarification - is it 1 , or 1% , or 100% ?
Do not trust GUI which may apply default units and conversions, do not trust charting (persistence) which may apply default units and conversions, look in your events.log or API Explorer.

For a Dimensionless, these two values are the same.

Its never clear to me if min/maxSince includes current Item state, I imagine it does. I’ll bet one of those is retrieved data and one is current state. Either is the “correct” answer for max/min when there’s been no change.

The averageSince has got into trouble, I would guess it is not properly converting in the 1==100% results in the absence of Item default unit - but that remains to be found out.
Note the “average” calculation is time-weighted - it might be trying to average 3 or 4 records and the current state e.g. 1,1,1,100

1 Like

Thank you both for you detailed answer. There is quite a bit for me to get my head around here. I’m ashamed to say that I had not encountered the API explorer until now, so this has really helped.

From the API Explorer, my item config looks like this. I don’t know what to make of this, except that it seems to be a floating point number.

{
    "link": "http://192.168.1.119:8080/rest/items/FroniusSymoInverter_Autonomy",
    "state": "0 %",
    "stateDescription": {
      "pattern": "%.1f %unit%",
      "readOnly": true,
      "options": []
    },
    "editable": true,
    "type": "Number:Dimensionless",
    "name": "FroniusSymoInverter_Autonomy",
    "label": "Autonomy",
    "category": "",
    "tags": [
      "Point"
    ],
    "groupNames": [
      "FroniusSymoInverter"
    ]
  },

I suppose I am confused that the events.log shows values between 0% and 100%, while the persistence values from the API explorer are a ratio between 0 to 1.

2022-12-08 18:32:33.193 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'FroniusSymoInverter_Autonomy' changed from 46.87730695408239 % to 86.47798742138365 %
2022-12-08 18:32:46.230 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'FroniusSymoInverter_Autonomy' changed from 86.47798742138365 % to 100 %

   {
      "time": 1670495580000,
      "state": "0.7959193804090523"
    },
    {
      "time": 1670495640000,
      "state": "1"
    },

Thanks,
Stuart

The important stuff for us here -

Okay, we knew that, but confirmed. This is a ‘Quantity type’ Item and its state will have units (and as stated earlier the Dimensionless type uniquely can have an invisible unit ONE, as well as %, ppm or dB etc.)

That’s clear too; the current state is 0% (with % unit)

This part is less obvious - ‘pattern’ is primarily about presentation, how to format the state for display.
In this case “show me the number to one decimal place, and use whatever unit it comes in”
That’s the %unit% part - those % characters are just special escape characters here, not percent units.

But ‘pattern’ has a secondary purpose - if a real unit is supplied there, the system will use that as the default unit for this Item. It’s a kludge having this dual-purpose, but it’s what we got for now because of development history.

So
"pattern": "%.0f %%",
would be “show me in round numbers, in percent units”
because to get % we need to escape the % character, leaving %%
But unlike %unit% this would also designate % as the default unit for this Item.

So …
if there is no default unit yet, why has the Item state got the % unit?
That’s easy - the binding knows what units the measurement comes in and its update via the channel is that whole ‘quantity’, including the unit.

But why do you need a default unit then?
We’ll see persistence in a moment, but also because “it might be different”.
You could for example have an Item, set default unit kW. If a binding now posts update 500W to that Item, the state will become 0.5kW. Because that’s what we asked for - and the system auto-converts.

To complete the picture, should a binding or rule post a plain 50 (no units) to that Item then the default would be assumed - state would become 50kW

Because … persistence services do not store units, only the numeric.

That means that recovering the data requires reconstructing the unit from “somewhere” - this is where the default comes into play.

In the special case of Dimensionless “no default” gets treated like “invisible unit” (ratio) and so you end up with value 0.0 to 1.0 standing in for 0% to 100%

The solution; give your Item a default unit of % (see above for ‘pattern’ %%).
Accept that any already-stored persistence data may now be messed up.

2 Likes

Thank you rossko57 for pointing me in the right direction. I appreciate your help.

A bit of an aside, but I redefined the Autonomy item using the file-based system as I was struggling to make the changes through the API. I deleted the existing Autonomy definition in the UI and added the following definitions to my .items file.

  Number  FroniusSymoInverter_Autonomy "Autonomy [%.0f]" <energy> {channel="fronius:powerinverter:e5e3b4dca8:powerflowautonomy"}
  Number:Dimensionless  FroniusSymoInverter_Autonomy2 "Autonomy [%.0f%%]" <energy> {channel="fronius:powerinverter:e5e3b4dca8:powerflowautonomy"}

Here is a script to print the item states to the console.

"use strict";
(function() {
  const twoMinutesAgo = new Date(new Date().getTime() - (2 * 60 * 1000));
  
  console.info('autonomy [%.0f]');
  var powerAutonomy = items.getItem('FroniusSymoInverter_Autonomy');
  console.info(powerAutonomy.state);
  console.info(powerAutonomy.rawState);
  console.info(powerAutonomy.history.minimumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.maximumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.averageSince(twoMinutesAgo));

  console.info('autonomy2 [%.0f%%]');
  powerAutonomy = items.getItem('FroniusSymoInverter_Autonomy2');
  console.info(powerAutonomy.state);
  console.info(powerAutonomy.rawState);
  console.info(powerAutonomy.history.minimumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.maximumSince(twoMinutesAgo));
  console.info(powerAutonomy.history.averageSince(twoMinutesAgo));
})();
2022-12-11 12:59:33.450 [INFO ] [nhab.automation.script.ui.e1d457cd25] - autonomy [%.0f]
2022-12-11 12:59:33.451 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100
2022-12-11 12:59:33.451 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100
2022-12-11 12:59:33.452 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100
2022-12-11 12:59:33.453 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100
2022-12-11 12:59:33.453 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100.0
2022-12-11 12:59:33.453 [INFO ] [nhab.automation.script.ui.e1d457cd25] - autonomy2 [%.0f%%]
2022-12-11 12:59:33.454 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100 %
2022-12-11 12:59:33.454 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100 %
2022-12-11 12:59:33.455 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 1
2022-12-11 12:59:33.455 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 100 %
2022-12-11 12:59:33.456 [INFO ] [nhab.automation.script.ui.e1d457cd25] - 15.59660132562403

I think that everything here makes sense to me now except for the Autonomy2 minimumSince value, which I would expect to be 100%. It looks like this value is being interpreted as a 1 and is dragging the average down.

My solution which is working so far is to use Number rather than Number:Dimensionless. While I’m not satisfied that fully understand what is going on here, is does seem to be related to units of measure. Thanks again rossko57.

Is it more than two minutes since you made your changes? Probably, but do bear in mind that making changes will NOT go back into the historic data records and change them.
You should ensure you are looking at data recorded under “new” conditions.

Defining the ‘pattern’ in the text Item should also define ‘pattern’ in the metadata. I think, maybe. But I wouldn’t gaurantee it defines default unit properly, check using API Explorer maybe.
These multiple possible sources are as confusing for developers as users and small changes can mess up this kind of thing.

You should be cautious about that; either the channel is feeding your Item update with a quantity/unit, or it isn’t. That ashould dictate choice of Item type to link it to. There may be other unwanted effects, just as obscure.