How to reload all MainUI rules?

I’m using javascript as the main language for my rules. I’ve created a personal library with commonly used code. When I change functions in this library I have to reload the rule using this function in order to get the change applied. So far so good. But when the library is used in other rules, these rules fail to execute and I receive various errors in the log, like:

 failed: org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: '@runtime'

or 

failed: org.graalvm.polyglot.PolyglotException: ReferenceError: "__wraprequire__" is not defined

or 

failed: org.graalvm.polyglot.PolyglotException: TypeError: initConsole is not a function

I have to save or enable/disable these affected rules too. And that is a pain, because I have a lot of rules and do not know exactly in which rule the library is included. Is there a way to automatically force reload all rules (without restarting openhab entirely)? Or is there a script that can do something like that?

I still hope there is another way to do this, but in the meantime I wrote a small rule (manual trigger) that does the trick.

  // ---------------- Imports
  const { rules } = require('@runtime');
  const RuleManager = osgi.getService('org.openhab.core.automation.RuleManager');
  
  // ---------------- Logger settings
  console.loggerName =  "org.openhab.rule." + ctx.ruleUID;

  // ---------------- Script Configuration Settings  
  
  /// Rules that should never be reinitialised
  const exclude_from_reinit = '[rule_reinit_enabled_rules, delay_start_rules]'.replace('[','').replace(']', '').split(', ');
  
  // ---------------- Functions
  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  // -------- Main ------- 
  
  var allRules = utils.javaSetToJsArray(rules.getAll());
  allRules.forEach( (i) => {
    const ruleUID = i.getUID().toString();
    //console.info("Processing rule's UID = ", ruleUID);

    if (!exclude_from_reinit.includes(ruleUID) ) {
      
      if ( RuleManager.isEnabled(ruleUID) ) {
        console.info("Rule " + ruleUID + " is enabled - reininitialising");
        RuleManager.setEnabled(ruleUID,false);
        sleep(200).then(() => { RuleManager.setEnabled(ruleUID,true); });
      } else {
        console.info("Rule " + ruleUID + " is not enabled - skipping");
      }
    } else {
      console.info("Rule " + ruleUID + " is excluded from reinitialising by configuration in the rule 'rule_reinit_enabled_rules' - skipping");
    }

  });

I imagine you can restart the jsscripting bundle by issuing a restart command through the karaf console or by uninstalling and reinstalling the JS Scripting add-on in the add-on store. That should cause a reload of all the rules I think.

Ultimately I’m not sure this isn’t a regression. I think it used to reload a library imported using require. it definitely should not start throwing unrelated errors. An issue on the JS. Scripting add-on might be warranted.

If you are using JS Scripting and not Nashorn, why are you importing raw Java stuff from @runtime?.A lot of work had gone into openhab-js to make sure you have a pure JS environment to work in.

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

  // ---------------- Logger settings
  console.loggerName =  "org.openhab.rule." + ctx.ruleUID;

  // ---------------- Script Configuration Settings  
  
  // Rules that should never be reinitialised
  var exclude_from_reinit = ["rule_reinit_enabled_rules", "delay_start_rules"];
  
  // -------- Main ------- 
  
  var allRules = utils.javaSetToJsArray(RuleManager.getAll());

  allRules.map( r => r.getUID())
           .filter( r => !exclude_from_reinit.includes(r) )
           .filter( r => rules.isEnabled(r)
           .forEach( r => {
             rules.setEnabled(r, false);
             Java.type("java.lang.Thread").sleep(200);
             rules.setEnabled(r, true);
           });

I just typed in the above on my phone so it almost certainly has typos. But hopefully you get the idea.

Are you routinely importing from @runtime in your rules? Are the rules that are throwing errors importing from @runtime?

I think you are right with the restart commands, but I wanted to have an easy fix, that is why I wrote a rule.

As for the importing Java stuff: I’m a total noob programming openhab. I don’t feel very comfortable posting issues to the source code. Most of the time it will be my own lack of knowledge.

To get this working I just grabbed some stuff from the forum and copied and paste it together. Got the job done! :slight_smile: I’m still a little bit in the dark with all the differences between Java / Javascript / Nashorn / JS Scripting etc. and when to include things from runtime and when i shouldn’t.

For this rule tried the option that you described (RuleManager from osgi.getService). However, I got an error when I invoked the getAll function, that is why I imported the rules variable from @runtime.

failed: org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (setEnabled) on org.openhab.core.automation.internal.RuleRegistryImpl@169b78e5 failed due to: Unknown identifier: setEnabled

I like the .map function though! Learned something new!

For the most part you should pretty much only be meeting with JavaScript. OH is written in Java so there are some rare cases where a Java Object might pop up (this is usually called out in the docs). But great possible have been taken to minimize that to a minimum.

When in doubt, the first place to look should be JavaScript Scripting - Automation | openHAB. These reference docs are pretty thorough and complete.

If you are looking at an old example, it might be Nashorn JS but that version of JS is 5.1. It’s ancient, not supported and does very little to help you along.

If that’s the error, it’s not from the get all, it’s from the set enabled.

And indeed, if you’ve continued to override rules from openhab-js which presents a pure JS interface to the RuleRegistry with your own variable it’s going to fail with that error.

This is why it’s important not to just import random stuff from @runtime and assign them to variables. items, rules, things and variable names like that are already taken by openhab-js and if you overwrite then you bow away your ability to use openhab-js to interact with OH. you are on your own.

Unfortunately I’m this case openhab-js doesn’t provide a way to get all the rules directly, so we use osgi, another part of openhab-js, to pull the raw RuleManager service. But I made a mistake. We need the RuleRegistry. which is different. RuleManager doesn’t have a getAll() method.

So I need to adjust the code somewhat:

  var { ruleRegistry } = require('@runtime/RuleSupport');

  // ---------------- Logger settings
  console.loggerName =  "org.openhab.rule." + ctx.ruleUID;

  // ---------------- Script Configuration Settings  
  
  // Rules that should never be reinitialised
  var exclude_from_reinit = ["rule_reinit_enabled_rules", "delay_start_rules"];
  
  // -------- Main ------- 
  
  var allRules = utils.javaSetToJsArray(ruleRegistry.getAll());

  allRules.map( r => r.getUID())
           .filter( r => !exclude_from_reinit.includes(r) )
           .filter( r => rules.isEnabled(r) )
           .forEach( r => {
             rules.setEnabled(r, false);
             Java.type("java.lang.Thread").sleep(200);
             rules.setEnabled(r, true);
           });

We don’t need the manager, we can access the parts of the manager we need through openhba-js’s rules Object which is available be default.

Note, I’m not hurtling across South Park at night at 60 MPH in the snow (in the passenger seat of course) so was able to actually test the code this time).

See Design Pattern: Working with Groups in Rules for all sorts of ways to loop through, reduce, find and filter Arrays/Lists, etc in most of the languages OH supports.

Also pay attention to the way the array is created above. You can just create the array, you don’t need to define the array as a String and then use a bunch of replace and split operations to convert the String to an actual array.

2 Likes

Thanks Rich! I’ve checked your code, it works flawlessly and I think I understand what you are saying! :smiley: