Pushover notifications from JavaScript rules

Hi,

I would like to start migrating from DSL to JavaScript rules. I have many rules sending notifications using Pushover, so first requirement/PoC for me would be to be able to send a notification using JS.

Current DSL rule snippet:

val actions = getActions("pushover", "pushover:pushover-account:account")
actions.sendMessage("Body", "Title")

(as documented in Pushover - Bindings | openHAB)

So I’ve read:

So following the Pushsafer example, this is now written like this in JS (not sure why API key is now needed):

let { actions } = require('openhab');
 actions.Pushsafer.pushsafer("<your pushsafer api key>", "Body", "Title", "", "", "", "")

And in DSL like this:

val actions = getActions("pushsafer", "pushsafer:pushsafer-account:account")
actions.sendPushsaferMessage("Body", "Title")

So I’ve tried (file-based):

rules.JSRule({
  name: "Test Pushover",
  description: "My first JS rule",
  triggers: [triggers.ItemStateChangeTrigger('Test', 'OFF', 'ON')],
  execute: data => {
    actions.Pushover.sendMessage('Body', 'Title');
  }
});

Result: Failed to execute rule Test-Pushover-bd382cbc-02d9-4061-8a0e-6b0e5df7c7ce: TypeError: undefined has no such function “sendMessage”: TypeError: undefined has no such function “sendMessage”

Then:
actions.Pushover.pushover.sendMessage('Body', 'Title');

Result: Failed to execute rule Test-Pushover-a7e357c7-b988-4f66-90e4-79c953b328f4: TypeError: Cannot read property “pushover” from undefined: TypeError: Cannot read property “pushover” from undefined

So it seems that Pushover is missing from available actions. Am I right that each binding action needs to be explicitly reimplemented for JavaScript usage, or am I missing something?

Is it possible to use the action with some other syntax from JS?

Best regards,
Jacob Laursen

If you’ve not changed the settings for the JS Scripting addon, this line is not necessary. Furthermore, for now you cannot use let or const in a UI rule because of the way OH reuses the script again on subsequent runs. The second time the rule runs you’ll get an error complaining that actions already exists.

I think the PushSafer example there is wrong. Or to be more precise, it’s for the old 1.x style PushSafer Action which are no longer supported in OH. You’ll want to use the standard way to get an Action provided by a Thing.

I think you’ll want to use:

val action = actions.thingActions("pushsafer", "pushsafer:pushsafer-account:account") 

to get the actions for the PushSafer Thing with that Thing UID.

NOTE: You don’t want to overwrite the actions variable. It’s unfortunate that the binding authors use that variable name in their examples.

From there you can call the functions on action as illustrated in the binding docs.

Thanks @rlkoshak, that got me in the right direction! This worked:

var action = actions.thingActions("pushover", "pushover:pushover-account:account");
action.sendMessage('Message.', 'Title');

Now ready for migration. :slight_smile:

Do you think it would appropriate to add such an example to documentation for each binding providing actions and already having a DSL example? With time this could be considered redundant, but at this moment it might be relevant when having multiple scripting languages?

Best regards,
Jacob Laursen

I don’t think it should be up to the individual binding authors to have to provide examples for each and every supported language and it’s not reasonable to expect the community to update every binding with an Action with examples for all the languages.

Right now, the only truly default language that is usable stand-alone is Rules DSL so that is what I would expect to be used in the examples for the binding docs. Eventually I’d like to see JS Scripting become the default and when that happens we can add or replace the Rules DSL examples with the JS Scripting examples. Until then we need to look at the readme for the automation add-on for how to do stuff like this.

After all, it’s an add-on. It’s optional.

However, the JS Scripting docs need to remove that old and no longer valid example and replace it with the above, along with some text on how this is how to access all Thing provided actions.

1 Like

@rlkoshak - agreed.

Slightly off-topic: Now that I have a working action, I would like to reuse that from a function to I can easily switch between push notification providers for all my rules and have less boiler-plate code.

So I’m trying to read up on how to do this in JS. There’s something called ECMAScript Modules (ESM) supported by GraalVM JavaScript. They should have the extension .mjs. However, it seems this extension is not being watched. Do you know best-practice for reusing functions/methods between JS files - supported by openHAB?

Also tried CommonJS way (I think) with “require(‘notification.js’);” but seems that no matter what I do, I just get: org.graalvm.polyglot.PolyglotException: TypeError: Cannot load CommonJS module: 'notification.js'

Best regards,
Jacob Laursen

Best practice is to create npm modules which will go in $OH_CONF/automation/js/node_modules. Once it’s an npm module, you can require the parts you need in your rule just like any third party library.

But the old style Nashorn load approach documented at OH 3 Examples: Writing and using JavaScript Libraries in MainUI created Rules will still work to inline your code into your rules.

You can also create a rule that gets called by other rules. You can pass data through a triggering Item, or call the rule directly and pass data to the rule as a map. You can also set data in the cache which gets shared between rules.

There are lots of options.

1 Like

Thanks again, placing it in node_modules did the trick. If anyone else new to JS would read this thread, this worked:

$OH_CONF/automation/js/node_modules/notification.js:

exports.send = function(title, body) {
        var action = actions.thingActions('pushover', 'pushover:pushover-account:account');
        action.sendMessage(body, title);
}

$OH_CONF/automation/js/test.js:

var notification = require('notification.js');
[...]
notification.send('Title', 'Body');

Best regards,
Jacob Laursen