Usage of new ECMA2021 Automation Scripting (GraalVM)

As the lastest OH3.1 snapshot officially supports ECMA2021 Java Scripting I am wondering how to use the new functionality.

Until now I do use the syntax of the OH scripters to watch an item and fire the rule:

function rule_test(event) {
    // Operations when rule is fired 
}
when("Item HO_Date changed")(rule_test);
rule(
    "Rule Test",
    "No desc"
)(rule_test);

Can i simply use this scripting with the new ECMA2021 Automation Add on?

Because I tried to and got the following error in the log:

2021-05-24 00:39:10.782 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: ReferenceError: "when" is not defined
	at <js>.:program(/etc/openhab/automation/jsr223/javascript/personal/graal_test.js:33) ~[?:?]
1 Like

It is highly unlikely that the existing Helper Libraries will work as is. I’m sure there will be some incompatibilities and breaking changes between the various versions. You’ll have to create rules using the raw openHAB Scripting API, an example of which you can find at JSR223 Scripting | openHAB. Notice how you need to create a Rule class which defines an execute function and then you need to create and add the rule triggers to that class using a TriggerBuilder.

Thanks for your help.

Doing so (using the mentioned Raw JS example) I get the following entry in the log:

2021-05-24 18:51:27.461 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/javascript/personal/graal_test_js.js': org.graalvm.polyglot.PolyglotException: ReferenceError: "scriptExtension" is not defined

Any ideas how to proceed?

Or does someone do have a running ECMA2021 example script?

I know that @jpg0 tends to error on the side of not including things into the rule rather than injecting references to things that would be of use just in case. I suspect you’ll have to load something or do a Java.type to bring in access to a lot of things, though I don’t know how that would work for something like the scriptExtension since I think that is how we gain access to the OH internals like the ItemRegistry and the like.

Over all, I suspect we’ll need to wait for some examples and docs to explain how to access these OH specific things and use this for rules.

Apologies, but I’m late adding docs for the new engine. @rlkoshak is correct, things are not injected, this is to preserve compatibility with 3rd parties libraries which would not expect them to be there.

If you want to bring in an object which was previously included by default, you can import it like so:

const myObject = import('@runtime').myObject (or you can use destructuring to reference multiple of them, etc)

(You are also welcome to copy/use/whatever code from my personal repo which has plenty of new JS code: oh-config/automation at master · jpg0/oh-config · GitHub)

3 Likes

Thanks for the clarification @jpg0

In your personal examples you are usuallyy use the “ohj” npm package:

const { rules, items, triggers } = require('ohj');

Has this package in any case to imported via npm? Or is there also another way to use this?

Also is there aa possibility to use the Helper Libraries within the new jsscripting add on?

As said, a more detailed documentation with script examples would be highly appreciated.

Thanks!

Has this package in any case to imported via npm?

This package is available on npm if you want to use it (you can install with npm if you like, or just directly from GitHub, just make sure it goes into the lib directory in openHAB). This is my own library which I wrote to replace the helper libraries; you’re welcome to use it (others do), but it’s not official or anything.

Also is there aa possibility to use the Helper Libraries within the new jsscripting add on?

You may be able to use the existing helper libraries, but they would require rework. There are a few issues I had with them:

  • They rely on things being injected into the context by the runtime (which doesn’t happen as you now know)
  • They also rely on setting things into global scope, where one lib may set it and another read it. This isn’t a good way to do things, and isn’t easy to do with a module system which deliberately tries to keep things isolated
  • They didn’t use a module system, nor any ES6+ features, as they were not available.

This is what led me to write ohj - I did attempt to port the helper libraries (e.g. allowing loading in both mechanisms) but ultimately it was too hard to keep compatibility to I completely rewrote them into ohj.

(I accept that we will probably need a new version of the helper libraries - I’m happy for that to be ohj, but it currently makes no attempt to mirror the python ones or anything.)

@jpg0 I cloned your ohj repo to the folder “/etc/openhab/automation/lib/javascript/personal/ohj” and tryed to use it with a modified example from your personal rule:

const log = require('ohj').log("minimal_test");
const { rules, triggers, items } = require('ohj');

//create a rule to handle scenes
rules.JSRule({
    name: "Roller Scenes",
    description: "Applies selected scenes to Rollers",
    triggers: [
        triggers.ItemStateChangeTrigger('RemoteopenHABServerPROD_ItemSFStudioLichtDecke')
    ],
    execute: function () {
        let status = items.getItem('RemoteopenHABServerPROD_ItemSFStudioLichtDecke').state;
        log.info("Setting scene for rollers: {}", status);
    }
});

No the log throws the following error:

2021-05-25 15:03:18.716 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'

How can i fix this?

Thanks!

1 Like

@jpg0 with the following code it seems that I was able to load the ohj package via require:

const ohj = require("./../../../lib/javascript/community/node_modules/ohj");
let rules = ohj.rules;

But now the log throws the following:

2021-05-26 07:19:43.485 [DEBUG] [rt.internal.loader.ScriptFileWatcher] - Dequeued file:/etc/openhab/automation/jsr223/javascript/personal/first.js
2021-05-26 07:19:43.487 [INFO ] [rt.internal.loader.ScriptFileWatcher] - Loading script '/etc/openhab/automation/jsr223/javascript/personal/first.js'
2021-05-26 07:19:43.490 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'js' with identifier: file:/etc/openhab/automation/jsr223/javascript/personal/first.js
2021-05-26 07:19:43.668 [WARN ] [script.js.osgi                      ] - Failed to get service org.openhab.core.items.MetadataRegistry: [object Error]		[osgi at source <unknown>, line 53]
2021-05-26 07:19:43.675 [WARN ] [script.js.osgi                      ] - Failed to get service org.eclipse.smarthome.core.items.MetadataRegistry: [object Error]		[osgi at source <unknown>, line 53]
2021-05-26 07:19:43.681 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: Error: Failed to get any services of type(s): org.openhab.core.items.MetadataRegistry,org.eclipse.smarthome.core.items.MetadataRegistry
	at <js>.getService(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/osgi.js:71) ~[?:?]
	at <js>.:anonymous(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/metadata/metadata.js:13) ~[?:?]
	at <js>.:anonymous(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/items/managed.js:6) ~[?:?]
	at <js>.:anonymous(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/items/items.js:8) ~[?:?]
	at <js>.:anonymous(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/rules.js:11) ~[?:?]
	at <js>.get rules(/etc/openhab/automation/lib/javascript/community/node_modules/ohj/index.js:13) ~[?:?]
	at <js>.:program(/etc/openhab/automation/jsr223/javascript/personal/first.js:30) ~[?:?]
	at org.graalvm.polyglot.Context.eval(Context.java:345) ~[?:?]
	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:379) ~[?:?]
	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:343) ~[?:?]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249) ~[java.scripting:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:56) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:79) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:56) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:79) ~[?:?]
	at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:172) ~[?:?]
	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:214) ~[?:?]
	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.lambda$1(ScriptFileWatcher.java:193) ~[?:?]
	at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFileWhenReady(ScriptFileWatcher.java:191) ~[?:?]
	at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:168) ~[?:?]
	at org.openhab.core.service.WatchQueueReader.lambda$3(WatchQueueReader.java:322) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:834) [?:?]

Any advice how to get this run would be great!

Thanks!

When you install ohj, it must go in lib, however in a subdir of it called node_modules.

(Not sure if that will fix your other problem, but give it a go.)

@jpg0 the ohj package was installed (with “npm i ohj”) into the “/etc/openhab/automation/lib/javascript/community” folder:

[12:39:24] openhabian@ubuntu:/etc/openhab/automation/lib/javascript/community/node_modules$ ll
insgesamt 536K
drwxr-xr-x 129 openhabian openhab 4,0K Mai 25 21:33  ./
drwxr-xr-x   3 openhabian openhab 4,0K Mai 25 22:01  ../
drwxr-xr-x   6 openhabian openhab 4,0K Mai 25 21:33  acorn/
...
drwxr-xr-x   6 openhabian openhab 4,0K Mai 25 21:33  ohj/
...

However I get the above mentioned error in the log “org.graalvm.polyglot.PolyglotException: Error: Failed to get any services of type(s): org.openhab.core.items.MetadataRegistry,org.eclipse.smarthome.core.items.MetadataRegistry”

So something seems to be wrong, I do have a clean automation/* installation there are no other *.js files within except the mentioned “node_modules” package and two test scripts within the folder “/etc/openhab/automation/jsr223/javascript/personal”.

Here is the fix: Testing the GraalJS package with OH3 · Issue #2 · jpg0/oh-config · GitHub - specifically the bundle context part I believe. Although I thought I had merged that already; maybe not. Note that you will likely require the ohj-support bundle too (which is referenced in that comment chain).

@jpg0 I got it now to run, things I did:

  1. Installed ohj-support Add-on from https://github.com/jpg0/oh-config/files/5745369/org.openhab.automation.ohj-support-3.0.0-SNAPSHOT.jar.zip
  2. A fresh git clone of GitHub - jpg0/ohj: Openhab Javascript Library into “/etc/openhab/automation/lib/javascript/community”
  3. Changed the mentioned function “lookupService” to the fixed one Testing the GraalJS package with OH3 · Issue #2 · jpg0/oh-config · GitHub

After a restart it worked.

What I am wondering is that I do have to use always relative paths for loading the ohj package via require:

const { item, sendIt } = require("../../../lib/javascript/community/ohj/fluent/fluent");
const ohj = require("./../../../lib/javascript/community/ohj");
const LOG = ohj.log("second");
const fluent = ohj.fluent;

with (fluent) {
    when(cron("0/30 * * * * ?")).then(sendOff().toItem("RemoteopenHABServerPROD_ItemSFStudioLichtDecke"));
}

When I am going through your personal git repo you always use “
require(‘ohj’);”.

Why is that not working on my setup?

Also I am wondering if there is something like the “event.itemState” property like it is available in the definition of “classic” oh scripters ECMA5 rules (when using JSRule with ohj)?

Thank you very much for your help!

Why is that not working on my setup?

Hmm, looking at the current implementation, you need to put ohj in personal, not community. The problem is that node cannot use multiple library paths, so I hardcoded it to this. TBH I believe that the right option is to not use ‘lib’ at all and instead to follow the node standard of a relative ‘./node_modules’ folder (hence within the jsr233 folder), but this deviates from openHAB current, and requires some more work.

Also I am wondering if there is something like the “event.itemState” property like it is available in the definition of “classic” oh scripters ECMA5 rules (when using JSRule with ohj)?

Looking at the code, for the fluent API, there is something that is defined for rules that trigger on commands send to items; you can see some of my code here: oh-config/automation/jsr223/javascript/personal/fluoro_lights.js at master · jpg0/oh-config · GitHub

The fluent API was very much to satisfy what I have needed for bulk rules, and even then for more complex rules I’ve switched to the JSRule style rule declaration, so I expect there to be missing features. Saying that, I’m happy to accept PRs if you want to add any features.

1 Like

Thank you very much, now I know how to import services like itemRegistry and events.

But how can I import:

  • types, as I would like to do the HSBType.fromRGB(r, g, b) conversion
  • openHAB core actions, like Exec.executeCommandLine(), HTTP actions and NotificationAction
  • Java classes from openHAB, e.g. org.openhab.core.model.script.actions.Exec — I can import normal Java classes like java.time.Duration, but importing openHAB classes causes errors like:
org.graalvm.polyglot.PolyglotException: TypeError: Access to host class org.openhab.core.model.script.actions.Exec is not allowed or does not exist.
1 Like

@florian-h05

Works in my ECMA2021 scripts

const screx = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
const ohj = require('./../../../lib/javascript/community/node_modules/ohj');
const actions = ohj.actions;
...
actions.Exec.executeCommandLine("/path/to/script.sh");
1 Like

I am facing exactly the same error
i tried with a quite simple rule

//'use strict';

//const LoggerFactory = Java.type('org.slf4j.LoggerFactory');
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.rule." + ctx.ruleUID);

logger.info("Hello world!");
logger.info(JSON.stringify(ctx));

//var Exec = Java.type("org.openhab.core.model.script.actions.Exec");
//const JavaThingBuilder = Java.type('org.openhab.core.thing.binding.builder.ThingBuilder');
var ZonedDateTime = Java.type("java.time.ZonedDateTime");  
var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
//  timer = createTimer(now.plusMinutes(3), function() { logger.info("Hello from Timer") })
timer = ScriptExecution.createTimer(ZonedDateTime.now().plusSeconds(10), function() { logger.info("Hello from Timer") });

but as soon as it comes to the Java.type("org.openhab... line i get a error, other types can be imported, also i observed that my ctx.ruleUID is undefinded

23:54:11.323 [INFO ] [openhab.event.RuleUpdatedEvent       ] - Rule '334a42598c' has been updated.
23:54:12.367 [DEBUG] [ript.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'application/javascript' with identifier: 402f3e2d-dcb4-4392-987b-893844574a3e
23:54:12.427 [WARN ] [g.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.
23:54:12.453 [INFO ] [org.openhab.rule.undefined           ] - Hello world!
23:54:12.465 [INFO ] [org.openhab.rule.undefined           ] - {}
23:54:12.487 [ERROR] [ab.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: Access to host class org.openhab.core.model.script.actions.ScriptExecution is not allowed or does not exist.
        at <js>.:program(<eval>:13) ~[?:?]
        at org.graalvm.polyglot.Context.eval(Context.java:345) ~[?:?]
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:379) ~[?:?]
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:356) ~[?:?]
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) ~[java.scripting:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[?:?]

in the meanwhile i read so many post here, but nothing really helped me.
i reinstaled the jsscript automation i restarted/rebooted but nothing helped
(runinning an openhabian on a RPI)

5 Likes

for what it’s worth, I have the same issue.

There are reports (@opus at openHAB 3.1 Release discussion - #8 by opus) that reboot should help but it did not help on my side.

2 Likes

Did you followed the steps from my post?

I haven’t


Is that needed even if I don’t use ohj? I am simply trying to use new ecmascript as an “action” in UI rules