JRule - OpenHAB Rules using Java

Also you can call

JRuleEngine.get().getItemNames();

Which will return a

Set<String>

Best regards s

my concrete example

@JRuleName("init_DI_LED16")
@JRuleWhen(item = _BT_SW_LED16_TOGGLER.ITEM, trigger = _BT_SW_LED16_TOGGLER.TRIGGER_RECEIVED_COMMAND)
public void init_DI_LED16(final JRuleEvent event) {
    _G_DI.members().stream()
        .filter(memberItemName -> memberItemName.startsWith("DI_LED16_"))
        .map(memberItemName -> itemRegistry.getItem(memberItemName)) // <--- how get an item by its name?
        .forEach(memberItem -> {
            if (memberItem.state == "LED_OFF") {                               //<--- use state of member item identified by name
                ...
                sendCommand(memberItemName, "LED_RED")
            }
        }
    }
}

where _G_DI is a group of LEDs.

If it is possible to send a command/update the state of an item identified by its name it should also be possible to read the item state by its name.

I could introduce more groups and use operations on these groups (intersections, slices, …) but this does not feel natural to me.

See Design Pattern: Associated Items

I don’t mind exposing the ItemRegistry to run for instance getAllItemName, getAllItemsByPattern etc.

For your usecase now would this work?

If you put all DI_LED16_* named items into one group _G_DI
Then this should (?) work:

@JRuleName("init_DI_LED16")
@JRuleWhen(item = _BT_SW_LED16_TOGGLER.ITEM, trigger = _BT_SW_LED16_TOGGLER.TRIGGER_RECEIVED_COMMAND)
public void init_DI_LED16(final JRuleEvent event) {
    _G_DI.members().stream()
        .forEach(memberItem -> {
            if (JRuleSwitchItem.getState(memberItem) == JRuleOnOffValue.OFF) {
                sendCommand(memberItem, "LED_RED");
            }
        }
    }
}
1 Like

This would work for >99%. However, I would prefer something like

JRuleSwitchItem mySwitchItem = JRuleSwitchItem.forName(memberItemName)
if (JRuleSwitchItem.getState(memberItem) == JRuleOnOffValue.OFF) {
    mySwitchItem.sendCommand("LED_RED") 
}

That way, the script would gain more direct access to all the member functions of the JRule*Item.

Sure I’ll add something like this.
Best regards s

Hello !

Thank you again for your work.
A discussion opened on a related subject (a JSR223 compliant java scripting langage). I don’t know if it is on the same path / same objectives, or if it is complementary or another thing, but I think it is worth mentionning here.
Maybe have you all some thoughts about it ?

Hi!

I think we can make JRule JSR223 compliant, and also add GUI support if there is demand for it.
I don’t have that much to add at the moment. There are some gaps in JRule (like actions), I hope it will continue to grow, I have merged several PR:s that past week.

Regards, S

That would be a great thing. I’d appreciate it very much if I could use such triggers in my conditions.

Today I updated to BETA5 by simply replacing the jar in my addons folder.
I can see that java files get compiled and that JRule validates the rules.
My old JRule rules using cron seem to be executed fine.

Now I added a new rule, listening for item changes. The problem I have is, that the rule wasn’t picked up after I saved it. Once I restarted OH I could see that it was picked up. But whenever I change the rule by saving, it does not get updated automagically by JRule. I have to restart OH for this. Also it seems that the rule, although the value was changed for that item, is not fired. That the value has changed I can see in the openhab logs. If I change the condition back to cron to run every minute and restart OH, it runs. What could be wrong that this results in the behavior seen? Also if I change an existing cron rule, it does not take effect until I restart OH. That worked before IIRC. The item is of type String.


public class LinkindKeypadRule extends JRule {

    @JRuleName("LinkindKeypadRule")
    @JRuleWhen(item = _LinkindKeypad_Action_Transaction.ITEM, trigger = _LinkindKeypad_Action_Transaction.TRIGGER_CHANGED)	
    public void listenForKeypadAction() {
        logInfo("Action: "+_LinkindKeypad_Action.getState()+", tx: "+_LinkindKeypad_Action_Transaction.getState()+", code: "+_LinkindKeypad_Action_Code.getState());
    }
}

The item change log:

2022-02-17 22:27:08.818 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'LinkindKeypad_Action_Transaction' changed from 0 to 2

2022-02-17 22:27:08.821 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'LinkindKeypad_Action_Code' changed from 123 to 789
1 Like

Big update and BETA6 released.

BETA 6

2 Likes

Sorry for replying so late to this. I can’t reproduce this, could you try and run the latest beta 6. You could also enable debug logging for jrule to give you more information

[15:03:56] openhab@stardust:~$ ssh -p 8101 openhab@127.0.0.1
Password authentication
Password: 

                           _   _     _     ____  
   ___   ___   ___   ___  | | | |   / \   | __ ) 
  / _ \ / _ \ / _ \ / _ \ | |_| |  / _ \  |  _ \ 
 | (_) | (_) |  __/| | | ||  _  | / ___ \ | |_) )
  \___/|  __/ \___/|_| |_||_| |_|/_/   \_\|____/ 
       |_|       3.3.0-SNAPSHOT - Build #2833

Use '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
To exit, use '<ctrl-d>' or 'logout'.

openhab> 
openhab>                                                                                                                                                                
openhab> log:set DEBUG org.openhab.automation.jrule   

Another thing you could try is to add the JRuleEvent to the method, not sure it will change anything but that is where you want value that triggered the rule (see example 3).
Make sure you clean out all folders for jrule, so you don’t have any old java or classes interfering. When updating all your items will be generated again, so try and start fresh and then add the rules (in java-files).

public class LinkindKeypadRule extends JRule {

    @JRuleName("LinkindKeypadRule")
    @JRuleWhen(item = _LinkindKeypad_Action_Transaction.ITEM, trigger = _LinkindKeypad_Action_Transaction.TRIGGER_CHANGED)	
    public void listenForKeypadAction(JRuleEvent event) {
        logInfo("Action: "+_LinkindKeypad_Action.getState()+", tx: "+event.getValue()+", code: "+_LinkindKeypad_Action_Code.getState());
    }
}

Added BETA7 build

BETA7

1 Like

Thank you very much for creating and sharing this! Just got it up and running without any issues.
Going to migrate all my messy Rules DSL scripts now.
Really happy to see this project growing.

1 Like

Have you already managed to look into that?
I’d also like to get an item from it’s name, like JRuleItem.forName("…").


Second question:
When using multiple “JRuleWhen” triggers, how to identify which item actually triggered the rule?

I tried with a group of multiple Number items but changing the value of one item won’t trigger the group.

Do you have an example where you want to use it? If you know the name of the item, you can do _MyItem.ITEM or similar.
If you want to send a command with itemname stored in a variable you can do JRuleSwitchItem.sendCommand(myitem, JRuleOnOffValue.ON)
The problem with JRuleItem.forName(…) would be: What type should be returned? The Generic JRuleItem does not have many methods. JRule relies on the generated items provided in the jar.
EDIT: Yes I could try and add it for the specific types, I promised to that, but it I forgot it.
So you could have JRuleSwitchItem.forName() and JRuleNumberItem.forName().

Take A look at example 3 in the documentation:

 @JRuleName("MyEventValueTest")
   @JRuleWhen(item = __MyTestSwitch2.ITEM, trigger = __MyTestSwitch2.TRIGGER_RECEIVED_COMMAND)
   public void myEventValueTest(JRuleEvent event) {
      logInfo("Got value from event: {}", event.getValue());
   }

An example is given here. The memberItemName is built at runtime.
There are various cases where an item name is not known at implementation-time but needs to be built at runtime. The Design Pattern: Associated Items explains the problem and solutions in python/rules DSL.

1 Like

I already use that method but I also need to know which item actually changed.

Example:
Item_1_Time
Item_2_Time

Now when one of those changes, I want to update:
Item_1_Time_Scaled
or
Item_2_Time_Scaled

I can’t do that without knowing which item changed. :smiley:

Look at example 15, or am I misunderstanding you?

When I use the group as a trigger, the rule won’t get triggered. That’s why I use one “JRuleWhen” for each item.
The group only contains NumberItems, I don’t know if that’s the issue.

I use the same approach (repeat the JRuleWhen for every item) for my window sensors since the group does not trigger the rule.
My guess is that it’s the same as described here.