How to calculate averagesince without NULL values

Hi all,

i have a question for scripting with persistence.
given:
In this Persistence-screenshot you can see my calculated cop. This value is only written if not null now.


searched for:
I want to calc the cop average but only if a cop value is there. The time where values available can differ every day from 1 hour to 24 hours values available depends on how long the machine is running.
my script now:

debug=1;

var now = time.ZonedDateTime.now();
var cop1d=items.waermepumpe_cop.history.averageSince(now.minusMinutes(1440));


(debug==1)?console.log("cop1d", cop1d):null; 

Result until now:
cop1d 0.1025502024192454
cop1d 0.1024965139778226
cop1d 0.1024773211519935
…the value sinks, that means that the incoming null values are calculated into average. the in graph shown 0-values were no more written to graph now.

Could someone give me an idea how to calculate average with only the values and not treating the null values as 0 in average-calculation?

I added this for reasons like these.

You can just loop through and do your own calculation, filtering the null as you go.

I’m not familiar with jsscripting but to illustrate, this how it would be done in jruby:

states = waermepumpe_cop.all_states_since(24.hours.ago)
                        .reject { |history| history.state.is_a?(UnDefType) }
                        .map(&:state)
zero = QuantityType.new(0, waermepumpe_cop.unit)
average = states.sum(zero) / states.size
logger.info average

Hi @JimT
thanks for getting an idea. Your code looks very smart. I tried to do something like you with “historicState” but in javascript i didnt get this to work. historicState is not a function…

Then i tried to get an iteration about averages per every minute.
So the javascript code which is doing now what it should is slow but working:

debug=1;

var now = time.ZonedDateTime.now();

sum=count=0;
for (let i = 0; i < 1440; i++) {
  minavg=items.waermepumpe_cop.history.averageBetween(now.minusMinutes(i+1),now.minusMinutes(i))
  if (typeof minavg!=='undefined' && minavg>0){sum+=minavg;count+=1;}
}
var cop1d=sum/count;
(debug==1)?console.log("copsum/count ", sum + " " + count):null; 
(debug==1)?console.log("cop1d", cop1d):null; 

items.getItem("waermepumpe_cop1d").sendCommandIfDifferent(cop1d)

It may not have been added to the js helper library. @florian-h05 this was the PR

1 Like

yeah, that could be, but in actual Documentations its documented about “historicState”
https://www.openhab.org/addons/automation/jsscripting/#items
https://www.openhab.org/docs/configuration/persistence.html#persistence-extensions-in-scripts-and-rules

also the webgui-editor knows this if im writing it down (oh4.0.2) but i had no luck. Maybe it would be a good idea to add it to the helper library as you told or a little copy-paste example in doc how to use it which would it make easier to script with that.

historicState will give you just one data point, not a list of all data. You could loop through with that, but it might be slow since it’ll issue a separate query for each call.

Until it’s added to the helper library, you could call PersistenceExtensions directly.

getAllStatesSince and getAllStatesBetween does not appear to be added to the library yet.

Maybe we need a bit more detail here because unless something has changed, OH doesn’t save NULL and UNDEF to persistence. For these zero values the Item would be getting updated to 0. Which database is in use and can you confirm that it’s really saving 0 for NULL and UNDEF? That might be a bug because it’s not supposed to.

It should be: JavaScript Scripting - Automation | openHAB

How did you call it?

items.ItemName.history.historicState(timestamp, serviceId);

Note it returns a HistoricItem, not just the raw number. Maybe that was the problem? It works for me.

Note this means working with the raw Java API so paying attention to type is important.

var PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');

var values = PersistenceExtensions.getAllStatesBetween(items.waermepumpe_cop.rawItem, startTime, endTime); // This will be a Java  List of Java HistoricItem Objects
var jsValues = utils.javaListToJsArray(values); // Still Java HistoricItem Objects but at least it's a JS array now

var filtered = jsValues.filter( hi => hi.state.toString() != "0" ) // filter out the 0 values
                      .map( hi => parseFloat(hi.state.toString())); // get the states and convert to a JS number
var sum = filtered.reduce( (sum, value) => sum + value, 0);
var avg = sum /  filtered.length;

I just typed in the above, there is likely a typo or two.

Hi Rich,

Which database is in use and can you confirm that it’s really saving 0 for NULL and UNDEF?

im using influxdb persistence with influx 2 The Persistence Rule i entered for all is on change and 1 minute which i read long time ago on a manual for getting normal graphs and ON/OFF Switches correctly shown.
My cop-item im writing by another rule only if it is greater 0 to the item and applied an expiration timer to “UNDEF”

How did you call it?

var now = time.ZonedDateTime.now();
var test=items.waermepumpe_cop.history.historicState(now.minusMinutes(21));
console.log("xxx", test);

Result:
2023-08-30 16:51:40.005 [INFO ] [nhab.automation.script.ui.8ca32754a8] - xxx {
“rawState”: {},
“state”: “23.982974094665227”,
“numericState”: 23.982974094665227,
“quantityState”: {
“raw”: {}
},
“timestamp”: “2023-08-30T14:07:00.006+02:00[Europe/Zurich]”
}
Ok, i cannot remember why it was former not working, but i can confirm that this works in the way you told me.

I just typed in the above, there is likely a typo or two.

No, i did not noticed any typo.

debug=1;
var now = time.ZonedDateTime.now();

var PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');

var values = PersistenceExtensions.getAllStatesBetween(items.waermepumpe_cop.rawItem, now.minusMinutes(1440), now); // This will be a Java  List of Java HistoricItem Objects
var jsValues = utils.javaListToJsArray(values); // Still Java HistoricItem Objects but at least it's a JS array now

var filtered = jsValues.filter( hi => hi.state.toString() != "0" ) // filter out the 0 values
                      .map( hi => parseFloat(hi.state.toString())); // get the states and convert to a JS number
var sum = filtered.reduce( (sum, value) => sum + value, 0);
var avg = sum /  filtered.length;
(debug==1)?console.log("cop1d2", avg):null; 

The speed is about 0.1 seconds and the former variant through minutly looping was longer than 30 seconds! The difference is really a difference :wink: Thanks a lot!

But it gives me another (higher) Average. This is what i dont understand this time.

You can’t have your cake and eat it to. I find that if you want to “float” the value so you get nice continuous charts and you want to use persistence to calculate stuff or know when the last time an Item changed or was updated was is you really need two Items. One that is used for charting with a periodic charting period and the other which only saves on changes. You use the latter one to do stuff like calculating averages, lastUpdate, and stuff like that.

These are two different use cases and in many cases they cannot both be accommodated with the same persistence strategies.

This is kind of an awkward way to do debug logging.

You can use

console.debug()

and then adjust the logging level of the logger for that rule to INFO or above to suppress it or down to DEBUG or below to show the log statement in the logs.

You can change the logging level through the log4j2.xml file, karaf console, or the following at the top of your script:

osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');

You can set the logger name using

console.loggerName = 'org.openhab.automation.rules_tools.TimeStateMachine';

That’s kind of the whole point of logging levels.

I don’t know the values so I can’t say.

persistence-strategies: so i will think about my strategies and possibly separate to different strategies

debug: thats a nice possibility to learn from you switching the debugging and doing it! I will try it out

Average: tomorrow i will have hopefully better cop-base values and i will see then better if the average could match.

…i changed for cop and cop1d at first to get feeling the persistence only to on change and restore on startup. Debug on/off works fine and its a nice methode now for me.
Also the newly calculated cop1d seems to be a good average (the rightest point of green line)

Thank you again for the very fast script!

I filed an issue to add the two missing persistence actions to openhab-js and, if I can put together a couple hours on a real computer will submit a PR. Hopefully these will be in the next release of openhab-js.

1 Like