JRule - openHAB Rules using Java

Glad to hear you like it. I’m currently rewriting my old Halloween rules and stumbled on similar issues with my color bulbs. The generated class should not be empty, it should be the plain class similar to string. I’ll add so you can send HSBTypes on top of that.
Any thing else you would like in the color class? Otherwise I’ll post an update when I have tested it some more.

BR S

You’re right. It’s not empty, but also does not contain any useful methods. It looks like this:

public class _colorDiningRoomLight extends JRuleItem {

    public static final String ITEM = "colorDiningRoomLight";
}

Ideally the color class would support the commands mentioned in the item docs (OnOff, IncreaseDecrease, Percent, HSB) but I guess HSBType should do it for most cases.
While looking through the classes for my items, I noticed more of those like above:

  • Rollershutter
  • Contact

The group types though seem to have methods for getting the state (as string) and updating, so maybe this could be a workaround for now.
Anyways. Looking forward to the next release and thanks a lot. :slight_smile:

THIS IS EXCELLENT.
The infinite pleasure of a good strongly typed langage with perfect auto completion.
Your automation bundle is so simple to use, and yet so powerful. Many many thanks.

I have a question : is there any support for generic action, those provided by a binding for example ?
(the equivalent to actions.get(<binding_name>, < thing> ) that we can use in other JSR223 or DSL)
Or is this what you imply when you said no “script actions” in “Limitations” ?

Anyway, I’m looking forward to see other improvements/possibilities. Thanks again.

1 Like

I had a look on it and it really depends on specific binding. Finding action is one thing, firing it is another. Some of bindings provide interface which you can use from outside (ie. org.openhab.binding.alarmdecoder.internal.actions.BridgeActions implements ThingActions, IBridgeActions) gain at least some type safety. Other ones which are available within scripts use annotations and private implementation classes making it impossible to refer it externally other than mapping inputs/outputs and action (method) names.

@dalgwen I have added Color items now.
I have built the first BETA version if you have and rules with
with for instance


import static org.openhab.automation.jrule.rules.JRuleOnOffValue.OFF;
import static org.openhab.automation.jrule.rules.JRulePlayPauseValue.PAUSE;
import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON;

Or similar, the have now been moved to:

import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.OFF;
import static org.openhab.automation.jrule.rules.value.JRulePlayPauseValue.PAUSE;
import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.ON;

Regards S

Regarding actions for bindings, I have not looked into that, is there any specific binding you had in mind or just in general?

splatch Seem to have some insights. If it’s possible to call them from other JSR223 or DSL, at least in theory it should be possible.

I have been working for 25 years in Java, so I was thinking of writing openHAB rules in Groovy in pure Java syntax, but the helper libraries currently only support Jython very well.

So I am very thrilled of discovering your JRules.

It is a great idea, great work and an amazing enhancement to openHAB for programmers.

Still, even if the idea seems perfect to me, not everything is perfect yet, please allow me to give some feedback:

is the org.openhab.automation.jrule.rules.user package necessary or would another or the default package be sufficient?

The runtime would have to watch everything below jrule/rules

The protected String getRuleLogName() seems unnecessary bloat

why not just an optional annotation @Logger(“MY_TEST”) ?

I don’t like the underline in front of the item class name,

the package suffices as name space.

pure-annotation based Rules without inheritance

public class MySwitchRule extends JRule

couldn’t that be

@Rule
public class MySwitchRule

The rules should be pure-annotation based, no need for inheritance.

why do you need generated item classes in the first place?

to have a strongly typed item reference?

@JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON)

If you’d use the item String id as item reference, there’d be no need for the item classes, everything could be done in the runtime using the item and thing registries. I find it natural to use the item id in Java rules, too.

And no need for an ever changing items.jar in Eclipse or Maven.

@When(item = “MyTestSwitch”, trigger = JRULE.TRIGGER_ON)

The runtime can look up what TRIGGER_ON means for MyTestSwitch.

All (verbose!) compiler output should go into the log

2021-10-20 10:23:21.071 [INFO ] [rule.internal.compiler.JRuleCompiler] - Error on line 3 in file:///home/openhab/test/openhab-3.1.0/conf/automation/jrule/rules/org/openhab/automation/jrule/rules/user/MySwitchRule.java

(this is caused by the JRuleOnOffValue.ON import)

Or what about creating in the source folder a compiler output file in the spirit of JBoss’s .failed files?

MySwitchRule.java.failed containing all compiler output

additional classes in the rules package?

You cannot simply add additional classes that are no rules, can you? I don’t see them getting compiled.

Like

class MyDTO

and in the rule

String s = MyDTO.name;

With an @Rule annotation the JRules runtime can see what are rules and what are not.

The toplevel readme.md should show the minimal maven pom to build the rule.
I’d like a line about how to trigger the rule

In the Gui switch on the item, or …

Or what about a MySwitchRule.trigger file that changes in MySwitchRule.done or MySwitchRule.failed containing Exception stacktrace?

remote debugging

A huge advantage of Java rules is that you can remote debug them from Eclipse, this should go in the readme.

Greetings,
Jürgen

Much appreciated. This is exactly the kind of feedback I’m looking for.
Don’t be afraid to submit a PR if you want to help out.

Yes I think it would be possible, it was easier to have set package when doing class loading, you would probably have to read the package beforehand in order to be able to class load the rules from within java.

I agree will change that, or add the possibility to have loggers via annotation.

The underscore is for the generated classes only. It’s a least to me easier to find
the items you are looking for when using ctrl + space. I guess it could be configurable to use it or not, or even decide your own prefix.

The inheritance is mainly to be able to use predefined methods, like createOrReplaceTimer, sendCommand,logInfo and similar without having to call a helper class. This could be changed to use a helper class, I like the inheritance structure for this, but I have not made up my mind.
You also don’t have to use the extend JRule, I think you would be able to skip that part without the extend as long as you annotate your method.

Yes and to get help when writing you rules for the items. Using the generated items, you are guaranteed that it will reflect the actual item. If you would have it more loosely coupled you would have to remember your item names by heart, or at least always check what they are named, and you could then possibly spell it wrong when calling sendCommand(item). You can in fact skip the generated classes if you prefer and call JRuleStringItem.sendCommand(“myItemName”, “My str update”);
It is also used for the triggers in order to know which item is triggering the rule, that could I suppose also just be a string, but by using the generated items you sort of know it will work.

The item= could be removed I believe but see my answer above. I was planning to use the item annotation reference for timer locks etc, to support that type of annotations, but could be done differently. The trigger=_item is there for the reason described above.

Yes could remove the item and only have trigger like this:

@JRuleWhen(trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON)

Yes, I like that idea.

Yes you can add classes without annotation and use them. They will not be compiled as rules, but it’s fine to use. See example 5 and 6 for instance.

I’m not using a pom for that. Typically you would develop the rule and have it compiled in your java idea with the jrule.jar and jrule-items.jar as dependencies. The jrule engine will compile the classes for you when they are placed in the jrule-folder. I guess if you want to run automatic tests using Jenkins on the rules you could use a pom. I don’t see a use case myself, but seems simple enough to add a pom if it adds some value?

How to trigger the rule I guess it depends on the trigger or rule. Any way you can use openhab to alter state of items could trigger the rule depending on what happens. I’m not sure I’m following. I like the idea about having logging for stacktraces related to the trigger in files, that could probably help a lot. I guess you could also output statistics on how often the rules are triggered, average time for the rules etc. I could see that hooked up to grafana to get an idea of what your system is doing. Great ideas!

Yes could add instructions for that. A lot of people tend to use IntelliJ and Netbeans as well.

Regards, S

Eclipse project

I have set up an Eclipse project OpenHAB-JRules, linked conf/automation/jrule/rules as source folder into the project, added the jars from conf/automation/jrule/jar (why isn’t this called lib?) and was up and running. Non-Rule helper classes do work.
Started openHAB with start_debug.sh, created in Eclipse a new Remote Java Application Debug Session to openHAB’s 5005 debug port, added a breakpoint to the rule, triggered the rule and the breakpoint was hit. Perfect.

If the thread is stuck too long on a breakpoint, openHAB complaines in the log, but so do application servers.

.failed bad idea

My idea with .failed files isn’t good after all, this would pollute the source folder.
Better all compiler output to the log.

source jars

Could you add sources jars to the lib and items jars?

Presets

JSR223 Scripts get a lot of presets into their scope, not needed for jrules?
Neither have Rules a reference to the runtime, it seems.

Windows

Not a problem for me, but why are jrules not supported in Windows?

no script actions

How would one do this dsl code in a jrule?

val mailActions = getActions("mail","mail:smtp:mysmtp")
mailActions.sendMail("email@gmail.com", "subject", "content.")

Greetings, Jürgen

Thanks a lot for the color item support. It works great!
I tried to setup a dev environment locally for the addon so that I can contribute. Had some trouble though. (I’m not a maven expert, more in the Scala/SBT area) What I ended up doing was cloning the openhab-addons repo and copying your addon to the bundles folder. After that I could compile the code. One thing I noticed was that there were some rules complaining about the copyright header:

[ERROR] org.openhab.binding.jrule.internal.JRuleAnnotationTest.java:[2]
Header line doesn't match pattern ^ \* Copyright \(c\) 2010-2021 Contributors to the openHAB project$

I took the opportunity and replaced the 2020 with 2021. After that, all errors were gone and I added support for the OpenClosedItem (Contact). Here’s my PR for this. Don’t be scared about the number of files I touched. I thought it makes sense to just commit the copyright year change as well.

Cheers,
Timo

1 Like

Nice that you got remote debug working, I’ll try it out as well. Yes could be called lib. I’ll try to add it to my change list of things.

Well you could have in a different folder, it would take some testing to get it working, I still think the idea itself is still good.

One goal has been to reduce the jars. Adding source jars would maybe be nice for development so you can peak into the code. I’ll note it down as a good idea.

Some are available like PlayPause, On etc. It was a design decision to not have any dependencies on OpenHAB core when developing rules. The benefit is that you will only need to add dependencies on jrule and jrule-items, nothing else. The downside is that jrule needs to wrap those classes (OnOffType etc). JRule could be refactored to expose core-classes, or the remaining items could be implemented, either way would work. Obviously I’m working towards wrapping it since I see some benefits I like with it.

Supported to do development in windows, but not running openhab jrules in windows. The only reason is that I don’t run openhab in windows. It would probably be easy to add support for windows (changing file paths (/etc/openhab/automation etc) and path separators). If anyone wants windows support I would welcome a PR for it.

It could be implemented in the same way as “say” and “commandLineExecute”. It’s not implemented at the moment.

BR S

Thank you, I’ll check the pr out as soon as possible.

Regards s

Thanks for merging, I’ve made another PR :grin: to be more complete according to the docs. The following commands/items have been added:

  • Color (changed)
    • OnOff (command, state)
    • IncreaseDecrease (command)
    • Percent (command, state)
  • Dimmer (changed)
    • IncreaseDecrease (command)
  • Rollershutter (new)
    • UpDown (command)
    • StopMove (command)
    • Percent (command, state)

Cheers,
Timo

Awesome! I’ll try it out.

Br S

I don’t agree, because for say or commandLineExecute in JRule you are invoking components from openhab-core, but when you would like to execute any different action not coming from openhab-core but from openhab-addons (you can not predict even if the actions from an addon will be available on a classpath) it is impossible for now. You can deal with that by exposing a rule using ruleDSL with a “proxy” item to call the given action defined in the ruleDSL. Using reflection maybe we could achieve invoking custom actions from addons. I will look at this in the nearest future.

1 Like

Sounds good. I have not looked into this myself.
I’m out travelling, but will be home next week and will do some work on the binding once I’m home.

Regards S

I was just trying to convert my window sensor rules which look basically like this:

rule "Fenster offen gelassen"
    when Member of groupKontakte changed
then
...

Is there an equivalent in JRule? I though the group example would fit here:

 @JRuleName("groupMySwitchesChangedOffToOn")
 @JRuleWhen(item = _gMySwitchGroup.ITEM, trigger = _gMySwitchGroup.TRIGGER_CHANGED, from="OFF", to="ON")
    public synchronized void groupMySwitchesChangedOffToOn(JRuleEvent event) {
        logInfo("Member that changed the status of the Group from OFF to ON: {}", event.getMemberName());
    }

The problem is that it relies on the GroupItemStateChangedEvent which is not fired at all. The OH-native rule with Member of still works (and does not seem to use that event either). Is there anything I misunderstand here?

Cheers,
Timo

I think I have this working, but need to test it when I get back home. It would be good if you could share what events.log show when it is changed.

Regards S

This is my item definition:

Group groupKontakte
Contact openCloseDachfensterLinks "Kontakt Dachfenster links [%s]" <door> (groupLivingRoomDachfensterLinks, groupKontakte) ["OpenState"] { channel="deconz:openclosesensor:conbee2:aqaraDachfensterLinks:open" }

And when I open the window, this gets logged:

2021-11-06 11:29:25.370 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'openCloseDachfensterLinks' changed from CLOSED to OPEN
2021-11-06 11:29:25.383 [INFO ] [hab.core.model.script.windowReminder] - New timer created for: linkes Dachfenster

The second line is my OH-native rule which gets triggered. As I said, I’ve never seen the GroupItemStateChangedEvent logged at all but Member of still seems to be able to handle it.

Cheers,
Timo

I run this with presens-detection with a switch item, and it seems to work.
Like this:

Group:Switch:OR(ON,OFF)    gUnknownPresent            "Unknown Detection [%s]"       <network> 
 @JRuleName("UnknownDetection")
  @JRuleWhen(item = _gUnknownPresent.ITEM, trigger = _gUnknownPresent.TRIGGER_CHANGED, to = "ON")
    public synchronized void gUnknownDetection(JRuleEvent event) {
        double timeout = getDefaultValue(_UnknowDetectionTimer.getState(), 15) * 60;
        String message = "UnknowDetection turned on for duration: " + timeout + " trigger by: " + event.getMemberName();

I can see this in my logs:
2021-11-06 21:21:14.174 [INFO ] [openhab.automation.jrule.rules.JRule] - [PRES] UnknowDetection turned on for duration: 900.0 trigger by: ZwaveEye1Motion

Try with a switch as above and see if it works. Also I guess if you enable debug it might be more visible to see what is happening.

Regards, S