JSRule Questions

Hi Guys,

Just started using JSRules (as opposed to DSL rules) and I have some questions.

Can we get the rule name inside of the rule? The event object has a module field but no name field. Can we get the name from the module ID? Or do all the rules in the collection share the same module ID?

Also, is there some reference I can read on classes and methods I can use with JS scripting, specifically for openhab? Currently I am using https://openhab.github.io/openhab-js/index.html as my main point of reference, however there is no reference to timers, etc in this guide. Or should I be looking at generic javascript for these things?

Finally, can I put any js file with functions inside the node_modules folder and have them available without importing?

Iā€™m also finding I get no intellisense using VS Code for item names, DSL used to pick them up as long as I had the items file open (I still get them if I use the name once in a file), any way to solve this?

Thanks, Mike.

ruleUID is available. At least itā€™s available in UI triggered scripts. I see no reason it wouldnā€™t be available in file based rules as well, especially since itā€™s used as the ā€œnameā€ of Timers when a name isnā€™t provided.

As far as I can tell, the module is not related to your script. Itā€™s information that tells OH how to load and build your rule. Itā€™s not going to contain anything useful as far as I can tell.

Itā€™s often way faster and more informative to experiment to find the answer to stuff like this. What do you see when you log it out? Let us know what you find! Iā€™ve never messed with module and wasnā€™t even aware that itā€™s part of the event Object.

JavaScript Scripting - Automation | openHAB has the same content and more and it preserves all the screen shots and has better formatting. That should be your primary reference.

For timers see JavaScript Scripting - Automation | openHAB and JavaScript Scripting - Automation | openHAB. It should be noted though that both of those sections are also in the reference you are currently using too, only the formatting of that version of docs does make it challenging to navigate and read.

No. First, your library needs to be configured as a node library: see JavaScript Scripting - Automation | openHAB. Thankfully itā€™s really easy and only takes a couple of commands.

Secondly, youā€™ll have to import them using require: see JavaScript Scripting - Automation | openHAB. Only the add-onā€™s helper library gets imported by default, and even then there is a setting to disable even that importation. You do not want to import everything all the time and everywhere.

I donā€™t use file based rules but I believe itā€™s actually best practice to turn that setting off and explicitly import only those things you need in any given file rather than letting OH inject everything all the time. Thatā€™s quite a burden in UI based scripts though which is why the setting exists in the first place.

Someone will need to write an add-on to VSCode that does that for JS Scripting, jRuby or any of the other languages, or somehow add those languages to the existing add-on. The current add-on only supports Rules DSL. The biggest issue is that the list of Items is not really known until runtime so there is no way for the generic JS intellisence to pick them up.

Even in the UI, the intellisence doesnā€™t include the Item names unfortunately. Though the developer sidebar makes that no too much of a burden.

Its telling me itā€™s not available:

ReferenceError: "ruleUID" is not defined

Iā€™ve been experimenting by logging out the objects using JSON.stringify(event), works quite well, mostly itā€™s just the states but you do get a module member which corresponds to the hash that OH puts after the rule name in the rules section of paperUI. I thought I might be able to get the name from this hash somehow. Itā€™s not important, I just wanted to make my logs prettier.
Looks like this:

 {"oldState":"100","newState":"81.03","eventClass":"org.openhab.core.items.events.ItemStateChangedEvent","payload":{"type":"Percent","value":"81.03","oldType":"Percent","oldValue":"100"},"itemName":"Lounge_Light","eventType":"change","triggerType":"ItemStateChangeTrigger","module":"836928f5-3fce-4f26-9c59-e3395180ffba"}

Iā€™ll keep playing.

Hmmm. There are several things you can try.

First, I vaguely recall that there might be a global variable. It might be in there. I seem to have that in one of my library functions but there is also indication that only works for UI rules.

global.ruleUID

But it also indicates that you can get at the filename for file based rules.

    if (global.ruleUID !== undefined) { // Use UI ruleUID and key if available
      name = 'ui.' + global.ruleUID + ((key !== undefined) ? '.' + key : '');
    } else if (global['javax.script.filename'] !== undefined) { // Use filename and key if available
      name = 'file.' + global['javax.script.filename'].replace(/^.*[\\/]/, '') + ((key !== undefined) ? '.' + key : '');
    }

Maybe the filename is enough? This kind of smells like an XY Problem so I canā€™t be sure.

Secondly, it might work if we add it as an argument to the execute function.

execute: (event, ruleUID) => {

or

execute: (event, global.ruleUID) => {

Third is to import @runtime. It might be available there, though I donā€™t see it documented as such.

  execute: (event) => {
    const runtime = require('@runtime');
    console.log(runtime.ruleUID);
  }

Fourth, the ruleUID is part of the default logger name for the rule so we could pull it from that. But here there is further indication that for file based rules you only get the filename, not the ruleUID.

The default logger name consists of the prefix org.openhab.automation.script and the scriptā€™s individual part .file.filename or .ui.ruleUID . This logger name can be changed by assigning a new string to the loggerName property of the console:

But you can get the filename or ruleUID with a simple split:

var ruleID = console.loggerName.split('.').pop();

We also might be going about this the long way.

Youā€™ve created the rule in a file. Hopefully youā€™ve given it a meaningful ID thatā€™s already hard coded there. Why not hard code it again in the execute function? Do you really need to pull it dynamically? You already know it. If you are just trying to add them to the logs, set the loggerName and just hard code it there.

Not really, itā€™s just a time saver exercise, I wanted to make a shared function that takes the event and sets everything up for me, filename, script name, etc so I can trace things better, Iā€™m a little bit OCD in keeping all my event functions in separate files, so knowing where the log entry came from is useful to me.

Itā€™s a shame considering the event declaration itself is an object, would be nice if the Execute (event) part included this object, or the declaration object was inside the event object. You could also potentially store other information in this declaration and have it available inside the rule.

Where can I look at this runtime class?

Also, is there a script loaded trigger? Right now I am either having to wait for a cron to fire or change something (like turning the light on and off) in order to test a rule. Would be handy if just saving it fired the rule when its re-added.

Well, itā€™d be kind of backwards though. The event is created by and generated by what caused the rule to run. The ruleUID isnā€™t really a part of the event. Itā€™s part of the rule and the event is passed to the rule.

I suspect, when using file based rules, itā€™s simply not technically feasible to expose the ruleUID or else it would be done for consistency sake if nothing else. The fact that is uses filename instead is a strong indication of that.

Itā€™s basically the raw set of Java stuff that OH injects into a rule. JS Scripting uses itā€™s own helper library for this instead but you can import it if you want. Itā€™s mostly useful if you have an old Nashorn script that you want to get to run in the new GraalVM JS engine.

Itā€™s not a class. Itā€™s a whole mess of stuff.

No but in OH 4 the behavior of the system started/runlevel triggers changed back to the old 2.5 behavior. If the runlevel has passed with the rule is loaded the rule will immediately trigger. Thus, any rule that has a system runlevel trigger will run every time itā€™s loaded/reloaded.

You can also manually run a rule from MainUI, the karaf console, the REST API, or from another rule. Note that in all these cases though that event will not exist so if you need a rule to trigger in this way you need to take that into account.

You can probably fake a system loaded trigger by writing a function in your file that get executed that calls the rule(s) you want to trigger on a load.

(function() {
  rules.runRule(uidOfRuleToRun);
})()

If I understand JS correctly, that will create the function and immediately run it. Put that at the bottom of your file, replacing ā€œuidOfRuleToRunā€ of course, and it should run the rule when the file is loaded/reloaded.

Iā€™m not sure if there is any inconvenience for not coding the execute method as an arrow function, when creating file based rules with JSRules, but if it is written as a traditional function, then you can access to the rule data (id, name tags, etc) using the ā€˜thisā€™ keyword.
This way, if needed, you can also call other custom methods created inside the rule body.


    rules.JSRule({
		id: "rule_id",
		name: "rule name",
		triggers: [...],
		
		execute: function(event) {
			console.log(this.id);
			console.log(this.name);
			this.anotherMethod();
		},
		
		anotherMethod: function() {

		}
    });
1 Like