Experimental Next-Gen Rules Engine Documentation 5 of : Actions

jsr223
ngre
Tags: #<Tag:0x00007f51ea536310> #<Tag:0x00007f51ea535e60>

(Rich Koshak) #1

Other Functions and Actions

OH supports a number of functions which allow one to cause something to occur in a script such as sending an email or scheduling something to occur at a later date. There are a number of built in functions and a number of add-ons called Actions that can be installed and used.

createTimer

Schedules a function to execute sometime in the future.

// Native Quartz version
var ScriptExecution = Java.type("org.eclipse.smarthome.model.script.actions.ScriptExecution");
var DateTime = Java.type("org.joda.time.DateTime");
var runme1 = function(){ logInfo("Native Quartz Timer completed"); }
var timer1 = ScriptExecution.createTimer( DateTime.now().plusSeconds(5), runme1);
...
timer1.cancel();

// Native Java/Nashorn
var Timer    = Java.type("java.util.Timer");
var timer2 = new Timer("setTimeout", true);
var runme2 = function() { logInfo("Native Java/Nashorn Timer completed"); };
timer2.schedule( runme2, 2000);
...
timer2.cancel();

// Library version
// NOTE: This does not use the Quartz Scheduler like the above does
var runme3 = function(){ logWarn(logInfo("Library Native Quartz Timer completed"); };
var timer3 = createTimer( now().plusSeconds(2), runme3, "argument");
...
timer3 = timer.cancel();

// setTimeout Library version javascript like, with argument.
var runme4 = function(){ logWarn(logInfo("Library setTimeout completed"); };
var timer4 = setTimeout( runme4, 1000, "argument");
...
timer4.cancel();

Unfortunately, unlike in the Rules DSL where we can have global variables that survive a Rule’s execution methods like cancel and reschedule will be less useful in NGRE.

sleep

Pauses execution of the Rule for the given number of milliseconds.

// Native and Library version
java.lang.Thread.sleep(1000);

storeStates/restoreStates

Retrieves a map of Item name and State pairs for the given list of Items. Because PaperUI rules do not support global variables, these may not be as useful. Typically, one will call storeStates and save those values somewhere, then later call restoreStates later on to restore those Items to the saved states.

// Native version
var states = events.storeStates(ir.getItem("Test"), ir.getItem("ColorTest"), ir.getItem("DimmerTest"));
...
events.restoreStates(states);

// Library version - Untested as of this writing
var states = events.storeStates(ir.getItem("Test"), ir.getItem("ColorTest"), ir.getItem("DimmerTest")); 
...
events.restoreStates(states);

Unfortunately, the utility of these two actions are greatly reduced as there is no way to persist the variable states across multiple runs of the Rule or across multiple Rules. TODO: However, one could parse the Map returned by storeStates into a String and post that to an Item and then parse it back to go the other way. Submit a PR to add this to the library.

logging

Often it is useful to log certain information to the logs for debugging and monitoring. OH supports five levels of logging. Levels are used to limit how much gets saved to the log file. While debugging a problem or developing new Rules one might set the logging level to DEBUG or TRACE to get more information but set the level to INFO or WARN during normal operations. Each level includes the logging from the levels above it in the following table (i.e. WARN level also includes ERROR logs).

Level Purpose
ERROR Use to denote a fatal error that prevents a Rule from working.
WARN Use to denote something that is wrong or unexpected in a Rule but the Rule is able to manage it.
INFO Informational level info needed for general monitoring.
DEBUG Additional information that might be useful for writing new Rules or identifying problems.
TRACE Highly detailed information.

By default, the Rules loggers are configured to only output at the INFO level.

// Native
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.eclipse.smarthome.model.script.Rules");
logger.error("This is an error log");
logger.warn("This is a warning log");
logger.info("This is an info log");
logger.debug("This is a debug log");
logger.trace("This is a trace log");

// Library
logError("This is an error log");
logWarn("This is a warning log");
logInfo("This is an info log");
logDebug("This is a debug log");
logTrace("This is a trace log");

In the Native version above, you can change the string in getLogger to be the name of where you want your logs to go. So, if for example, you want to put all the logs from one Rule or set of Rules in a separate logger so they can be separately filtered or saved in a separate log file, use a different path.

The library writes to "org.eclipse.smarthome.automation.module.script.rulesupport.internal.shared.SimpleRule".

See the logger docs for details on how to format and create the strings that get logged.

executeCommandLine

To execute a shell script or program from a script use executeCommandLine or executeCommandLineAndWaitResponse. These functions use the same underlying code to kick off the scripts that the Exec binding does so the same warnings and work arounds apply:

  • sometimes it will be required to replace spaces with @@
  • the command will run as the same user that openHAB is running as which is usually openhab
  • the openhab user doesn’t have a full set of environment variables so the full path to the command and files used by the command will need to be supplied.
// Native
var ExecUtil = Java.type("org.eclipse.smarthome.io.net.exec.ExecUtil");
var r1 = ExecUtil.executeCommandLineAndWaitResponse("echo 'wait for result'", 5000);
logInfo(r1);
ExecUtil.executeCommandLine("echo `don't wait for result`");

// Library 
var r2 = executeCommandLineAndWaitResponse("echo 'wait for result2'", 5000);
logInfo(r2);
executeCommandLine("echo 'don't wait for result'");

transform

There will be times where one will want to call the transformation service from a script. This can be done with the transform function.

// Native
var ScriptServiceUtil = Java.type("org.eclipse.smarthome.model.script.ScriptServiceUtil");
var services = ScriptServiceUtil.getActionServices();
var tnsf = services.stream().filter(function(el){ return el.getActionClassName().contains("Transform"); }).findFirst().get();
logInfo(tnsf.getActionClass().static.transform("MAP", "locks.map", "21"));

// Library - TODO didn't work
logInfo(transform("MAP", "locks.map", "21"));

See the Transformation Service Documentation for the available services and how to use them.

HTTP Requests

There are a number of functions built in to OH to make simple HTTP requests to a web server.

var url = "http://demo.openhab.org:8080/rest/";
var timeout = 5000;

// Native Java
var HttpUtilNativ = Java.type("org.eclipse.smarthome.io.net.http.HttpUtil");

var results = HttpUtilNativ.executeUrl("GET", url, timeout);
logInfo("results GET: ", results);

results = HttpUtilNativ.executeUrl("PUT", url, timeout);
logInfo("results PUT: ", results);

results = HttpUtilNativ.executeUrl("POST", url, timeout);
logInfo("results POST: ", results);

// Library
results = sendHttpGetRequest(url, timeout);
logInfo("results sendHttpGetRequest: ", results);

results = sendHttpPutRequest(url, timeout);
logInfo("results sendHttpPutRequest: ", results);

results = sendHttpPostRequest(url, timeout);
logInfo("results sendHttpPostRequest: ", results);

results = sendHttpDeleteRequest(url, timeout);
logInfo("results sendHttpDeleteRequest: ", results);

//  Library executeUrlWithContent( httpMethod, url, httpHeaders, content, contentType, timeout)
var header  = "application/x-www-form-urlencoded; charset=UTF-8";
var results = executeUrlWithContent("POST", url, null, "", header, timeout);
logInfo("results executeUrlWithContent: ", results);

Thing Status

To obtain the online/offline status of a Thing.

// Native Action
var thingHelper = Java.type("org.eclipse.smarthome.model.script.actions.ThingAction");
var thingStatus = thingHelper.getThingStatusInfo("zwave:device:dongle:node6");

// Using things variable
var ThingUID = Java.type("org.eclipse.smarthome.core.thing.ThingUID");
var tUid = new ThingUID("zwave:device:dongle:node6");
var thing = things.get(tUid);
var status = thing.getStatus();

// There is no library function

TODO add PR to add this action to the library

The possible values from calling toString() on the ThingStatus are:

  • INITIALIZING
  • OFFLINE
  • ONLINE
  • REMOVED
  • REMOVING
  • UNINITIALIZED
  • UNKNOWN

callScript

This is an action that is available in Rules DSL to call a subscript. Because NGRE has support for libraries this Action is deprecated.

Cloud Notification Actions

Publishes messages to apps connected through myopenhab.org.

// Native JavaScript
var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;
NotificationAction.sendNotification("rlkoshak@gmail.com", "This is a directed notification");
NotificationAction.sendNotification("rlkoshak@gmail.com", "This is a detailed directed notification", "light", "WARN"); // TODO what is severity? "light" is the icon
NotificationAction.sendBroadcastNotification("This is a broadcast test");
NotificationAction.sendBroadcastNotification("This is a detailed brodcast test", "light", "WARN")
NotificationAction.sendLogNotification("This is a log notification");
NotificationAction.sendLogNotification("This is a detailed log notification", "light", "WARN");

// No library helper functions

TODO: Add library helper functions.

Non-core Actions

Actions beyond the the above core set can be installed and called from scripts.

// Native JavaScript


// Library version, a collection of all available Actions are put into a collection that can then be
// called by name.

//To send a Mail:
getAction("Mail").static.sendMail("mail@you.de", "subject", "message");


I’ve not tested the code below this point yet.

TODO: Call an Action

load(Java.type("java.lang.System").getenv("OPENHAB_CONF")+'/automation/jsr223/jslib/JSRule.js');

//Returns actionList like = Voice,Transformation,PersistenceExtensions,ThingAction,Audio,Mail,XMPP,Telegram
logInfo("getActionList:", getActionList());

//To send a Mail:
getAction("Mail").static.sendMail("mail@you.de", "subject", "message");

  • third party installed Actions

TODO: Create an Object

  • in Rule body
  • reusable across multiple Rules

TODO: Migrate JSONDB Rules to Stand Alone .js Files

  • how to build up a JSON Rule in a separate file (JSR223 docs for building a Rule)

TODO: Rule Templates

  • what are they?
  • how do I create them?
  • how do I use them?

TODO: Creating Libraries

Will probably be a new posting.

  • reusable functions
  • reusable Rules

TODO: Comprehensive Examples

  • shows multiple rule trigger types
  • shows rules enabling/disabling each other
  • shows many if not all of the above
  • maybe pick one or more DPs as the example

Trouble Shooting

  • errors
  • where to look
  • how to interpret

Previous step: Experimental Next-Gen Rules Engine Documentation 4 of : Writing Scripts
Next step:


Understanding / Avoiding ERE (Experimental Rule Engine)
Send Pushover Notification from Paper UI "Rules"
Next-gen Rules / PanelUI
Paperui rules engine usage
(Bogdan Manta) #2

I have installed the Rule Engine (experimental) in the paperUI, set a rule to send broadcast notification to openhab cloud and worked. After 2-3 days all of a sudden the notification rule stopped working. I deleted the rule thinking that something messed it up and made a new one to test the notification. Upon closer inspection, in the log viewer I get a ClassNotFoundException.
Here is the complete log:

    2019-03-18 12:01:33.371 [WARN ] [omation.core.internal.RuleEngineImpl] - Fail to execute action: 1

java.lang.RuntimeException: java.lang.ClassNotFoundException: org.openhab.io.openhabcloud.NotificationAction.sendBroadcastNotification

	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:397) ~[?:?]

	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:449) ~[?:?]

	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:406) ~[?:?]

	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:402) ~[?:?]

	at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155) ~[?:?]

	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) ~[?:?]

	at org.eclipse.smarthome.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:60) ~[?:?]

	at java.util.Optional.ifPresent(Optional.java:159) ~[?:?]

	at org.eclipse.smarthome.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:57) ~[?:?]

	at org.eclipse.smarthome.automation.core.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1199) [249:org.eclipse.smarthome.automation.core:0.10.0.oh240]

	at org.eclipse.smarthome.automation.core.internal.RuleEngineImpl.runNow(RuleEngineImpl.java:1047) [249:org.eclipse.smarthome.automation.core:0.10.0.oh240]

	at org.eclipse.smarthome.automation.core.internal.RuleEngineImpl.runNow(RuleEngineImpl.java:1063) [249:org.eclipse.smarthome.automation.core:0.10.0.oh240]

	at org.eclipse.smarthome.automation.rest.internal.RuleResource.runNow(RuleResource.java:288) [258:org.eclipse.smarthome.automation.rest:0.10.0.oh240]

	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]

	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]

	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]

	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]

	at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.internal.Errors.process(Errors.java:315) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.internal.Errors.process(Errors.java:297) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.internal.Errors.process(Errors.java:267) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317) [171:org.glassfish.jersey.core.jersey-common:2.22.2]

	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154) [172:org.glassfish.jersey.core.jersey-server:2.22.2]

	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473) [169:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]

	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427) [169:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]

	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388) [169:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]

	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341) [169:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]

	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228) [169:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]

	at com.eclipsesource.jaxrs.publisher.internal.ServletContainerBridge.service(ServletContainerBridge.java:76) [20:com.eclipsesource.jaxrs.publisher:5.3.1.201602281253]

	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:865) [85:org.eclipse.jetty.servlet:9.4.11.v20180605]

	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535) [85:org.eclipse.jetty.servlet:9.4.11.v20180605]

	at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:71) [186:org.ops4j.pax.web.pax-web-jetty:7.2.3]

	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) [82:org.eclipse.jetty.security:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1317) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:293) [186:org.ops4j.pax.web.pax-web-jetty:7.2.3]

	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) [85:org.eclipse.jetty.servlet:9.4.11.v20180605]

	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1219) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:80) [186:org.ops4j.pax.web.pax-web-jetty:7.2.3]

	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.Server.handle(Server.java:531) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260) [84:org.eclipse.jetty.server:9.4.11.v20180605]

	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281) [75:org.eclipse.jetty.io:9.4.11.v20180605]

	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102) [75:org.eclipse.jetty.io:9.4.11.v20180605]

	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118) [75:org.eclipse.jetty.io:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:680) [87:org.eclipse.jetty.util:9.4.11.v20180605]

	at java.lang.Thread.run(Thread.java:748) [?:?]

Caused by: java.lang.ClassNotFoundException: org.openhab.io.openhabcloud.NotificationAction.sendBroadcastNotification

	at jdk.nashorn.internal.runtime.NativeJavaPackage.classNotFound(NativeJavaPackage.java:162) ~[?:?]

	at jdk.nashorn.internal.scripts.Script$17$\^eval\_.:program(<eval>:2) ~[?:?]

	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637) ~[?:?]

	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494) ~[?:?]

	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) ~[?:?]

	... 72 more

And this is the test script that I am trying to run:

var NotificationAction= org.openhab.io.openhabcloud.NotificationAction;
NotificationAction.sendBroadcastNotification("This is a detailed brodcast test", "light", "WARN");

Any help?


(Scott Rushworth) #3

This topic is meant for building out the documentation for the NGRE, so it would be better to create a new topic for your issue. But… I’m guessing you tried testing your rule by clicking the runNow button, before it was actually triggered. This is a known issue, which needs to be migrated over to the openhab-core repo. I’ve tested your rule and got the same error when using runNow. After saving the rule again and triggering it (I had it hooked up to a switch), the rule executed properly when triggered and runNow could be used without an error.