Global functions or includes for JSR223 script utility functions

Is it possible to include non-rule scripts for utility purposes with JSR223 rules?

For example I am writing some rules in javascript and would like to use some useful functions from common libraries like underscore js or even just a file full of useful functions I have written myself.

It seems that the JSR223 plugin won’t allow any .js files without getRules defined and I can’t seem to find a way to share functions between files.

Trying to use the nashorn load(“filename”) functionality just results in an exception “TypeError: Cannot load script from myscript.js in at line number 4”.

I guess I could put everything in one really long file but that would be inconvenient.

Is there a proper way to do this?

I can’t answer your question for Nashorn specifically, but I do this extensively with Jython. I don’t put utility scripts in the scripts directory for the reason you mention ( getRules requirement) but I put them in another directory (could be a subdirectory of scripts) and use Jython’s import mechanism to load the utility modules.

1 Like

I was trying to load lodash.js in my rules js file. I placed lodash.js in js subfolder of the scripts folder (\OpenHab\configurations\scripts\js) to prevent the getRules exception.
I tried calling load('js/lodash.js') but I got javax.script.ScriptException: TypeError: Cannot load script from js/lodash.js in <eval> at line number 16.

Then I tried to load it from a cdn server like this:
load('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.js') and it worked without any error.

I also have a local webserver which is using lodash.js as well so I tried:
load('http://localhost/ha/js/lib/lodash.js') and that was working as well (and it obviously loaded faster)

Then I played a little with jjs.exe from JRE and it was loading lodash.js from a local folder just fine. So then I tried to use absolute paths in my rules.js like this:
load('c:/OpenHab/configurations/scripts/js/lodash.js') and that was finally working too.

So that made me realize that the scripts folder is not used as the base path when calling load('js/lodash.js'). I found that the base path is the OpenHab folder. So if you want to use relative paths you have to call it like this:

load('configurations/scripts/js/lodash.js')

I have not find that information anywhere else so hopefully it will be useful to somebody.

3 Likes

Hey steve1, would you mind giving a quick example? I tried importing from a subdirectory but i did not work.

Check the Python sys.path. Jython module import is mostly like CPython, but there are quirks with importing using wildcards. If you are doing that, check the Jython docs for more details.

Hi, I have been playing around with jsr223 script loading in openhab2.3 and came up with the following results, in case somebody finds them useful:

  1. My (automatically loaded) js scripts are in …\conf\automation\js223. It appears they each have their own context, so variables declared in one are not seen in another file.
  2. My additions scripts are in …\conf\scripts\js and are loaded with load(“./…/conf/scripts/js/…”)
  3. I use openhab2-javascript/jslib at master · lewie/openhab2-javascript · GitHub to make js rules simple. However, I placed them in a directory outside \conf\automation\jsr223 to prevent autoloading. Instead they are now in \conf\scripts\js\libs. I modified the path in these files accordingly. This way I can define a rule and use in it (or ouside) getItem to access items direcly and postUpdate to post an event.
  4. For times, I use a nashorn polyfill (setTimeout for Nashorn · GitHub) and load it with
    load(‘./…/conf/scripts/js/libs/settimeout-nashorn.js’);
    Here is a simple example:

‘use strict’;
print(“example.js loaded”);

load(‘./…/conf/scripts/js/libs/JSRule.js’);

load(‘./…/conf/scripts/js/libs/settimeout-nashorn.js’);

var x = 17;

JSRule({
name: “My JS Rule”,
description: “Line:”+ __LINE__
triggers: [
UpdatedEventTrigger(“TestSwitch1”)
],
execute: function( module, input){
print(“x=” + typeof x);
print(“y=” + typeof y);

    setTimeout(function() {events.postUpdate(getItem("TestSwitch2"), OFF)}, 0);
    setTimeout(function() {events.postUpdate(getItem("TestSwitch2"), ON)}, 3000);
    setTimeout(function() {events.postUpdate(getItem("TestSwitch2"), OFF)}, 6000);
}

});

The variable y could for instance be defined in a separate script in the automation/jsr223 file and is therefore not defined here.

I hope this helps. If I did something conceptionally wrong or could be done better, please let me know.

Addon/Edit:
I am no longer using a timer polyfill for nashorn, but rather the action.timer from openhab itself (I didn’t see it earlier). Using the above jslibs, you can use createTimer just as in normal rules…
Thanks,
Daniel

1 Like