jpg0 / oh-config

Openhab Configuration
1 stars 2 forks source link

Testing the GraalJS package with OH3 #2

Closed LukasA83 closed 3 years ago

LukasA83 commented 3 years ago

hi @jpg0,

as discussed in separate thread, I tried your package, but basically I struggle on the installation, I believe.

Setup:

OH3 Build 2094 (docker debian) on Raspi, installed npm 6.14.10.

"npm i ohj" run successfully from $OPENHAB_CONF/automation/lib/javascript/community.

I created a test rule: `with(require('ohj').fluent){

//turn on the kitchen light at SUNSET
when(item("Test_Item_Switch").changed()).then(() => { logWarn("testwarn"); });

}`

Somehow it seems not to work, logs: 15:06:27.278 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/testGraalvm.js': org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'

jpg0 commented 3 years ago

So you've done everything right, however I don't yet have ohj fully working with OH3 yet.

So I would suggest to either:

Either way, I'll update this once ohj is working (shouldn't be too long, depends on my time during this period!).

LukasA83 commented 3 years ago

Right now I'm still using the javascript helpers provided for OH 2.5 with own Utils and same updates for OH3. Will it work to keep the set-up, maybe with minor adaptions to GraalJS? That would allow me a staggered migration ;-)

LukasA83 commented 3 years ago

Additional question: Going forward will I need to install npm to the Openhab docker image? That feels unhandy in terms of updates.

jpg0 commented 3 years ago

npm shouldn't be required in the docker image. It only downloads and places the JS files, so can do it from the host. It's not needed to run the downloaded JS.

jpg0 commented 3 years ago

As for migration; this is possible, but not as simple as you may expect (it's what I originally did, to try to keep compatibility, but ultimately abandoned). The reason is the global namespacing in the 2.5 helpers; almost all variables in the 2.5 helpers are dropped into the global namespace, whereas the pattern I use for 3.0 is the same as standard module-based JS, where they are put into a specific namespace. So for example:

With namespacing:

let mythings = require('mythings'); //or import scriptExtensions, same pattern
let doorItem = mythings.door;

Without namespacing:

load("../somecrazypath/mythings.js"); //or scriptExtension.importPreset("mythings");
let doorItem = door; //this was just retrieved from the global namespace

I tried to ease migration by explicitly copying namespaced properties into the global namespace to try to emulate the 2.5 approach. This mostly worked, but there were unexpected clashes and overrides all the time that were frustrating. Saying this, if your usage of the 2.5 helpers is simple, it could certainly work. At the very least - examples of how you migrate could certainly help others.

jpg0 commented 3 years ago

I have published a new version of ohj (3.0.0, to match the OH version that it is targeting). Whilst I have not tested it extensively, it looks to be working fine with OH3.

LukasA83 commented 3 years ago

Today I tried again. Following steps I did:

  1. Upgrade to latest openhab 3 snapshot docker image
  2. Copy jsscript jar from https://github.com/openhab/openhab-addons/files/5718546/org.openhab.automation.jsscripting-3.0.0-SNAPSHOT.jar.zip into addons/
  3. Remove any files in automation/
  4. Install latest ohj via npm
  5. created dummy rule as above w. filename "fritzbox.js"

ohj package.json indicates right version:

{
  "dependencies": {
    "ohj": "^3.0.0"
  }
}

Now I get this error: During startup: 12:29:11.409 [INFO ] [org.openhab.ui.internal.UIService ] - Started UI on port 50880, 12:29:10.531 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js', 12:29:10.526 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 20, 12:29:15.535 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started., 12:29:15.532 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 40, 12:29:15.529 [DEBUG] [enhab.core.service.StartLevelService] - Reached start level 30, 12:29:16.313 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': <eval>:65:51 Expected an operand but found ), when(item("Test_Item_Switch").changed()).then(() => { logWarn("testwarn"); });, 12:29:17.450 [WARN ] [ty.util.ssl.SslContextFactory.config] - Trusting all certificates configured for Client@7a6c3c46[provider=null,keyStore=null,trustStore=null], ^ in <eval> at line number 65 at column number 51

When changing the rule after startup:

`2:30:15.585 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js'

12:30:19.866 [WARN ] [.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.

12:30:20.509 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:

org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'

    at <js>.:program(<eval>:41) ~[?:?]

    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:51) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) [bundleFile:?]

    at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:170) [bundleFile:?]

    at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:191) [bundleFile:?]

    at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:157) [bundleFile:?]

    at org.openhab.core.service.WatchQueueReader.lambda$3(WatchQueueReader.java:322) [bundleFile:?]

    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) [?:?]

12:30:20.537 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'ohj'`
LukasA83 commented 3 years ago

I created another js file with some dummy set-up which works, so it seems I'm still struggling with the npm ohj package. Any idea what is going wrong? Dummy script, that works:

const GENERATED_RULE_ITEM_TAG = "GENERATED_RULE_ITEM";

const ModuleBuilder = Java.type("org.openhab.core.automation.util.ModuleBuilder");
const Configuration = Java.type("org.openhab.core.config.core.Configuration");
const LOGGER_PREFIX = "script.js";

let logger = Java.type("org.slf4j.LoggerFactory").getLogger(LOGGER_PREFIX);
//let RuleManager = osgi.getService("org.openhab.core.automation.RuleManager");
const { automationManager } = require('@runtime/RuleSupport');

/**
 * Creates a trigger. Internal function, instead use predefined trigger types.
 * 
 * @memberof triggers
 * @private
 * @param {String} typeString the type of trigger to create
 * @param {String} [name] the name of the trigger
 * @param {Configuration} config the trigger configuration
 */
let createTrigger = function(typeString, name, config) {

    return ModuleBuilder.createTrigger()
        .withId(name)
        .withTypeUID(typeString)
        .withConfiguration(new Configuration(config))
        .build();
}

/**
 * Creates a rule. The rule will be created and immediately available.
 * 
 * @example
 * import { rules, triggers } = require('ohj');
 * 
 * rules.JSRule({
 *  name: "my_new_rule",
 *  description": "this rule swizzles the swallows",
 *  triggers: triggers.GenericCronTrigger("0 30 16 * * ? *"),
 *  execute: triggerConfig => { //do stuff }
 * });
 * 
 * @memberOf rules
 * @param {Object} ruleConfig The rule config describing the rule
 * @param {String} ruleConfig.name the name of the rule
 * @param {String} ruleConfig.description a description of the rule
 * @param {*} ruleConfig.execute callback that will be called when the rule fires
 * @returns {HostRule} the created rule
 */
let JSRule = function (ruleConfig) {
    let ruid = ruleConfig.id || ruleConfig.name.replace(/[^\w]/g, "-") + "-";
    let ruTemplateid = ruleConfig.name.replace(/[^\w]/g, "-") + "-";
    logger.info("Adding rule: {}", ruleConfig.name ? ruleConfig.name : ruid);

    let SimpleRule = Java.extend(Java.type('org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule'));

    let doExecute = function (module, input) {
        try {
            return ruleConfig.execute(input);
        } catch (error) {
            logger.error(`Failed to execute rule ${ruid}: ${error}: ${error.stack}`);
            throw error;
        }
    };

    var rule = new SimpleRule({
        execute: doExecute,
        getUID: () => ruid
    });

    var triggers = ruleConfig.triggers ? ruleConfig.triggers : ruleConfig.getEventTrigger();

    rule.setTemplateUID(ruTemplateid);

    if (ruleConfig.description) {
        rule.setDescription(ruleConfig.description);
    }
    if (ruleConfig.name) {
        rule.setName(ruleConfig.name);
    }

    //Register rule here
    if (triggers && triggers.length > 0) {
        rule.setTriggers(triggers);
        rule = registerRule(rule);
    }

    return rule;
};

let currentProvider = automationManager;

let registerRule = function(rule) {
    return currentProvider.addRule(rule);
}

let testRule = JSRule({
    name: "new rule the old way",
    triggers: [
        createTrigger("timer.GenericCronTrigger","cron-trigger",{
            "cronExpression": "0/30 * * * * ? *"
        })
    ],
    description: "bla",
    execute: triggerConfig => {
        logger.warn("new rule trigger");
    }
});
LukasA83 commented 3 years ago

Ok I think I found one issue. In the readme you refer to install the npm package to the community folder, but it seems it has to be in the personal folder. Now I'm getting this error:

23:21:37.916 [WARN ] [script.js.osgi                      ] - Failed to get service org.openhab.core.items.MetadataRegistry: [object Error]     [osgi at source <unknown>, line 53]

23:21:37.931 [WARN ] [script.js.osgi                      ] - Failed to get service org.eclipse.smarthome.core.items.MetadataRegistry: [object Error]       [osgi at source <unknown>, line 53]

23:21:37.947 [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(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/osgi.js:71) ~[?:?]

    at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/metadata/metadata.js:13) ~[?:?]

    at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/items/managed.js:6) ~[?:?]

    at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/items/items.js:8) ~[?:?]

    at <js>.:anonymous(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/fluent/fluent.js:9) ~[?:?]

    at <js>.get fluent(/openhab/conf/automation/lib/javascript/personal/node_modules/ohj/index.js:12) ~[?:?]

    at <js>.:program(<eval>:41) ~[?:?]

    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:51) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocable.eval(DelegatingScriptEngineWithInvocable.java:51) ~[bundleFile:?]

    at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable.eval(InvocationInterceptingScriptEngineWithInvocable.java:78) [bundleFile:?]

    at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:170) [bundleFile:?]

    at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.importFile(ScriptFileWatcher.java:191) [bundleFile:?]

    at org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileWatcher.processWatchEvent(ScriptFileWatcher.java:157) [bundleFile:?]

    at org.openhab.core.service.WatchQueueReader.lambda$3(WatchQueueReader.java:322) [bundleFile:?]

    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) [?:?]

23:21:37.953 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/openhab/conf/automation/jsr223/javascript/personal/fritzbox.js': org.graalvm.polyglot.PolyglotException: Error: Failed to get any services of type(s): org.openhab.core.items.MetadataRegistry,org.eclipse.smarthome.core.items.MetadataRegistry
jpg0 commented 3 years ago

Sorry that you're having so many issues!

I can see a few causes here:

jpg0 commented 3 years ago

Also: what version of ohj do you have installed? You should be able to see this in the package.json file in the ohj directory. You want 3.0.0.

I ask this because it's failing to load MetadataRegistry, although I am not seeing that error.

jpg0 commented 3 years ago

Here's a build of ohj-support:

org.openhab.automation.ohj-support-3.0.0-SNAPSHOT.jar.zip

LukasA83 commented 3 years ago

I backtraced the error to the osgi.js. the bundleContext seems to be undefined.

jpg0 commented 3 years ago

Oh, and great news that the dummy script works - that signifies that the base JS + modules are working (plus I see it has ES6+ features in there like const, which would fail with the current JS engine). So it's just ohj which is the problem here.

LukasA83 commented 3 years ago

I've added the support addon, still bundleContext seems to be undefined in osgi.js. Any idea?

LukasA83 commented 3 years ago

I got it to work with changing the function "lookupService" in osgi,js like this (quick hack...):

let lookupService = function (classOrName) {
    var bc = bundleContext;
    if(bundleContext === undefined) {    
        log.warn("bundleContext is undefined");
        var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
        var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
        bc = (_bundle !== null) ? _bundle.getBundleContext() : null;
    }
    if (bc !== null) {
        var classname = (typeof classOrName === "object") ? classOrName.getName() : classOrName;
        var ref = bc.getServiceReference(classname);
        return (ref !== null) ? bc.getService(ref) : null;
    }
}
jpg0 commented 3 years ago

Great! Thanks for letting me know. It looks like you are not resolving require('@runtime/osgi').bundleContext for some reason (it's supplied in ohj-support: https://github.com/jpg0/ohj-support/blob/master/src/main/java/org/openhab/automation/module/script/extension/OSGiScriptExtensionProvider.java).

Either way, I wouldn't worry about it if I were you - your hack should work fine and I'll try to figure out why the standard version is not resolving. ohj is certainly much less stable than the core JS scripting stuff at this point.

LukasA83 commented 3 years ago

I'll use ohj as a starting point and combine with own scripts. For example, I've added most of the other OH triggers and conditions to the helpers... Are there already helpers in your package to schedule timers, like setTimeout in the former js helper?

simonihmig commented 3 years ago

For the record: I was now able to sort out the issues I reported in https://github.com/jpg0/ohj/issues/19 (basically a duplicate of this issue) with these steps:

I have just a limited set of rules defined with JS for now, but at least they are working now again w/ OH3!

jpg0 commented 3 years ago

I've merged https://github.com/jpg0/ohj/pull/20 and also included the fix from @LukasA83. Still not really sure why it was failing, but I'm guessing it's something related to the startup ordering issues in OH3.