JRule - openHAB Rules using Java

There has been some refactorings so it could be related to that.
Please report a bug on github and we will try to fix it.

Done

1 Like

Hi, everyone.

Is there any conversion for this DSL rule trigger?

System reached start level 50

How to find items dynamically?

tempItem = ScriptServiceUtil.getItemRegistry.getItem("Roomba_Space_" + position)

And how can I change the log level in the Karaf console? Before I ran the following command, changing the log level and log name:

log:set DEBUG org.openhab.core.model.script.Switches
1 Like

AFAIK not for the time being, but feel free to add a feature request or PR at GitHub - seaside1/jrule: openHAB Java Rules Engine :slight_smile:

JRuleItem.forName("dynamic_item_name")

I would however recommend you to use the typed version, ie for a Switch item use JRuleSwitchItem.forName("dynamic_item_name")

For full example see jrule/doc/EXAMPLES.md at main Ā· seaside1/jrule Ā· GitHub

Regarding logging see GitHub - seaside1/jrule: openHAB Java Rules Engine

Thanks for your reply, @seime
Iā€™ll create the feature request.

I had seen this in the examples but somehow I got confused by the example description ā€œcreate itemā€ :smile:

This one actually doesnā€™t solve my doubt. I had read the documentation and I know how to log something in the code. Moreover, I use the following instruction within my rules.

@JRuleLogName(ā€œCustom_Lognameā€)

The problem is I donā€™t know how to set the Karaf console to show me the debug, or trace logs generated through JRule.
I know how to do it with Rules DSL, but the same approach didnā€™t work for the JRule logs. I didnā€™t see that part explained in the documentation.
What package should I set to DEBUG, for example?

Did you try log:set DEBUG Custom_Logname?

If not, maybe @Seaside knows - Iā€™m not using this myself.

You can enable debug logging for JRule with:

log:set debug org.openhab.automation.jrule

However using debug / info / trace / error is usually not how JRule-rules has been using the logging feature. If you enable debug log you will get a lot of debug logs from the JRule-engine itself.

Using @JRuleLogName on rules etc it is not possible to state if it is to be logged as debug info etc. It will be logged to info.

If you want to handle logging on your own you can do so. You just have to add dependency on slf4j and use that logger. I used to do this early on, but nowadays all my logging from rules are done to info.

/S

Thanks for your reply, @Seaside.

By this, do you mean this?

You can add logger to your rules class and log however you want.
Something like:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClass {

 private static final Logger logger = LoggerFactory.getLogger(MyClass.class); // (Or something else)

public void myMethod() {
   logger.debug("My log message {} and some other message {}", "My logg message", "some other message");
}

Whatever package you are using (I think) will be used as prefix in openhab when you log.
So you should be able to set log level (debug, error, info) etc with that package in the karaf console.

Look at this: https://github.com/seaside1/jrule/blob/main/src/main/java/org/openhab/automation/jrule/internal/JRuleLog.java

You can also set log level in log4j2.xml typically in ā€¦/userdata/etc/log4j2.xml, it is also possible to log to a specific file if you want to do that.

1 Like

BETA20

Hi.

Does anyone have a working rule with a timer? Iā€™m not sure if the example in the documentation is incorrect or my compiler doesnā€™t get it right, and I couldnā€™t figure out how to solve it.

I get the following error:

Iā€™m using Scala, but I donā€™t think thatā€™s the issue. Here is my test rule:

@JRuleName("myTimerRule")
@JRuleWhenItemReceivedUpdate(item = JRuleItemNames.MBR_Scene_Study_White)
def Timer_Test(event: JRuleItemEvent): Unit = {
        //JRuleItems.group_Lights.memberItems().forEach(f)
        createOrReplaceTimer("Test", Duration.ofMinutes(1), new Runnable() {
            @Override
            def run(): Unit = {
                logInfo("Time is up! Am I supposed to do something?")
            }
        })
    }

I can see that the definition of the createOrReplaceTimer in the JRule.java class indeed requires a function, not a Runnable, and the function must have a JRuleTimer as a parameter:

/**
     * Creates or replaces a timer.
     *
     * @param timerName Name of the timer or null.
     * @param delay Initial delay and delay between the timers.
     * @param function Code to execute.
     * @return A handle for the timer.
     */
protected JRuleTimerHandler.JRuleTimer createOrReplaceTimer(@Nullable String timerName, Duration delay,
        Consumer<JRuleTimerHandler.JRuleTimer> function) {
        return JRuleTimerHandler.get().createOrReplaceTimer(timerName, delay, function, null);
    }

I can of course create a separate function that takes a JRuleTimer as a parameter, but what value should I pass on to it as an argument?

I feel Iā€™m within an Inception :smiley:

Yes the timers have been refactored a couple of times, so documentation should be updated.
You no longer pass a runnable, but a lambda function:

Very simple function would be

  createOrReplaceTimer(ZwaveRelaySwitch1, Duration.ofMinutes(12), (Void) -> { // Lambda Expression
                sendCommand(ZwaveRelaySwitch1, OFF);
        });

Notice that you can chain timers also
createTimer(ā€¦).createTimeAfter(ā€¦)
You can do repeating timers and much more.

/S

I also saw that in Example 32 (by the way, itā€™s missing the anchor) and I tried it in different ways, but I always get the same error because the compiler expects a function that has a JRuleTimer as a parameter.

Test with a lambda:
image

Test with an explicit function:

From the screenshot it does not look like you have the correct syntax for a lambda (at least in java)

(Void) -> { // Lambda Expression
                sendCommand(ZwaveRelaySwitch1, OFF);

Notice void and ->. If you can try it in java and see if you can get it working, then it might be easier to translate to scala.

It seems to compile

Iā€™ll have to figure out how to convert that (Void)

how is it different to rescheduling a timer?

If you reschedule a timer, you typically execute the same code again at a later stage.
This gives the option to execute something else when the timer finishes.
You can do this by executing multiple timers, but then you have the problem of determining when they finish.

As an example:
Using chaining timers to execute different code when finished:

chainTimer1.runAfterSeconds(10).createTimerAfter(5).createTimerAfter(6);
Last timer will start executing 6 seconds after the two first timer are finished which would be 10 + 5 + execution time for the first two timers.
vs
asyncTimer1.runAfterSeconds(10);
asyncTimer2.runAfterSeconds(10+timer1Exectime+5);
asyncTimer3.runAfterSeconds(10+timer1Exectime+5+timer2Exectime+6);

So to wrap up, it gives you possibility to right after one timer has finished to execute something else.
I use this among other things to control lights, smoke and other effects on my Halloween lightshow when someone rings the doorbell :slight_smile:

So itā€™s a nested timer but with a chaining syntax?

Yes.

Just in case, after trying for a while I could make the timer work with Scala. Here is the conversion from Java:

package org.openhab.automation.jrule.rules.user

import org.openhab.automation.jrule.rules.{JRule, JRuleName}
import org.openhab.automation.jrule.generated.items.{JRuleItems, JRuleItemNames}
import org.openhab.automation.jrule.rules.event.JRuleItemEvent
import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedUpdate
import org.openhab.automation.jrule.rules.value.JRuleOnOffValue.*
import java.time.Duration
import org.openhab.automation.jrule.internal.handler.JRuleTimerHandler

class Tests extends JRule {
    @JRuleName("myTimerRule")
    @JRuleWhenItemReceivedUpdate(item = JRuleItemNames.MBR_Scene_Study_White)
    def Timer_Test(event: JRuleItemEvent): Unit = {
        logInfo("Creating timer.")
        createOrReplaceTimer("Test", Duration.ofMinutes(1), { (t: JRuleTimerHandler#JRuleTimer) =>
            logInfo("Time is up! Am I supposed to do something?")
        })
    }
}
2 Likes