JRule - openHAB Rules using Java

openHAB Rules using Java

logo

This automation package aims to enable Java development of openHAB Rules. The addon will allow the user to create custom openHAB rules in one or several .java-files. The Java Rules will need to define triggers in order for the rules engine to know how and when to execute them.

The triggers are very similar to the triggers in Rules DSL but expressed using java annotations. In order to execute rules based on items defined in openHAB either in .items-files or the GUI. The addon needs to know about these items and this is realized by the Rule Engine by generatinga .java and a .class file for each item in the system. The class files are then packaged in a .jar-file which the user can use as a dependency when doing Rules Development.

For the addon to be able to pick up rules, they first need to be compiled by the rules engine. The source .java rules-files are placed in a specific rules folder and will be automatically compiled and loaded into openHAB when the rules engine is started. The syntax for rules as well as the design and thinking behind the addon is to provide something that is similar to Rules DSL but more powerful, flexible and customizable.

Bugs

Any bug you find is good to report. Preferred way of reporting bugs is to create an issue on github: Issues · seaside1/jrule · GitHub

Contribute

Create a Pull Request on github: Pull requests · seaside1/jrule · GitHub

Changelog

BETA21

BETA20

Version BETA19

Version BETA18

  • Refactored timers and added method for checking for timer lock by seaside1 PR #171
  • Added the ability to configure the name of the package in jrule.conf by kuimovvg PR #170
  • Add previousState and a test by querdenker2k PR #168

Version BETA17

  • Fix for thing actions without @ActionInput by seimePR #143
  • Store jar file as build artifact by seimePR #144
  • Merge test packages by seimePR #145
  • Only consider methods declared in rule class, not inherited methods by seimePR #147
  • Reduce loglevel to debug by seimePR #148
  • Send updates/commands to group items itself, not just members by seimePR #150
  • Reduce flawky builds by querdenker2k PR #151
  • Notify for rule runs by querdenker2k PR #152
  • Update to openhab 4.0.0 by sealside1 PR #153
  • Add reschedule by querdenker2k PR #155
  • Fix Timer:isRunning by querdenker2k PR #157
  • Print useful exception trace while InvocationTargetException by querdenker2k PR #160
  • Removed OS dependent configurations by sfdumas PR #164

Version BETA16

  • Enrich memberOf with options to just listen on items or groups by querdenker2k PR #95
  • Add http action by querdenker2k PR #97
  • Fixed typo by thwint PR #98
  • Log exception stacktrace by querdenker2k PR #99
  • Enhance and cleanup the internal timers by querdenker2k PR #100
  • Improve trigger channelname conversion and error reporting by seimePR #103
  • Add previousCondition to JRuleWhenItemChange by querdenker2k PR #104
  • Let transform throw a runtime exception by querdenker2k PR #106
  • ForNameOptional, UnspecifiedGroupItem, QuantityItem by querdenker2k PR #107
  • Fix NPE with watchingForItem while not completely initialized by querdenker2k PR #110
  • Fixed contact item issue by seaside1 PR #114
  • Add tests for historicState NPE, add toString for values by querdenker2k PR #118
  • Fix merge (transform exception) by querdenker2k PR #119
  • Add metadata and tags getters for items by querdenker2k PR #121
  • Add delayed execution, remove unused JRuleOr by querdenker2k PR #122
  • Remove illegal characters from generated channel name by seimePR #124
  • Reduce loglevel for non-annotated java methods in rule classes by seimePR #125
  • Fix member of by querdenker2k PR #126
  • Make Item constants constant by querdenker2k PR #128
  • Exclude lambdas while scanning methods by querdenker2k PR #129
  • Exclude persistence verifications by querdenker2k PR #130
  • Memberitems type specific by querdenker2k PR #131
  • Fix repeating timers by querdenker2k PR #133
  • Let JRuleItemEvent contain JRuleItem by querdenker2k PR #134
  • Restructure documentation by seimePR #135
  • Fixed group item class generation by gerriegPR #138
  • Remove direct item registry update causing new events to be produced by seimePR #140
  • Spelling fixed rdrxr PR #141

Version BETA15

  • BREAKING: All JRuleWhen has to be change to corresponding JRuleWhenItemChanged (as an example, look at JRule Examples documentation)
  • JRule When refactoring by querdenker2k PR #61
  • Thing Channel triggers by seime PR #62
  • Generate Actions by querdenker2k PR #63
  • Add option to get groupMembers as Items by querdenker2k PR #65
  • Memberof Trigger by querdenker2k PR #66
  • Fix buffer being read twice and breaking classloading by seime PR #67
  • Fix missing precondition support for timer rules by seime PR #68
  • Fix timer trigger by querdenker2k PR #70
  • Initial tests for JRuleWhenItemChange triggers by seime PR #73
  • Threadlocal logging - some improvements by seime PR #79
  • Junit test for duplicate rule invocations by seime PR #75
  • Add docker integration test by querdenker2k PR #77
  • Include old thing status in event by seime PR #80
  • Use thread safe list instead of arraylist by seime PR #81
  • Defer to parent classloader if file not found by seime PR #83
  • Fix inheritance in actions by querdenker2k PR #87
  • Fix mqtt for tests by querdenker2k PR #91
  • Fix ConcurrentModificationException in test by querdenker2k PR #92
  • Added typing for thing channel triggers, ie JRuleWhen(channel = binding_thing.triggerChannel) instead of typing the channel id string

Version BETA14

  • Thing support in rules by seime pr #59
    -BREAKING: jrule-items.jar has been renamed to jrule-generated.jar
  • Added missing sendCommand for StopMove commands by seime pr #57
  • Fixed parsing of double value for Quantity type by seime pr #56
  • Added generic action handler by querdenker2k pr #55 see exampe #34
  • Refactoring of event for channel plus cleanup by querdenker2k pr #52
  • Refactoring of persistance functions and item handling with exceptions by querdenker2k pr #51
  • Added item id and fixes for generated items by LumnitzF pr #50
  • Added MDC Logging tags to be used with elastic search (logstash,kibana and similar) by querdenker2k pr #49
  • Fixed parsing of double values in rule conditions by seime pr #48
  • Fixed parsing of UNDEF by seime pr #45

Version BETA13

  • Fixed a bug with naming of JRuleItems.java
  • Fixed issues with post and sendCommand to groups

Version BETA12

  • Major refactoring by seime #42
  • Replaces the templating mechanism with Freemarker, mainly to allow more advanced constructs such as loops - and to avoid all the repetitive code in the template files
  • Generates a new file Items.java which looks a bit like public class Items { public static MySwitchItem SwitchItem = JRuleItemRegistry.get(“MySwitchItem”, MySwitchItem.class); public static MyStringItem StringItem = JRuleItemRegistry.get(“MyStringItem”, MyStringItem.class); }
  • Adds a skeleton support for LocationItem (which was missing)
  • Adds a new field LABEL (item label)
  • Adds a few convenience methods such as getLabel() and getName()
  • Adds more typing of Group items

Version BETA11

  • Wrap TransformationException in JRuleExecutionException by seime #39
  • Add equivalent postUpdate logging as sendCommand by seime #38
  • Fix group sendCommand for UpDown by seime #36
  • Added eq and neq to channel event by gerrieg #35
  • Added support for ZonedDateTime in DateTimeItem by gerrieg #34
  • Fixed issued with undef item for state
  • Added mocked eventbus for testing rules with junit

Version BETA10

  • Optimized items by gerrieg #33
  • Syntax change: event.getValue(), event.getValuesAsDouble() etc replaced with event.getState().getValue() and event.getState().getValueAsDouble()
  • Syntax change JRuleSwitchItem.sendCommand(myItem, ON) replaced with JRuleSwitchItem.forName(myItem).sendCommand(ON)

Version BETA9

  • Fixed bug with item generation and forName overloading

Version BETA8

Version BETA7

  • Fixed item for Number:Quantity, you can now send a quantity type in the command see example 26
  • Added precondition see example 24 and 25 by seime: #30
  • Added possibility to use subdirs and packages for rules by seime: #30

Version BETA6

  • Added seprate thread executors supplied by seime: #23
  • Added Windows Support by LumnitzF #24
  • Added cancellation of repeating timers by seime: #26
  • Null check on timers with futures by seime #29
  • Update to Openhab 3.3.0
  • Fixed rescheduling of timers by seime: #28
  • Added check for repeating timers by seime: #27
  • Fixed repeated timer executing directly by seime: 25
  • Added transformation sevice example 23 by seime: #20
  • Improved compiler error logging by seime: #12
  • Added UP/Down support for group items by seime: #13
  • Quantity Type support by seime: #14
  • Configurable item-prefix by weberjn #16
  • Improve exception handling by No3x #19
  • Made thread executors configurable, default disabled
  • Possible to build jrule standalone #24 or together with Openhab-addons
  • Cleaned up repostiory from binary files. Remove and old clones of jrule and clone a fresh repo

Version BETA5

  • Addes support for adding rules in jar-files, as an alternative.

Version BETA4

  • Added config for character to be used when generating items files

Version BETA3

  • Major refactoring of logging

Version BETA2

  • Fixed color item
  • Added annotation for setting logger on a rule see example 21 and 22
  • Optional to override getLogName on class
  • Contact item update
  • Rollershutter item support added
  • UpDown, Increase Decrease support added to various items
  • OnOff and Percent commands added to ColorItem

Version BETA1

  • Added color item see example 20
  • Moved org.openhab.automation.jrule.rules.JRuleOnOffvalue, JRulePlayPause etc to org.openhab.automation.jrule.rules.value

Version ALPHA12

  • Fix some language typos, some refactor of java classes, improved initialization of singletons due to concurrency aspects

Version ALPHA11

  • Added check for working dir via system properties

Version ALPHA10

  • Added LatUpdate via JRulePersistenceExtentions see example 19

Version ALPHA9

  • Added cron expressions for rules see example 18
  • Bug fix by @roth for reloading channel triggers

Version ALPHA8

  • Channel triggers provided by @roth see example 17

Version ALPHA7

  • Fixed bug with group member value was null for non StringType types

Version ALPHA6

  • Added group functionality getMember will return who triggered a change for a group

Version ALPHA5

  • Removed dependencies on slf4japi and eclipse annotations
  • Added logInfo logDebug (to wrap slf4j and remove dep)
  • Fixed compilation of rules to be more robust with internal dependencies

Version ALPHA4

  • Refactored completable futures
  • Added 5 seconds of delay for initialization of the rule engine to avoid multiple reloads
  • Added support for play & pause for player item
  • Added commandLineExecute

Version ALPHA3

  • Fixed issue when reloading rules if they are changed with monitored items
  • Fixed classpath issue when executing rules using 3rd party libraries

Version ALPHA2

  • Added possibility to include 3rd party libraries when developing rules

Version ALPHA1

  • Refactored internal jar dependencies and jar-generation
  • Added eq comparator for number triggers in rules

Resources

https://github.com/seaside1/jrule/releases/download/jrule-4.0.0-BETA21/org.openhab.automation.jrule-4.0.0-BETA21.jar

https://github.com/seaside1/jrule/

16 Likes

I’ve opened up the repository now. It is working ok now I believe. Still expect major updates and changes. Anyone who want to contribute or change stuff please submit a PR.

/S

A few comments on usability for a wide openHAB audience:

  • If only .items files are supported that will be a deal killer for a huge portion of OH 3 users. More and more users are migrating to UI managed Items and new users never use .items files at all. If you were to support managed Items through REST API calls (or internal API calls) then you’d be able to get the Items from both .items files and managed since the .items files Items are reflected in the REST API as well.

  • If the .java files are only picked up when the binding starts, that means one needs to restart OH or at least restart the binding which becomes really awkward if in a develop/test loop working on rules.

  • The standard folder for JavaScript, Python, and Groovy rules (maybe jRuby too?) is /etc/openhab/automation. /etc/openhab/rules and /etc/openhab/scripts is legacy. Speaking only as myself, I’d prefer to see this jrule folder default under automation for consistency.

  • I would have expected this to be an automation type add-on, not a binding. It is counter intuitive to have to create a Thing to enable rules. I believe there is a way to specify add-on configuration through the UI without requiring a Thing.

  • This appears to be completely incompatible with UI created rules. So no Script Conditions or Script Actions appear supported.

It’s nice to see this coming along though and I look forward to see it progress. But I can’t even test it out right now because of the limitations.

1 Like

Hi!
Thanks for the feedback! I appreciate all feedback!

I’m not even sure this is my aim. I guess it depends on how it evolves, if more people are willing / have the interest to develop the binding further. Or if it will be just a handful of people preferring to use java to write rules. I could even see this being developed into something else, maybe we should view this as a Proof of Concept.

The binding connects to the itemRegistry to fetch the items. It should be possible to define
items in the GUI. I have not tested this yet, but should work.

The binding will reload the rules if you modify, add or delete files in the location, no need for restarts.

I agree, I’ll change this in the next changes.

Yes this was my original plan. I could not find a suitable base class to extend for a automation package. The base class used by the other JSR-automations will not work with this binding.
The documentation for automation is very limited (compared with binding documentation), as well as the number of automation addons gave me no apparent options how to add this.
I’ll look into it and see if I can transform it later on, I still expect it to work in a similar manner.

True, and it’s not a priority for me to this. I will add a note about this.

BR S

I guess I’m not sure why someone would bother to develop something for OH that only a small portion of the openHAB userbase could use. But everyone has their own goals. It just becomes really hard to manage all these different subsystems for writing rules when there is so little overlap between who they all work. We already have so many niche rules subsystems it’s unmanageable and really hard for new users to choose “the right one.” But given that’s the case, there probably isn’t any reason why you should stick to the Rules DSL precedent for how to structure the rules if Java pushes you in a different direction.

All Items no matter how they are defined should be available then.

Yes, but if there are a lot of adopters it will be easier to motivate a merge and add the needed functionality that is required for an official automation/binding-addon.
I developed this mostly because it makes my life so much easier being able to write my rules in java. With the approach I’ve chosen here, I find that my rules tend to work straight away, it’s more difficult to make syntax errors and you don’t have to spell the item names correctly (since you are using them as a dependency generated from OpenHAB). I never felt at home using Jython/Python/javascripts for creating rules. Rules DSL was fine but had too many limitations and held me back. I got stuck having so many ideas on how to make cool automations in my home, by not having the right tool for writing rules, I have create more addons than might be necessary and also held back with some work trying figure out if I would go the Python (Habapp) or Jython way.

I’m choosing to share my work because I want give back to the community and also because I find it interesting to develop these type of addons. I’m hoping some more people with pick up this track, and that it can grow with the help of others. If only a handful of people want to use this addon that’s fine. It’s also fine if more people want to use this and it grows into something else and eventually will reach a point where it can be merged.
I never had the intention to reach the wide OpenHAB user base but at the same time I’m sure that there are more people like me who perfer text-based configuration, not using GUI:s for creating rules and in general wants to use Java rather than something else.

This is understandable and probably caused by OpenHAB being such a open, powerful and generic platform. I have written that this is not for beginners and I should probably highlight that more. If you are a new user to OpenHAB this is probably not for you. The target group for this addon would be someone with Programming knowledge (preferably java).
For you, since you tend to be the person everyone asks for help and feedback when it comes to writing rules and automations (as well as design patterns) I can understand your frustration.

I honestly don’t know what language for writing rules I would choose if I began my OH-journey now, but I would probably be confused.

I tried to keep it similar to rules DSL, when I rewrote most of my DSL rules I just copy pasted them into to the java source, and could quite easily convert them into java. But yes probably better to see where Java can be utilized rather than sticking to the limitations and structure of DSL.

/S

As I wrote in the other thread , I am afraid it will never get merged until it is refactored to be an automation bundle.

Probably true, not a big issue as far as I see. The binding is not using much binding stuff and should be easy to convert when I figure out the automation base class to use.

/S

From the developer guide:

A binding is an extension to openHAB that integrates an external system like a software service or a hardware device. The external system is represented as a set of Things and sometimes Bridges with Channels .

There is also a section for automation modules.

Don‘t get me wrong, I don‘t want to disencourage you, but I think it would be better to do it right from the beginning instead of having never ending discussions later on…

Like I said I agree. It should not be a big refactoring since it is not relaying on channels or binding specific things.

/s

Small suggestion with regard to installation/compilation. You might try to simplify integration by removing explicit need for slf4j-api jar. Their packages are available within pax-logging-api (look for specific JAR/version in system/org/ops4j/pax/logging). It is always there if you use Karaf/OH!

The slf4j-api is not used by the binding, it is used when developing java-rules in a standalone Java ide, to be able to log directly from the rules. I could probably remove the dependency by adding a wrapper around slf4j, but I haven’t looked that much into that yet.

/S

I have refactored it from a binding to an automation package instead.
default location is now set to: /etc/openhab/automation/jrule and there is no need to add a jrule thing.

Br S

2 Likes

Out of curiosity and for future developers who come along, what was the right class to inherit from?

I didn’t extend any class. I got locked on extending some factory class due to the fact that’s what you do in bindings as well as the other automation modules I looked at.
Instead I added a @activate and @Deactivate method.

@Component(configurationPid = "automation.jrule")
@NonNullByDefault
public class JRuleFactory {

    private final ItemRegistry itemRegistry;
    private final JRuleEventSubscriber eventSubscriber;
    private final EventPublisher eventPublisher;
    private final VoiceManager voiceManager;
    private final JRuleHandler jRuleHandler;

    @Activate
    public JRuleFactory(Map<String, Object> properties, final @Reference JRuleEventSubscriber eventSubscriber,
            final @Reference ItemRegistry itemRegistry, final @Reference EventPublisher eventPublisher,
            final @Reference VoiceManager voiceManager) {
        this.itemRegistry = itemRegistry;
        this.eventSubscriber = eventSubscriber;
        this.eventPublisher = eventPublisher;
        this.voiceManager = voiceManager;
        jRuleHandler = new JRuleHandler(properties, itemRegistry, eventPublisher, eventSubscriber, voiceManager);
        jRuleHandler.initialize();
    }

    @Deactivate
    public void dispose() {
        jRuleHandler.dispose();
    }
2 Likes

I’ve built a new release, fixing internal dependencies and jar-generation. The previous jar was 7.5mbs :smile: Now 156kb.

Built an ALPHA2 version where you can add 3rd party jar dependencies.

Created and ALPHA3 prebuilt jar. Fixed some issues for 3rd party libraries and reloading rules when they are changed on disk.

Added an ALPHA4 build:

  • Refactored completable futures
  • Added 5 seconds of delay for initialization of the rule engine to avoid multiple reloads
  • Added support for play & pause for player item
  • Added commandLineExecute

Added an ALPHA5 build.

  • Removed dependencies on slf4japi and eclipse annotations
  • Added logInfo logDebug (to wrap slf4j and remove dep)
  • Fixed compilation of rules to be more robust with internal dependencies

You can still use sl4j for logging, but then you need to add it as an external dependency.