JavaScript rules and item history after migrating from OH3 to OH4

I migrated my openHAB setup from version 3.4 to 4 in July. Since this upgrade, several JavaScript-flavoured rules no longer work.

I know there were breaking changes to openHAB 4 and JavaScript rules, but I run into problems updating my scripts.

First thing I see is that the flavour of ECMAAscript or JavaScript needs to be updated. Easy to solve in the script editor in MainUI. Then comes the tricky part where I am stuck.

I realised that items.getItem('ITEM_NAME_HERE') no longer works, and instead I should write the shorthand version items.ITEM_NAME_HERE.

When I want to access the item’s history, I used to be able to write items.getItem('ITEM_NAME_HERE').history.minimumSince(time.toZDT().withHour(0).withMinute(0).withSecond(0).withNano(0)) but this no longer works, nor does the following: items.ITEM_NAME_HERE.history.minimumSince(time.toZDT().withHour(0).withMinute(0).withSecond(0).withNano(0))

In addition, item states can return a Quantity in OH4 and apparently there’s a toUnit() method available… which isn’t: items.ITEM_NAME_HERE.state.toUnit('alternative_unit') returns Error: 'toUnit' is not a function.

Here’s an example rule which I am struggle with to get working again:

configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: SolarInverter_Energy_Total
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript
      script: >-
        var midnight =
        time.toZDT().withHour(0).withMinute(0).withSecond(0).withNano(0);
        var eTotal = items.getItem('SolarInverter_Energy_Total').rawState.floatValue().toFixed(6) * 1000; // MWh -- kWh
        // var eToday = items.getItem('SolarInverter_Energy_Today').rawState.floatValue().toFixed(3); // kWh
        var eTotalAtMidnight = items.getItem('SolarInverter_Energy_Total').history.minimumSince(midnight).toFixed(6) * 1000; // MWh -- kWh

        /*
        console.log('Midnight = ', midnight);
        console.log('ETotal = ', eTotal, ' kWh');
        console.log('ETotal at start of day = ', eTotalAtMidnight, ' kWh');
        console.log('EToday = ', eToday, ' kWh');
        console.log('EToday (computed) = ', (eTotal - eTotalAtMidnight).toFixed(3), ' kWh');
        */

        items.getItem('SolarInverter_Energy_Today').postUpdate((eTotal - eTotalAtMidnight).toFixed(3) + ' kWh');
    type: script.ScriptAction

Can you elaborate on this a little bit? What version of JS are you working with here, Nashorn (ECMAScript 5.1) or GraalVM (ECMAScript 11+)? The syntax looks like GraalVM but it’s not clear.

items.getItem() is still valid in GraalVM JS. Indeed, two short hand ways to get an Item were added in OH 4 but the old way still works too. items['NameOfItem] items.NameOfItem`

However, in Nashorn only items['NameOfItem'] is supported, hence my confusion.

These are both still valid in GraalVM but not in Nashorn. A simplified version of that line would be

items.ITEM_NAME_HERE.history.minimumSince(time.toZDT("09:00"));

The whole purpose of time.toZDT() is to avoid all that plusThis and withThat stuff. Check the docs for everything that’s supported.

In GraalVM JS, the stateof the Item has always been a String. In OH 4 numericState and quantityState were added to Item to get the state as a number or Quantity so you don’t have to parse it yourself. If you get the quantityState you can call toUnit on that, but not numericState nor state.

However, in Nashorn, the state of the Item is the raw Java State Object which does have a toUnit() on it.

items.ITEM_NAME_HERE.quantityState.toUnit('alternative_unit');

OK, that definitely means it will be using GraalVM.

        var midnight = time.toZDT("00:00"); // midnight today
        var eTotal = items.getItem('SolarInverter_Energy_Total').quantityState.toUnit('kWh');
        // var eToday = items.getItem('SolarInverter_Energy_Today').quantityState.toUnit('kWh');
        var eTotalAtMidnight = items.getItem('SolarInverter_Energy_Total').history.minimumSince(midnight).quantityState.toUnit('kWh');

        /*
        console.log('Midnight = ', midnight);
        console.log('ETotal = ', eTotal);
        console.log('ETotal at start of day = ', eTotalAtMidnight);
        console.log('EToday = ', eToday);
        console.log('EToday (computed) = ', (eTotal.minus(eTotalAtMidnight));
        */

        items.getItem('SolarInverter_Energy_Today').postUpdate((eTotal.minus(eTotalAtMidnight)).toString());

The above should work with the modifications I added. If not, double check that you are not running with an old version of the helper library under conf/automation/js/node_modules. If you are, you need to updated that with npm update or remove it so that the newer version of the library that comes with the add-on is used.

2 Likes

Thanks for your clear explanation.

I am indeed using GraalVM (built-in with openHAB 4).

I realised that there were permission errors which prevented npm update to work properly. After running sudo openhab-cli reset-ownership I reinstalled the js library and helper libraries through openhabian-config (menu items 46 and 47) which executed successfully.

I then restarted my Raspberry Pi after updating OpenJDK 11 (headless) to version 11.0.21.

Your updated script seems to address a lot of issues. The last remaining issue seems to be eTotal.minus():

org.graalvm.polyglot.PolyglotException: TypeError: eTotal.minus is not a function

I suppose that this is due to state of an Item being a String in GraalVM.

To make the script work, I converted the item states from String to floats with 3 decimals by means of parseFloat("State as String").toFixed(3):

var midnight = time.toZDT("00:00"); // midnight today
var eTotal = parseFloat(
  items.getItem('SolarInverter_Energy_Total').quantityState.toUnit('kWh')
).toFixed(3);
var eTotalAtMidnight = parseFloat(
  items.getItem('SolarInverter_Energy_Total').history.minimumSince(midnight).quantityState.toUnit('kWh')
).toFixed(3);

var eToday = (eTotal - eTotalAtMidnight).toFixed(3);

/*
console.log('Midnight = ', midnight);
console.log('ETotal = ', eTotal, 'kWh');
console.log('ETotal at start of day = ', eTotalAtMidnight, 'kWh');
console.log('EToday = ', eToday, 'kWh');
*/

items.getItem('SolarInverter_Energy_Today').postUpdate(eToday + ' kWh');

I should have looked it up first instead of going from memory. It’s “subtract”, not “minus”.

Excellent! Now the following works:

var midnight = time.toZDT("00:00"); // midnight today
var eTotal = items.getItem('SolarInverter_Energy_Total').quantityState.toUnit('kWh');
// var eToday = items.getItem('SolarInverter_Energy_Today').quantityState.toUnit('kWh');

var eTotalAtMidnight = items.getItem('SolarInverter_Energy_Total').history.minimumSince(midnight).quantityState.toUnit('kWh');
var eToday = eTotal.subtract(eTotalAtMidnight);

/*
console.log('Midnight = ', midnight);
console.log('ETotal = ', eTotal);
console.log('ETotal at start of day = ', eTotalAtMidnight);
console.log('EToday = ', eToday);
*/

items.getItem('SolarInverter_Energy_Today').postUpdate(eToday.toString());```