OH 3 Examples: Writing and using JavaScript Libraries in MainUI created Rules

@rlkoshak ok gotcha. Thank you for taking the time to respond to my inquiries. I appreciate it. Apologies for steering away from the original topic.

No worries and I feel like I need to say the following as well. I do not speak for openHAB nor the openHAB foundation. Everything I’ve said is based on experience and personal opinion.

2 Likes

Hi Rick,

Thanks for the great article! I’ve already read it a couple of months ago, but got back to it only now. I’ve started with the OH update to 3.2, so now I have the GraalVM engine. Can you please advice me, what is the trick in this new engine to define a library function? The code which is written in this post is not working there.

Thanks,
Attila

Not easily. You’ve two choices really.

  1. Do it just like the above to essentially include the contents of the library into your rule.

  2. Follow any tutorial out there for how to build an npm module.

2 is the standard way to do it and will be more flexible and sharable in the long run. But it’s more involved so may be more work than you may want to do. If you want to go that rout look for just about any tutorial on Google and you should be OK. The modules go in $OH_CONF/automation/js/node_modules/<your folder>. For example, I’ve got a personal folder there where I’ve put a few of my personal libraries. I’m working on an openhab_rules_tools module which I hope to push to npm and eventually integrate some of them into the helper library that comes with JS Scripting.

Maybe once I figure the ins and out of this approach I’ll write a tutorial but I’m still learning it myself.

1 Like

Thanks for the quick answer Rich. Yeah, would be good to have the simplicity as in the old version, but will give a shot to the npm package as I do not want to duplicate the code. Thanks!

Great tutorial on using JavaScript libraries in MainUI-created rules! I found the examples particularly helpful for understanding how to structure and load libraries effectively. One question: do you have any tips for debugging libraries when something goes wrong? Also, have there been any updates or changes to this approach with newer openHAB versions? Thanks for sharing this valuable resource!

It depends on how independent the library is from OH. If it’s generic JS, then any JS focused testing approach/suite will work just fine. If it requires some interaction with OH you’ll need to either mock up the OH interactions (when using a testing suite) or test while connected to OH.

Personally I’m not doing anything that is complex enough so simply exercising the code in an OH rule is sufficient for my testing needs

Kind of. Everything discussed above is for the old Nashorn JS Add-on which is ECMAScript 5.1. If you want to use a much more recent version of JS you should use the JS Scripting add-on. This supports npm modules for libraries. See JavaScript Scripting - Automation | openHAB.

Importing is also different using require instead of include.

Also, there is now the cache to save variables instead of using this as demonstrated above.

Note that all of the examples above have been rewritten in the newer ECMAScript and are available in openhab_rules_tools which you can install using openhabian-config or through npm (instructions on the link).

With the newer JS Scripting the Offline Alert example would look something like:

triggers:
  - id: "1"
    configuration:
      time: 08:00
    type: timer.TimeOfDayTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: ServiceStatuses
      state: ON
      operator: "!="
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: >-
        var {utils} = require('rlk_personal');


        var nullItems = utils.getNames("ServiceStatuses", function(i) { return i.state.class == UnDefType.class; });

        console.info(nullItems);


        var offItems = utils.getNames("ServiceStatuses", function(i) { return i.state == OFF; });

        console.info(offItems);


        var msg = "";


        if(nullItems.length > 0) {
          msg = "The following sensors are in an unknown state: " + nullItems;
        }

        if(offItems.length > 0) {
          if(msg.length > 0) msg += "\n";
          msg += "The following sensors are known to be offline: " + offItems;
        }


        utils.sendInfo(msg);
    type: script.ScriptAction

The reimplementation of utils (which is part of a node module I created called rlk_personal:

exports.sendAlert = function(message, logger) {
  var logger = (logger) ? logger : log('sendAlert');
  logger.warn('ALERT: ' + message);
  actions.NotificationAction.sendBroadcastNotification(message, 'alarm', 'alert');
}

exports.sendInfo = function(message, logger) {
  var logger =  (logger) ? logger : log('sendInfo');
  logger.info('INFO: ' + message);
}

exports.isNight = function() {
  const currToD = items.getItem('TimeOfDay').state;
  return currToD == 'NIGHT' || currToD == 'BED';
}

exports.isAway = function() {
  return exports.isNight() || items.getItem('Presence').state != 'ON';
}

exports.getNames = function(group, filterFunc) {
  return items.getItem(group.name || group).members
                             .filter(filterFunc)
                             .map(s => (s.getMetadata()['name']) ? s.getMetadata()['name'].value : s.label)
                             .join(', ');
}

You will notice that the code is much shorter, is a lot less work to set up thanks to the helper library that comes with the add-on, and uses pure JS Classes, Objects, and idioms everywhere instead of needing to jump back and forth between JS and Java as shown in the original post. You can especially see this in the getNames function where the original uses Java’s streams API to filter the Java List as opposed to the new verison that uses all JavaScript Array manipulation and features.