ECMAScript Edition 11 Error - no globals like 'items' populated

  • Platform information:
    • Hardware: Raspi 4
    • OS: Linux openhabian 5.10.63-v7l+ #1496 SMP Wed Dec 1 15:58:56 GMT 2021 armv7l
    • openHAB version: 3.2
    • add-ons: only “JSScripting” installed, no Groovy or JRuby
    • upgraded with openhabian-config - upgrade

Hi all,

after upgrading openhabian from 3.1 to 3.2 to test the pretty new Javascript support it appears that the “ECMAScript Edition 11” scripts are executable, but globals, like “items” are empty.
Tested that inside a rule or as standalone script, same same.

Errors:

  1. from events.log
2021-12-24 08:44:53.185 [WARN ] [.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.
  1. from UI create rule screen
Getting handler 'script.ScriptAction' for module '2' failed: Config parameter 'script' is missing in the configuration of module '2'.

Script:

console.log("Number of items. items.size: ", items.size);

// load all items with tag radiosender
var allStreams = items.getItemsByTag("radiosender");
console.log(allStreams.size ," items found with tag radiosender");

// iterate them
allStreams.forEach(updateStreamAndSwitch);

// start stream und toggle off all other switches
function updateStreamAndSwitch(item) {
  console.log(radioSender)
}

After executing the script I see in the log, that “items” is empty, even if there exist like 30 items, and 2 with the tag “radiosender”.

2021-12-24 08:44:57.337 [INFO ] [org.openhab.automation.script       ] - Number of items. items.size:  undefined
2021-12-24 08:44:57.391 [INFO ] [org.openhab.automation.script       ] - undefined " items gefunden mit tag radiosender"

Investigations:
I checked the thread in the github issues " GraalVM JavaScript replaces Nashorn, breaking Blockly" (GraalVM JavaScript replaces Nashorn, breaking Blockly and existing Nashorn rules · Issue #2433 · openhab/openhab-core · GitHub) and found that this is issue is fixed, since the heading of my script logs:

application/javascript;version=ECMAScript-2021

Anybody an idea where I could look to fix that?
How can I disable the old ECMA support, I anyhow want to switch to standard Javascript.

Items is empty, because the items of the new JS library is not the same as it was in in the old ECMA Engine (NashornJS).
In NashornJS, items allowed direct access to the items, but in the new engine (GraalJS), items is a namespace that provides functions. For more information about the items namespace, have a look at: JavaScript Scripting - Automation | openHAB or at the JSDoc (items - Documentation).

I have updated your script to work with GraalJS:

// items.size is NOT working anymore, as items in the helper library is a namespace providing functions
// and not the same as the items Object in NashornJS.
// console.log("Number of items. items.size: ", items.size);

// load all items with tag radiosender
var allStreams = items.getItemsByTag("radiosender");
// getItemsByTag returns an Array of Items, therefore use length for number of items
console.log(allStreams.length ," items found with tag radiosender");

// iterate them
// allStreams.forEach(updateStreamAndSwitch);
// For iterating over Items, you need the name from the Items Object.
for (i in allStreams) {
  updateStreamAndSwitch(allStreams[i].name);
}

// start stream und toggle off all other switches
function updateStreamAndSwitch(item) {
  // do stuff
}

Please note that items.getItemsByTag() does return an Array of Item objects. To get an item‘s name or state, you have to append .name or .state to the object.
Again, have a look at the docs: JavaScript Scripting - Automation | openHAB.

1 Like

Thanks Florian for you superquick resonse, even on Heiligabend :wink:

I tested that, your code works, but the “items” array is still empty, even if I use the “length” property.

Is there anything else I need to know or to change to make sure the globals are populated properly?

Test script (ECMA 11):

console.log("items.length: ", items.length);

var allStreams = items.getItemsByTag("radiosender");

// getItemsByTag returns an Array of Items, therefore use length for number of items
console.log(allStreams.length ," items found with tag radiosender");

Log output:

2021-12-24 14:42:40.482 [INFO ] [org.openhab.automation.script       ] - items.length:  undefined
2021-12-24 14:42:40.490 [INFO ] [org.openhab.automation.script       ] - 0 " items found with tag radiosender"

Nils, you are welcome, feel free to ask again when it is still unclear.

Yes, as far as I know, this is the new behaviour under ECMA 11.
In the old ECMA, items was an Array that provided access to Items, therefore you were able to get the number of Items through length/size.

But with ECMA 11 and the library, the items global has changed.

items is not the old Array anymore.
If you know the itemRegistry from the old ECMA, you can compare itemRegistry a little bit with the new items global.
Instead, the items global in ECMA 11 provides functions, most of them return an Item object (something new in ECMA 11). The Item object has properties like name, state, …
Have a look at JavaScript Scripting - Automation | openHAB

Example:

In the old ECMA you did something like that to access an Item‘s state:

if(items["MySwitch"] == OFF) {
  // do stuff
}

In ECMA 11, items is completely different, to access the state you have to use:

if (items.getItem("MySwitch").state === "OFF") {
  // do stuff
}

I think the globals are populated properly, but as mentioned before, the items global has changed.

1 Like

Migration is also discussed in Migrate from Nashorn to JSScripting for UI Rules

I think the @runtime provides backwards compatible items and such.

1 Like

Yes, you can get backward compatibility for items by using:

var { items } = require('@runtime');

But I‘d rather support people using the new JS library instead of directly importing the raw Java stuff.

2 Likes

Good morning, okay, thanks for your help, I understood now and was able to solve that issue.
You were right, Javascript works, and also the items namespace.

The reason why items.getItemsByTag() didnt return anything, was that my custom tag “radiosender” wasnt persisted.
Actually the items edit screen let you add tags, but after clicking on save they are not persisted (see screenshot below).
Edit: as noted below by @florian-h05 (many thanks!) I just missed pressing enter after entering the tag in the input box :blush:

Anyhow, I solved that for now by using the semantic-class “WebService” for all these items. This automatically sets WebService as a tag which can be selected like:

var allStreams = items.getItemsByTag("WebService");
console.log(allStreams.length ," items found with tag WebService");

Maybe a nice improvement would be to add a function items.getItemsByGroup().
Edit: wow, this is just supported by selecting the group item by name and then the members of it.
Here FYI the tested code example with the different loading and looping options:


// *** loading items ***

// Option a) load all stream items by group
var group = items.getItem("AudioStreams");
var allStreamsByGroup = group.members;
console.log(allStreamsByGroup.length ," descendent items found in group AudioStreams");

// Option b) load all stream items by tag WebService
var allStreams = items.getItemsByTag("WebService");
console.log(allStreams.length ," items found with tag WebService");

// *** iterating items ***

// Option 1) iterate them by function call
allStreams.forEach(updateStreamAndSwitch);
// start stream und toggle off all other switches
function updateStreamAndSwitch(item) {
  console.log(item.name, item.state, item.label)
}

// Option 2) iterate them by straight forward loop
for (i in allStreamsByGroup) {
  updateStreamAndSwitch(allStreamsByGroup[i]);
}
// start stream und toggle off all other switches
function updateStreamAndSwitch(item) {
  console.log(item.name, item.state, item.label)
}

Screenshot item edit:

Okay Perfect.

Please note that is also members which only returns the direct members of the group.
descendants returns all members of a group, therefore the members of child groups are also returned.

Have you tried pressing enter after typing your tag name in to the field and then clicking save?
On my system it works that way.

1 Like

Oh thanks again alot! Yes that works, I’ll update my post above with both aspects.

FYI:

The JSDoc of the library says:

members: Members / children / direct descendents of the current group item (as returned by ‘getMembers()’).

descendents: All descendents of the current group item (as returned by ‘getAllMembers()’).

https://openhab.github.io/openhab-js/items.Item.html#members