JRule - openHAB Rules using Java

Hm is it possible from within a JRule to also access a Thing and its configuration and channels? Maybe even modify it?

Currently I’m struggeling with RuleDSL and its limited JSON processing support. JSONPATH transformations are limited to single values, but I would need to parse the channels of a thing and modify them. Reading and modifying would be possible via REST API, but processing the json is too limited as it seems. Easiest of course would be to do it with Java and all its JSON parsers. Maybe even being able to access thing attributes programmatically, without having to go through JSON at all :).

There is some support for Things. See:

and

You can get things from the JRuleThingHandler from a thing you can get channels. This is part of the openhab-core classes.

I have for instance a Thing where I automatically (with jrule) link items to all its channels.

/S

Hi, again.

I’m struggling with 2 things:

  1. Quantity Items.

I have the following item:

Number:Illuminance LR_Sensor_Illuminance    "Illuminance"

When I try to access it like this

var illuminance_level = JRuleNumberItem.forName(s"LR_Sensor_Illuminance")

I get the following error:

12:46:00.820 [ERROR] [ion.jrule.internal.engine.JRuleEngine] - [Lights_Test] Error in rule: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.openhab.automation.jrule.internal.engine.JRuleEngine.invokeRuleInternal(JRuleEngine.java:552)
	at org.openhab.automation.jrule.internal.engine.JRuleEngine.lambda$62(JRuleEngine.java:515)
	at org.openhab.automation.jrule.internal.engine.JRuleEngine.invokeDelayed(JRuleEngine.java:574)
	at org.openhab.automation.jrule.internal.engine.JRuleEngine.lambda$61(JRuleEngine.java:514)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.ClassCastException: class org.openhab.automation.jrule.internal.items.JRuleInternalQuantityItem cannot be cast to class org.openhab.automation.jrule.items.JRuleNumberItem (org.openhab.automation.jrule.internal.items.JRuleInternalQuantityItem and org.openhab.automation.jrule.items.JRuleNumberItem are in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @305704d5)
	at org.openhab.automation.jrule.items.JRuleNumberItem.forName(JRuleNumberItem.java:31)
	at org.openhab.automation.jrule.rules.user.Tests.Lights_Test(Tests.scala:28)
	... 13 more

I understand that I should use one of the Internal items and my assumption is JRuleInternalNumberItem, but those “Internal” items have no .forName function

I think it could be useful to add this to the examples as quantity items might be common.

  1. I tried to implement this because after migrating most of my rules, I have so many things logged to the Info log that they create noise.

But it turns out the Logger libraries are not available:

You should use JRuleQuantityItem.forName for this.

Why are you not using logInfo, logDebug?
You could add additional libraries e.g. in your pom. I am using the shade plugin to build a “fat” jar.

You can just add slf4j dependencies in your local project. If you use maven you can declare them as “provided” since they are included in openHAB:s runtime environment you don’t need to add them as a third party jar.

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4jVersion}</version>
<scope>provided</scope>

Otherwise I agree with querdenker2k that you can used the wrapped methods, this way is only if you want do some special logging or configuration.

@querdenker2k, the JRuleQuantityItem worked, thanks!

I don’t use the wrapped methods because of this:

I use the Karaf Console to check the logs and set in advance the logging level for the rule(s) I want to monitor.

I don’t use Maven, I use sbt and Bloop, so I’ll have to find out how to achieve something similar.

Thanks for your prompt help, @querdenker2k and @Seaside.

1 Like

Hi, again.

Does anyone know what could be the problem in the following line:

if ("ONLINE".equals(thing.getStatus().toString()))

with the following rule?

def reportOfflineThing(event: JRuleThingEvent): Unit = {
        logInfo("Report Offline Thing > STARTING.")
        val thingID = event.getThing()
        logInfo(s"Report Offline Thing > Triggering Thing: ${thingID}.")
        var thing = JRuleSubThing.forName(thingID)
        logInfo("Report Offline Thing > Restarting Thing.")
        thing.restart()
        logInfo("Report Offline Thing > Waiting for Thing to restart.")
        createOrReplaceTimer("Test", Duration.ofSeconds(10), { (t: JRuleTimerHandler#JRuleTimer) =>
            logInfo("Report Offline Thing > Checking Thing status.")
            if ("ONLINE".equals(thing.getStatus().toString()))
                //Notification().sendNotification(s"${thing.getLabel()} changed to ${event.getStatus()} 10 seconds ago. It's back online.")
                    logInfo("Report Offline Thing > Notification sent.")
            else
                //Notification().sendNotification(s"${thing.getLabel()} changed to ${event.getStatus()} 10 seconds ago")
                logInfo("Report Offline Thing > Notification sent.")
        })
        logInfo("Report Offline Thing > ENDING.")
    }

I pre-compile my rules and as soon as I upload the .jar file to the /jrule/rules-jar/ folder, JRule starts logging non-stop all events going on in the system. I have to remove the jar for it to stop. If I comment the IF sentence, JRule loads the .jar without issues.

Edit: By the way, is it possible to use Ephemeris?
I tried with JRuleActions but I couldn’t find anything related. Within some rules, I want to check if today is a bank holiday.

Hi!

Don’t know if anyone else is using Scala with JRule. Best thing is probably to write the same Rule in java and see if you can get it to work. Once working, translate it to scala. Easier to eliminate any language related problems.

For Ephemeris, no there is no built in support / wrappers for it. You can still use it, include openhabcore-jars as “provided” dependencies and just call it directly i.e

import org.openhab.core.model.script.actions.Ephemeris; 

Ephemeris.getBankHolidayName();

BR S

Hi, Seaside.

I’ll create the java file and test it.

About Ephemeris…

.model is not part of the package. Should I manually import it from somewhere? If yes, where can I find it?

If you build jrule locally with maven you can find the core jar in your m2-repo.

I.e on my linux host it is under: .m2/repository/org/openhab/core/bundles/org.openhab.core.automation.module.script/4.0.0-SNAPSHOT/org.openhab.core.automation.module.script-4.0.0-SNAPSHOT.jar

You should also be able to find it on an openhab installation somewhere under:
/usr/share/openhab/runtime/system/org/openhab/core/bundles/

I created the Java file, copied the code from the Scale file, and modified it to comply with the Java format. I compiled and uploaded the .jar to the server and, after fixing some minor things, uninstalling JRule, and reinstalling it, it worked, even the rule fired properly after changing the status of a Thing.

Then I copied just the rule code and pasted it back into the Scala file, modified it to match the Scala format, compiled it, and uploaded the .jar file… it worked. :smiley:

I don’t think it was related to uninstalling and reinstalling JRule. Here is the working Scala code:

def reportOfflineThing(event: JRuleThingEvent): Unit = {
        logInfo("Report Offline Thing > STARTING.")
        val thingID = event.getThing()
        logInfo(s"Report Offline Thing > Triggering Thing: ${thingID}.")
        val thing = JRuleSubThing.forName(thingID)
        logInfo("Report Offline Thing > Restarting Thing.")
        //Notification().sendNotification(s"Rosie > ${thing.getLabel()} changed to ${event.getStatus()}.")
        thing.restart()
        logInfo("Report Offline Thing > Waiting for Thing to restart.")
        logInfo("Report Offline Thing > Checking Thing status.")
        createOrReplaceTimer("Test", Duration.ofSeconds(10), { (t: JRuleTimerHandler#JRuleTimer) =>
            if ("ONLINE".equals(thing.getStatus().toString()))
                Notification().sendNotification(s"${thing.getLabel()} changed to ${event.getStatus()} 10 seconds ago.")
                    logInfo("Report Offline Thing > Notification sent.")
            else
                Notification().sendNotification(s"${thing.getLabel()} changed to ${event.getStatus()} 10 seconds ago.")
                logInfo("Report Offline Thing > Notification sent.")
        })
        logInfo("Report Offline Thing > ENDING.")
    }
1 Like

I see the same thing, for some (yet unknown) reason dynamic rule reloading sometimes hangs which results in no rules being executed and all events buffered.

I’ve yet to try to figure out why it happens, but a bundle:restart <bundleid> always brings JRule back to life.

I’ve never seen this. Could perhaps be related to not having any dynamic rules?

I’ve built a new release. @seime seen the issue lately?

BETA21

1 Like

I should have been more clear in my wording, and I see that my sentence could be misinterpreted. By dynamic I mean deploying updated rules after the initial start of jrule. I do not think it has anything to do with the new dynamic rule concept.

Im running HEAD from just before the weekend, and I still occasionally get hickups. I’ll take a deeper look at it a bit later.

Cheers

1 Like

Hi, @Seaside.

Could you please recompile the JAR for 4.1.2? Yesterday I updated my OH to 4.2.1 stable and now JRule is no longer installed, moreover, it cannot be installed from the Automations tab in the Add-ons Store on the front end.

I’m guessing, it is because the current one is only compatible with 4.0

Yes we can lift it to 4.1.2. But it requires some work.

I’m running it with 4.1.2. if you install the jar manually it will work. I think the problem with marketplace is the naming of the file 4.0.0 in the name.

I can’t seem to trigger properly.

import static org.openhab.automation.jrule.generated.items.JRuleItemNames.*;
import org.openhab.automation.jrule.rules.event.JRuleEvent;
import org.openhab.automation.jrule.rules.JRuleName;
import org.openhab.automation.jrule.rules.JRuleWhenItemChange;
import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedUpdate;
import org.openhab.automation.jrule.rules.JRule;
import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.OFF;
import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.ON;
 
public class BkmTest extends JRule { 
    @JRuleName("MyRule")
    @JRuleWhenItemReceivedUpdate(item = statusHumidity_CCNW)
    @JRuleWhenItemReceivedUpdate(item = statusHumidity_CCC1)
    @JRuleWhenItemReceivedUpdate(item = statusHumidity_CCK1)
    @JRuleWhenItemReceivedUpdate(item = statusHumidity_CCSW)
    public void myExec() {
        logInfo("||||| --> Hello World!");
    }
}

In my log file, I’m getting:

==> /var/log/openhab/openhab.log <==
2024-05-04 13:21:31.203 [INFO ] [internal.events.JRuleEventSubscriber] - [JRuleSubscriber] Event processing paused, queueing event Item 'statusHumidity_CCNW' shall update to 75
==> /var/log/openhab/events.log <==
2024-05-04 13:21:31.204 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'statusHumidity_CCNW' changed from 74 to 75

Is the rule paused? How do I enable it? It’s not listed in my rules.
Saving the java file results in this:

2024-05-04 13:23:42.292 [INFO ] [rule.internal.compiler.JRuleCompiler] - [JRuleCompiler] Number of classes to load in to memory: 53 folder: /etc/openhab/automation/jrule/gen
2024-05-04 13:23:42.299 [INFO ] [.jrule.internal.handler.JRuleHandler] - Instantiated JRuleItems class
2024-05-04 13:23:42.300 [INFO ] [.jrule.internal.handler.JRuleHandler] - Instantiated JRuleThings class
2024-05-04 13:23:42.300 [INFO ] [.jrule.internal.handler.JRuleHandler] - Instantiated JRuleActions class
2024-05-04 13:23:42.300 [INFO ] [rule.internal.compiler.JRuleCompiler] - [JRuleCompiler] Number of classes to load in to memory: 1 folder: /etc/openhab/automation/jrule/rules

It is a bug in the rule reloading that causes it to hang sometimes.

Workaround is to restart the jrule bundle via openhab-cli

I restarted the bundle, but am not getting anything in the openhab log.

Is there a howto on setting up JRule in a VSCode environment?