JS Scripting: Waiting for a script to finish

Running OH3.3-release with a couple newly migrated JS Scripting Rules and Scripts.

Is there a best practise way to

  1. run scripts from a rule
  2. wait for the script to finish
  3. then continue the rule?

I could just wait for a sec or 500msecs, but that doesn’t seem the best solution?

Depends on what you mean by “Script”. Unfortunately the term is way overloaded.

Do you mean a shell script using executeCommandLine? Then yes, add a Duration as the first argument and your rule will wait up to that amount of time for the command to return.

Do you mean calling another rule (the entries you see under “Scripts” in MainUI are just a special case of a rule that has only a single script action and no triggers or conditions, and tagged with “Script”)? In that case calling that other rule is already blocking. It will wait for the called rule to complete before returning (I’m like 90% sure of this but it occurs to me I never tested to confirm).

Do you mean loading a library and calling its functions? That too is blocking and it will only return when the called function is done.

Do you mean Rules DSL scripts called using the callScript action? That is blocking, no need to wait.

Actually no you can’t. One limitation of JS in GraalVM is that you can’t do multi-threaded stuff. A sleep is a multithreaded operation and if you tried to use it you’ll get an exception telling you it’s not allowed for JS. But there is almost never a case where you cannot replace a sleep with a timer.

1 Like

ATM I’d like to wait for another rule-script to finish.

var RuleManager = osgi.getService("org.openhab.core.automation.RuleManager");
RuleManager.runNow('EMS_CalcStatus');

And after calling the Rules-Script the rule almost certainly doesn’t wait for the script to finish - at least I can see some parallel log entries. No biggie yet, as I don’t 100% rely on the outcome of the script (yet), but would be nice to know how.
Perhaps setting a cached variable and put a while-loop until the variable changes to a defined value. I’d have to experiment, if there’s no best practice there.

Note, with JS Scripting you don’t need to pull the RuleManager yourself.

rules.runRule('EMS_CalcStatus');

Though I do notice that it somehow didn’t make it into the autogenerated add-on docs. It is in the reference docs though: rules - Documentation

Sounds like a Script isn’t the right approach for this then. You should move this to a library and call a function instead. Script rules really are not meant to be used as a replacement for library functions. If you need to wait for it to finish then, since I was wrong about the call blocking, you’ll never get it to work the way you want since you can’t sleep or wait.

Best practice in this case would be to put this code into a personal library instead of trying to call a Script rule. Failing that, setting a timer to wait and assume the called Script rule completed finished with the rest of the rule code would be the next choice.

1 Like

you mean like this:

does ist work with JS Scripting as well? like you said here: OH 3 Examples: Writing and using JavaScript Libraries in MainUI created Rules - #67 by rlkoshak

UPDATE:

If you wrap the action in the script in a function and a return-value and call that function with a variable declaration, it is serial.

Just to be clear:

rule:

RuleManager.runNow('EMS_CalcStatus');
// so something more
console.log("next after EMS_CalcStatus call");
...

and within the script:

// calculate first
console.info("INFO first: Status START-------------------------------");
var first = calculate("first");
console.info("INFO first: Status END-------------------------------", first);

// calculate second
console.info("INFO second: Status START-------------------------------");
var second = calculate("second");
console.info("INFO second: Status END-------------------------------", second);

function calculate(ww_typ) {
  // do something
  return "OK";
}

it’ll then work like this in the logs (instead of randomly getting calculations first and second in parallel):

2022-11-04 09:52:39.336 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO first: Status START-------------------------------
2022-11-04 09:52:39.345 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO first: calculate something:  178.9336733924352 2.0 0.9
2022-11-04 09:52:39.362 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO first: Status END------------------------------- OK
2022-11-04 09:52:39.364 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO second: Status START-------------------------------
2022-11-04 09:52:39.370 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO second: calculate something:  0.0 2.0 1
2022-11-04 09:52:39.373 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - INFO second: Status END------------------------------- OK
2022-11-04 09:52:39.393 [INFO ] [utomation.script.ui.EMS_CalcWWStatus] - next after EMS_CalcStatus call

So I can keep my “functions” in “Scripts”, so I don’t have to change the developemnt environment for it.

I’m not sure I understand what you are doing here now.

Yes, a function call is going to block until the function returns, but that’s not a script. In fact, what I proposed was creating functions like that in a personal library so they can be shared across rules.

It works but it’s slightly different depending on whether or not you will use third party libraries or not.

If you are on openHABian, you are using third party libraries because it installs openhab_rules_tools now (which has even more helpful libraries that implement many of the design patterns).

It takes just a tiny bit to set up.

  1. Create a folder somewhere.
  2. Add a file named “package.json” with contents along the lines of:
{
  "name": "rlk_personal",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
  1. Create your libraries. In my case I have alerting.js and utils.js. As you define your functions, put exports in front for those functions that will be used in your rules. For example, my utils.js library consists of only:
exports.hysteresis = function(curr, threshold, hyst) {
  var min = max - hyst;
  if(curr < min) return 'ON';
  else if(curr > max) return 'OFF';
  else return 'STAY';
}
  1. Now you need to export your libraries. Create an index.js and add an entry for each of your library fies.
module.exports = {
  get alerting() { return require('./alerting.js') },
  get utils() { return require('./utils.js') }
}
  1. tar up the folder and copy it to $OH_CONF/automation/js.
  2. Install it using npm install <filename>.tar where <filename> is the name of your library.

OK, that was a lot of work but there is good news. Now you can just edit the files in place ($OH_CONF/automation/js/node_modules/<filename>). You have to go through the above steps though because otherwise every time npm update is run, it will wipe out anything that isn’t “installed”.

NOTE: the above six steps are basically a really short NPM tutorial that I referred to in the post you linked to.

You’ll import your libraries using require. For example var { utils } = require('rlk_personal');. You’ll call the function using that imported namespace: utils.hysteresis(curr, thresh, hyst).

1 Like

Thanks for that! Really appreciated!

I accidentially created this use case as I wanted to check, if my “functions” are running smoothly or if there’s something “UNDEF” or “NULL”. So I put all my actions in JS-functions (within a “Rule” without a trigger (aka “Scripts” in OH3-UI)) and put return-conditions on it - and a side effect was, what I wanted to achieve in the first place: calling some code from various other rules - and ideally they’re running sequential, not parallel. That way I can access the “scripts” there fast and simple via OH3-UI.

Your suggestion for “real” functions in a personal library are great for either people using “old school”-text file configuration or have “ready”-functions, which are stable enough to not change frequently, while you’re using the UI for everything else.
I’ll take a look on it, if my latest “relaunch” of my openHAB-installation is kinda stable enough. Persently I have to much going on I’d like to add or change… :wink:

Or if you have function that needs to return something, like the result of a calculation or the like. You get nothing back when you call another rule from a rule.

1 Like