diff --git a/README.md b/README.md index 605488b2..74bf54ad 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,111 @@ # openHAB Rules using Java -The JRule Automation Addon aims to enable Java development of openHAB Rules. The automation addon will allow the user to create custom openHAB rules -in one or several .java- or jar-files. The Java Rules will need defined triggers in order for the 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. Rules tend to be written to trigger on changes to either items or things. The addon is compatible with items and things added either in the openHAB GUI or defined in plain .items and .thing files. -JRule will generate java-source files for items and things as well as compiling and package them into a jrule-generated.jar file. The jrule-generated.jar file should be used when the user is developing openHAB rules. - -The syntax for rules as well as the design and thinking behind the automation addon is to provide something that is similar to Rules DSL but more powerful, customizable and flexible. JRule relies on strict typing where you are less likely to construct rules that are not working due to syntax error. - - -# Limitations -- Not supporting OH3 GUI rules, script conditions - -# Why - - You will be able to use a standard Java IDE to develop your rules. - - Full auto-completion (Shift space) for all items, less chance of errors and typos - - Take full advantage of all java design patters - - Share and reuse code for you rules - - Advanced timers and locks are built in and can be used without cluttering the code - - Possibility to write junit-tests to test your rules - - Use any 3rd party dependencies and libraries in your rules - - You will be able to use JRule in parallel with any other Rules engine if you want to give it a try - - Compile and build your rules with tools such as maven, or provide rules in plain-java files - - Utilize thing actions and trigger on thing statuses - - Reuse you methods and code for many different purposes, reducing the amount of code you have to write. - - Advanced logging can be used with for instance logstash using MDC-tags - -# Who - -This addon is not for beginners, you should have knowledge in writing java-programs or a desire to do so. -As you can see in the examples below, rules will become short and readable making it easy to understand once you learn how to write the rules. - -# Maturity +The JRule Automation Addon enables openHAB rule development using plain old Java. It can considered a replacement or an +addition to other rule languages like openHAB DSL, Javascript and others. + +Rules may be provided as Java source files directly (compiled on server) or as precompiled jar files (compiled on +developer machine - like any other regular Java project - **recommended**). + +### Latest release + +* [![GitHub Release](https://img.shields.io/github/release/seaside1/jrule.svg?style=flat)](https://github.com/seaside1/jrule/releases/latest) +* [Full changelog](doc/CHANGELOG.md) + +## Table of contents + +- [openHAB Rules using Java](#openhab-rules-using-java) + + [Latest release](#latest-release) + + * [Why](#why) + * [Target audience](#target-audience) + * [Maturity](#maturity) + * [Limitations](#limitations) +- [Getting started](#getting-started) + * [Example rule file](#example-rule-file) +- [How it works](#how-it-works) + + [Third Party External Dependencies](#third-party-external-dependencies) + + [Configuration file](#configuration-file) +- [Rule constructs](#rule-constructs) + * [Rule annotations](#rule-annotations) + + [Triggers](#triggers) + + [Pre-conditions](#pre-conditions) + + [Logging](#logging) + + [Other annotations](#other-annotations) + * [Generated code](#generated-code) + + [JRuleItems and JRuleItemNames](#jruleitems-and-jruleitemnames) + + [JRuleThings](#jrulethings) + + [JRuleThingActions](#jrulethingactions) + * [Other built-in actions](#other-built-in-actions) +- [Examples](#examples) + +## Why + +- You will be able to use a standard Java IDE to develop your rules. +- Full auto-completion (Shift space) in your IDE for all items, things and actions yields less chance of errors and + typos +- Take full advantage of all Java design patters +- Share and reuse code for you rules +- Advanced timers and locks are built in and can be used without cluttering the code +- Possibility to write junit-tests to test your rules +- Use any 3rd party dependencies and libraries in your rules +- You will be able to use JRule in parallel with any other Rules engine if you want to give it a try +- Compile and build your rules with tools such as maven, or provide rules in plain Java files +- Utilize thing actions and trigger on thing statuses +- Reuse you methods and code for many different purposes, reducing the amount of code you have to write. +- Advanced logging can be used with for instance logstash using MDC-tags + +## Target audience + +This addon is not for beginners, you should have knowledge in writing Java programs or a desire to do so. +As you can see in the examples below, rules will become short and readable making it easy to understand once you learn +how to write the rules. + +## Maturity Beta, still major changes. -# Download - -Prebuilt jar file is available under https://github.com/seaside1/jrule/releases +## Limitations -# Java Rule Engine +- Not supporting OH3 GUI rules -Input plain java-rules files under: -/etc/automation/jrule/rules/org/openhab/automation/jrule/rules/user/ +# Getting started -It is also possible to add rules as pre-compiled jar files under: -/etc/automation/jrule/rules-jar/ +1. Install the addon by either -Output jar files to be added by the user as dependencies when doing rule development will be located under: -/etc/openhab/automation/jrule/jar +- copying the `org.openhab.automation.jrule-3.x.x-BETAX.jar` + from https://github.com/seaside1/jrule/releases to openhab-addons folder +- or by installing form openHAB Addon Marketplace. -Add external dependencies as jar-files under: -/etc/openhab/automation/jrule/ext-lib +2. Create rules by either +- clone https://github.com/seaside1/jrule-user and use as a template rule project. +- or provide Java source files directly in the `/etc/automation/jrule/rules/org/openhab/automation/jrule/rules/user/` on + your openHAB installation. -The following jar files can be found under the jrule/jar-folder: +## Example rule file -| Jar File | Description | -| -------------------------------------- | --------------------------------------------------------------------------------------------- | -| jrule-generated.jar | Contains all generated items, which will be used when developing rules | -| jrule.jar | JRule Addon classes neeed as dependency when doing development | +> Note: All rule classes must extend `org.openhab.automation.jrule.rules.JRule` +`default.items`: -# Get started with the JRule Automation Addon - -- Install the addon by copying the org.openhab.automation.jrule-3.x.x-BETAX.jar to openhab-addons folder - Download the latest release from https://github.com/seaside1/jrule/releases -- The default location is /etc/openhab/automation/jrule but can be configured -- When the addon is started it will: -1. Create JAVA source files for all items -2. Compile java source files and create a resulting jrule.jar file under /etc/openhab/automation/jrule/jar -3. Compile any java rules file under /etc/openhab/automation/jrule/rules/org/openhab/automation/jrule/rules/user/ - It is possible to use package structure with subdirectories in this folder, or the can be place in a flat structure right under this folder -4. Create jar files with dependencies to be used when creating your java-rules (jrule-generated.jar). -The two jar files needed for Java rules development can be found under /etc/openhab/automation/jrule/jar - -Once the JAVA rule engine has started and compiled items successfully you can either copy the jar files -form /etc/openhab/automation/jrule/jar/* to the place where you intend to develop the Java- Rules, or share that folder -using samba / CIFS / NFS or similar. -- Set up your favourite IDE as a standard java IDE. -- Create a new empty java project -- Create a package / folder org.openhab.automation.jrule.rules.user -- Place your Java rules file in this folder - -NOTE: The rules will be reloaded if they are modified. Any java file you place under /etc/openhab/automation/jrule/rules/org/openhab/automation/jrule/rules/user/ -will be compiled or recompiled, you don't have to restart OpenHAB. - -Designing your Java Rules File (Hello World) -1. Start by adding an item in OpenHAB. -Group JRule -Switch MyTestSwitch "Test Switch" (JRule) -Switch MyTestSwitch2 "Test Switch 2" (JRule) +``` +Switch MyTestSwitch "Test Switch" +``` -2. Create the following class +`MySwitchRule.java`: ```java package org.openhab.automation.jrule.rules.user; - -import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleName; + import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.OFF; import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.ON; -import org.openhab.automation.jrule.rules.JRuleName; -public class MySwitchRule extends JRule { +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +public class MySwitchRule extends JRule { @JRuleName("MySwitchRule") @JRuleWhenItemChange(item = MyTestSwitch, from = OFF, to = ON) public void execOffToOnRule() { @@ -112,856 +114,177 @@ public class MySwitchRule extends JRule { } ``` -Make sure you add the Jar-files from /etc/openhab/jrule/jar as dependencies. - -# Build and Deploy Rules using Maven -See https://github.com/seaside1/jrule-user for an example template project. - -# Third Party External Dependencies - -You can add any 3rd party library as dependency. Copy the jar files needed to /etc/openhab/automation/jrule/ext-lib -The Automation Engine will automatically pick these dependencies up when it is compiling the rules. - -# Core Actions - -Built in Core Actions that can be used -| Action | Description | -| -------------------------------------- | --------------------------------------------------------------------------------------------- | -| say | Will use VoiceManager to say action see Example 13 | -| commandLineExecute | See Example 14 | - - -# Thing actions -Thing actions are supported. JRule will generate a file that contains all available thing actions. -See example #34 - -# Logging from rules -Logging from rule can be done in 3 different ways -1. Not specifying anything will result in the usage of JRuleName as prefix when calling JRule.logInfo/Debug/Error etc see example 20 -2. Overriding method JRule.getRuleLogName will result in the same log prefix for all rules defined in that file in see example 21 -3. Specifically add rependency on log4j and define your own logger to do logging - -# Configuration -JRule has some optional configuration. Place config file under: /etc/openhab/automation/jrule/jrule.conf -Example of config file. -``` -## Run rules in a separate threadpool -org.openhab.automation.jrule.engine.executors.enable=false - -## Minimum number of threads -org.openhab.automation.jrule.engine.executors.min=2 - -## Maximum number of threads -org.openhab.automation.jrule.engine.executors.max=10 - -``` - -# Operators for triggers and preconditions -See example 12, 25 and 26 -| Operator | Description | -| -------------------------------------- | --------------------------------------------------------------------------------------------- | -| eq | Equals | -| neq | Not Equals | -| gt | Greater than | -| gte | Greater than equals | -| lt | Less than | -| lte | Less than equals | - - -# Examples - -## Example 1 - -Use Case: Invoke another item Switch from rule -```java - @JRuleName("MyRuleTurnSwich2On") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void execChangedToRule() { - logInfo("||||| --> Executing rule MyRule: changed to on"); - JRuleItems.MySwitch2.sendCommand(ON); - } -``` - -## Example 2 - -Use case: Invoke a Doorbell, but only allow the rule to be invoked once every 20 seconds. -This is done by acquiring a lock getTimedLock("MyLockTestRule1", 20). - -```java - @JRuleName("MyLockTestRule1") - @JRuleWhenItemChange(item = MyTestSwitch2, from = OFF, to = ON) - public void execLockTestRule() { - if (getTimedLock("MyLockTestRule1", 20)) { - JRuleItems.MyDoorBellItem.sendCommand(ON); - logInfo("||||| --> Got Lock! Ding-dong !"); - } else { - logInfo("||||| --> Ignoring call to rule it is locked!"); - } - } -``` -## Example 3 - -Use case: Use the value that caused the trigger -When the rule is triggered, the triggered value is stored in the event. - -```java - @JRuleName("MyEventValueTest") - @JRuleWhenItemReceivedCommand(item = MyTestSwitch2) - public void myEventValueTest(JRuleEvent event) { - logInfo("Got value from event: {}", event.getState().getValue()); - } -``` -## Example 4 - -Use case: Or statement for rule trigger -To add an OR statement we simply add multiple @JRuleWhen statements - -```java - @JRuleName("MyNumberRule1") - @JRuleWhenItemChange(item = MyTestNumber, from = "14", to = "10") - @JRuleWhenItemChange(item = MyTestNumber, from = "10", to = "12") - public void myOrRuleNumber(JRuleEvent event) { - logInfo("Got change number: {}", event.getState().asStringValue()); - // or - logInfo("Got change number: {}", event.getItem().getState().asStringValue()); - } -``` - -## Example 5 - -Use case: Define your own functionality -Create a Rules class that extends: JRuleUser.java -JRuleUser.java should be placed in the same folder as your rules -The JRuleUser class can contain common functions and functionality you want to reuse in your rules: - -```java -package org.openhab.automation.jrule.rules.user; - -import org.openhab.automation.jrule.rules.JRule; - -public class JRuleUser extends JRule { - -} -``` - -## Example 6 - -Your class rules can now extend the JRuleUser -package org.openhab.automation.jrule.rules.user; - -```java -import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; -import org.openhab.automation.jrule.rules.user.JRuleUser; - -public class MySwitchRule extends JRuleUser { - -} -``` - -## Example 7 - -Let's say we want to add a common function that should be available for all user rules. -We want to add a function that checks if it is ok to send notifications depends on what time it is. -We'll do this: - -```java -package org.openhab.automation.jrule.rules.user; - -import org.openhab.automation.jrule.rules.JRule; - -public class JRuleUser extends JRule { - - private static final int startDay = 8; - private static final int endDay = 21; - - protected boolean timeIsOkforDisturbance() { - return nowHour() >= startDay && nowHour() <= endDay; - } -} -``` - -We then extend the rule from the Java Rules file: +See also [extensive list of examples](doc/EXAMPLES.md). -```java -package org.openhab.automation.jrule.rules.user; +# How it works -import org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; -import org.openhab.automation.jrule.rules.event.JRuleEvent; -import org.openhab.automation.jrule.rules.JRuleName; +_Assuming_ you are running a plain Linux setup like openHABian, the `$JRULE_ROOT` would +equal `/etc/openhab/automation/jrule` in the following table: -public class MyTestUserRule extends JRuleUser { +| Location | Description | +|---------------------------------------|-----------------------------------------------------------------------------------------| +| `$JRULE_ROOT/gen/` | Generated source files containing all items, things and actions | +| `$JRULE_ROOT/jar/jrule-generated.jar` | Jar file of the generated files (`$JRULE_ROOT/gen/`) - used for rule development | +| `$JRULE_ROOT/jar/jrule.jar` | JRule Addon classes (reference to openHAB core classes etc) - used for rule development | +| `$JRULE_ROOT/jrule.conf` | Configuration file (optional), see below | +| `$JRULE_ROOT/ext-lib/` | Optional 3rd party dependencies (may also be shaded in a compiled rule jar) | +| `$JRULE_ROOT/rules/` | Java rule source files that will be compiled by openhab (optional) | +| `$JRULE_ROOT/rules-jar/` | Any jar files placed here will be loaded as precompiled rule classes | - @JRuleName("TestUserDefinedRule") - @JRuleWhenItemChange(item = MyTestSwitch, from = OFF, to = ON) - public void mySendNotificationRUle(JRuleEvent event) { - if (timeIsOkforDisturbance()) { - logInfo("It's ok to send a disturbing notification"); - } - } -} -``` -## Example 8 +* When the addon starts _or_ there is a change in items/things on your openHAB + instance, `$JRULE_ROOT/jar/jrule-generated.jar` and `$JRULE_ROOT/jar/jrule.jar` are updated, and all rules recompiled + and reloaded. +* When a Java source file with rules in `$JRULE_ROOT/rules/` is updated, the rule is compiled and reloaded. +* When a jar file in `$JRULE_ROOT/rules-jar/` is updated, all rules are reloaded. -Use case create a timer for automatically turning off a light when it is turned on. If it's running cancel it and schedule a new one. -```java - @JRuleName("myTimerRule") - @JRuleWhenItemChange(item = MyLightSwitch, to = ON) - public synchronized void myTimerRule(JRuleEvent event) { - logInfo("Turning on light it will be turned off in 2 mins"); - createOrReplaceTimer(MyLightSwitch, Duration.ofMinutes(2), new Runnable() { - @Override - public void run() { - logInfo("Time is up! Turning off lights"); - JRuleItems.MyLightSwitch.sendCommand(OFF); - } - }); - } -``` -## Example 9 +### Example scripts to easily handle the copy process -Use case: Let's say we have a 433 MHz wall socket with no ON/OFF feedback and a bit of bad radio reception. We can then create a repeating timer -to send multiple ON statements to be sure it actually turns on. - createOrReplaceRepeatingTimer("myRepeatingTimer", 7, 4, will create a repeating timer that will trigger after 0 seconds, 7s, 14s and 21s - If the Timer is already running it will cancel it and create a new one. - -```java - @JRuleName("repeatRuleExample") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public synchronized void repeatRuleExample(JRuleEvent event) { - createOrReplaceRepeatingTimer("myRepeatingTimer", 7, Duration.ofSeconds(10), new Runnable() { - @Override - public void run() { - String messageOn = "repeatRuleExample Repeating....."; - logInfo(messageOn); - JRuleItems.MyBad433Switch.sendCommand(ON); - }); - } +#### Grab the jrule engine from the server +```shell +scp @:$JRULE_ROOT/jar/jrule.jar lib/jrule.jar ``` -## Example 10 - -Use case Create a simple timer. When MyTestSwitch turns on it will wait 10 seconds and then turn MyTestSwitch2 to on. Note that -it will not reschedule the timer, if the timer is already running it won't reschedule it. -```java - @JRuleName("timerRuleExample") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public synchronized void timerRuleExample(JRuleEvent event) { - createTimer("myTimer", Duration.ofSeconds(10), new Runnable() { - @Override - public void run() { - String messageOn = "timer example."; - logInfo(messageOn); - JRuleItems.MyTestWitch2.sendCommand(ON); - }); - } +#### Grab the generated jrule source +```shell +scp @:$JRULE_ROOT/jar/jrule-generated.jar lib/jrule-generated.jar ``` -## Example 11 -Use case trigger a rule at 22:30 in the evening to set initial brightness for a ZwaveDimmer to 30% -```java - @JRuleName("setDayBrightness") - @JRuleWhenTimeTrigger(hours=22, minutes=30) - public synchronized void setDayBrightness(JRuleEvent event) { - logInfo("Setting night brightness to 30%"); - int dimLevel = 30; - JRuleItems.MyDimmerBrightness.sendCommand(dimLevel); - } +#### Grab the generated jrule source +```shell +scp target/openhab-rules-1.0-SNAPSHOT.jar @:$JRULE_ROOT/rules-jar/openhab-rules-1.0-SNAPSHOT.jar ``` -## Example 12 +### Third Party External Dependencies -Use case: If temperature is below or equals to 20 degrees send command on to a heating fan -It is possible to use: -lte = less than or equals -lt = less than -gt = greater than -gte = greater than or equals -eq = equals -```java - @JRuleName("turnOnFanIfTemperatureIsLow") - @JRuleWhenItemChange(item = MyTemperatureSensor, condition = @JRuleCondition(lte = 20)) - public synchronized void turnOnFanIfTemperatureIsLow(JRuleEvent event) { - logInfo("Starting fan since temperature dropped below 20"); - JRuleItems.MyHeatingFanSwitch.sendCommand(JRuleOnOffValue.ON); - } -``` +You can add any 3rd party library as dependency. Copy the jar files needed to `$JRULE_ROOT/ext-lib/` +The Automation Engine will automatically pick these dependencies up when it is compiling and reloading the rules. -## Example 13 +### Configuration file -Use case: Using say command for tts -```java - @JRuleName("testSystemTts") - @JRuleWhenItemChange(item = TestSystemTts, to = ON) - public synchronized void testSystemTts(JRuleEvent event) { - logInfo("System TTS Test"); - String message = "Testing tts! I hope you can hear it!"; - say(message, null, "sonos:PLAY5:RINCON_XXYY5857B06E0ZZOO"); - } -``` +JRule has some optional configuration properties. Add file `$JRULE_ROOT/jrule.conf` -## Example 14 +Example: -Use case: Executing command from CLI -```java - @JRuleName("TestExecutingCommandLine") - @JRuleWhenItemReceivedCommand(item = MySwitchGroup) - public synchronized void testExecutingCommandLine(JRuleEvent event) { - logInfo("Creating dummy file using CLI"); - executeCommandLine("touch", "/openhab/userdata/example.txt"); - } -``` -## Example 15 - -Use case: A group of switches, see if status is changed, and also which member in the group changed state -```java - @JRuleName("groupMySwitchesChanged") - @JRuleWhenItemChange(item = MySwitchGroup) - public synchronized void groupMySwitchGroupChanged(JRuleEvent event) { - final boolean groupIsOnline = ((JRuleItemEvent) event).getState().getValueAsOnOffValue() == JRuleOnOffValue.ON; - final String memberThatChangedStatus = ((JRuleItemEvent) event).getMemberName(); - logInfo("Member that changed the status of the Group of switches: {}", memberThatChangedStatus); - } -``` - -## Example 16 - -Use case: A group of switches , trigger when it's changed from OFF to ON -```java - @JRuleName("groupMySwitchesChangedOffToOn") - @JRuleWhenItemChange(item = MySwitchGroup, 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()); - } -``` - -## Example 17 - -Use case: Listen for a Channel Trigger Event -```java - @JRuleName("ChannelTriggered") - @JRuleWhenChannelTrigger(channel = binding_thing.buttonevent) - public synchronized void channelTriggered(JRuleEvent event) { - logInfo("Channel triggered with value: {}", ((JRuleChannelEvent) event).getEvent()); - } -``` - -## Example 18 - -Use case: Cron based expression to trigger rule -```java - @JRuleName("testCron") - @JRuleWhenCronTrigger(cron = "*/5 * * * * *") - public void testCron(JRuleEvent event) { - logInfo("CRON: Running cron from string every 5 seconds: {}", event); - } -``` - -## Example 19 - -Use case: getLastUpdated for an item -Note that `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM).getLastUpdated("mapdb");` -can be called without serviceId argument: `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM).getLastUpdated();` -```java -@JRuleName("testLastUpdate") -@JRuleWhenCronTrigger(cron = "4 * * * * *") -public void testLastUpdate(JRuleEvent event){ - logInfo("CRON: Running cron from string: {}",event.getState().getValue()); - ZonedDateTime lastUpdate = JRuleItems.MyCoolItem.getLastUpdated("mapdb"); - DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd - HH:mm:ss Z"); - String lastUpdateFormatted=lastUpdate.format(formatter); - logInfo("Last Update: {}",lastUpdateFormatted); -} -``` - -## Example 20 - -Use case: Get the brigtness from a color item, set a color item to white (HSB 0, 0, 100) -```java - - @JRuleName("testBrightnessFromColorItem") - @JRuleWhenItemChange(item = MyTestColorItem) - public void testBrightnessFromColorItem(JRuleEvent event) { - JRuleColorValue color = JRuleItems.MyTestColorItem.getState(); - int brightness = color.getHsbValue().getBrightness(); - } - - @JRuleWhenItemChange(item = MyTestColorItem) - public void testSetWhiteOnColorItem(JRuleEvent event) { - JRuleItems.MyTestColorItem.sendCommand(JRuleColorValue.fromHsb(0,0,100)); - } -``` - -## Example 21 -Use case: Set logging name for a specific rule -```java - @JRuleName("MyCustomLoggingRule") - @JRuleLogName("MYLOG") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void execChangedToRule() { - logInfo("||||| --> Executing rule MyRule: changed to on"); - JRuleItems.MySwitch2.sendCommand(ON); - } -``` - -## Example 22 -Use case: Override logging for all rules defined in one file -```java - public class ColorRules extends JRule { - - @JRuleName("MyCustomLoggingRuleOnClass") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void execChangedToRule() { - logInfo("||||| --> Executing rule MyRule: changed to on"); - JRuleItems.MySwitch2.sendCommand(ON); - } - - @Override - protected String getRuleLogName() { - return "CustomLogExample"; - } -} -``` - -## Example 23 - -Use case: Apply transformation using openHAB transformation service - -```java - public class TransformationRule extends JRule { - - @JRuleName("MyTransformation") - @JRuleWhenItemReceivedCommand(item = MyStringValue) - public void applyTransformation(JRuleEvent event) { - String transformedValue = transform("MAP(my.map):%s", event.getState().getValue()); - logInfo("Transformed {} to {}", event.getState().getValue(), transformedValue); - JRuleItems.MyTransformationReceiver.sendCommand(transformedValue); - } -} -``` - -## Example 24 - -Use case: Use precondition annotation in order to create "AND" logic. Example we have a switch that will tell -if it is ok for disturbance. If it is ok the switch is set to ON and we can send a notification if the notification -message is updated. - -```java - @JRulePrecondition(item = MyTestDisturbanceSwitch, condition = @JRuleCondition(eq = "ON")) - @JRuleName("MyTestPreConditionRule1") - @JRuleWhenItemReceivedCommand(item = MyMessageNotification) - public void testPrecondition(JRuleEvent event) { - String notificationMessage = ((JRuleItemEvent) event).getState().getValue(); - logInfo("It is ok to send notification: {}", notificationMessage); -// JRuleItems.MySendNoticationItemMqtt.sendCommand(notificationMessage); - } -``` -## Example 25 -Use case: Use precondition annotation in order to create "AND" logic. Example when the temperature is above 30 degrees (celcius probably) and -a motion detector is triggered we will turn on a fan. - -```java - public class PreConditionTestTemperature extends JRule { - - @JRulePrecondition(item=MyTestTemperatureSensor, gt = 30) - @JRuleName("MyTestPreConditionRuleTemperature") - @JRuleWhenItemChange(item = MyMotionDetector, from = OFF, to = ON) - public void testPrecondition(JRuleEvent event) { - logInfo("Temperature is above 30 and we should start the fan since the motiondetector is triggered"); - JRuleItems.MyFan.sendCommand(ON); - } -} -``` - -# Example 26 - -Use case: Send Quantity type Watt (W) from rule. - -```java - public class QuantityTypeRule extends JRule { - - @JRuleName("testQuantityPowerWatt") - @JRuleWhenItemChange(item = MyTestMeterPower) - public void testQuantityPower(JRuleEvent event) { - logInfo("TestQuantity power will send this value as Watt: {}", event.getState().getValue()); - JRuleItems.TestPowerQuantityType.sendCommand(event.getState().getValueAsDouble(), "W"); - } -} -``` -# Example 27 - -Use case: Use forName to create and item and send commands and get status - -```java - public class ForNameExampleRule extends JRule { - - @JRuleName("testForName") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void testForName(JRuleEvent event) { - JRuleSwitchItem switchItem = JRuleSwitchItem.forName("MyOtherTestSwitch"); - switchItem.sendItemCommand(OFF); - if (switchItem.getItemStatus == ON) { - switchItem.sendItemCommand(OFF); - } - } - } -``` -# Example 27b - -Use case: Use forNameOptional to create and item and send commands and get status - -```java - public class ForNameExampleRule extends JRule { - - @JRuleName("testForNameOptional") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void testForName(JRuleEvent event) { - JRuleSwitchItem.forNameOptional("MyOtherTestSwitch").ifPresent(item -> item.sendCommand(true)); - } - } +```properties +## Run rules in a separate threadpool +org.openhab.automation.jrule.engine.executors.enable=false +## Minimum number of threads +org.openhab.automation.jrule.engine.executors.min=2 +## Maximum number of threads +org.openhab.automation.jrule.engine.executors.max=10 ``` -# Example 28 +# Rule constructs -Use case: Get the name of the item that triggered the rule as well as new and old state value. -This can be useful if you have multiple JRuleWhen with different items, and you want to know which item -triggered the rule. +Multiple annotations are available to configure triggers etc. See [extensive list of examples](doc/EXAMPLES.md). -```java - public class TriggerNameExample extends JRule { - - @JRuleName("triggerNameExample") - @JRuleWhenItemChange(item = MyTestSwitch1, to = ON) - @JRuleWhenItemChange(item = MyTestSwitch2, to = ON) - public void triggerNameExample(JRuleEvent event) { - logInfo("The rule was triggered by the following item: {}", event.getItem().getName()); - logInfo("The rule was Old Value was: {} and new value: {}", event.getOldState().getValue(), event.getState().getValue()); - - } - } -``` +All rules must inherit from `org.openhab.automation.jrule.rules.JRule` which provides easy access to a set of useful +methods. -## Example 29 +## Rule annotations -Use case: get average value for a Number item last hour -```java -@JRuleName("testAverageLastHour") -@JRuleWhenCronTrigger(cron = "4 * * * * *") -public void testAverage(JRuleEvent event){ - Double average = JRuleNumberItem.forName(_MyNumberItem.ITEM).averageSince(ZonedDateTime.now().minus(1,ChronoUnit.HOURS)); - logInfo("Average value last hour: {}",average); -} -``` +All rule methods _must_ be annotated with `@JRuleName`, or the method will _not_ be considered. +### Triggers -## Example 30 +Triggers are necessary to cause rule method invocations. The following triggers are available: -Use case: Use generated JRuleItems.java to get hold of items. For instance get state of an item. -```java - public class ItemsExampleRule extends JRule { - - @JRuleName("testItems") - @JRuleWhenItemChange(item = MyTestSwitch, to = ON) - public void testItems(JRuleEvent event) { - JRuleItems.MyOtherTestSwitch.getState(); - } - } +| Annotation | Description | +|---------------------------------|--------------------------------------------------------------------------------------| +| `@JRuleWhenItemChange` | Trigger rule if item state changes | +| `@JRuleWhenItemReceivedCommand` | Trigger rule if item receives a command | +| `@JRuleWhenItemReceivedUpdate` | Trigger rule if item receives an update | +| `@JRuleWhenThingTrigger` | Trigger rule if thing status changes | +| `@JRuleWhenChannelTrigger` | Trigger rule if thing channel triggers | +| `@JRuleWhenCronTrigger` | Trigger rule if cron expression matches current time | +| `@JRuleWhenTimeTrigger` | Trigger rule if hour/minute/second matches current time (simplified cron expression) | -``` +### Pre-conditions -## Example 31 +Preconditions are conditions that must be met in order to execute a rule method. -Use case: Restart thing every night due to binding flakyness +Multiple `@JRulePreCondition` annotations may be added to each method. All conditions must be satisified for rule method +to be executed. -```java -@JRuleName("Restart thing every night") -@JRuleWhenTimeTrigger(hours=3) -public void restartThing() { - JRuleThings.my_flaky_thing.restart(); -} -``` +`@JRulePrecondition( item = JRuleItemNames.MyItemName, condition = @JRuleCondition( OPERATOR = VALUE ) )` -## Example 32 +Supported `OPERATOR` values: -Use case: Detect if a specific thing goes offline, wait for it to come online again within a given time +| `OPERATOR` | Description | +|------------|---------------------| +| `eq` | Equals | +| `neq` | Not Equals | +| `gt` | Greater than | +| `gte` | Greater than equals | +| `lt` | Less than | +| `lte` | Less than equals | -```java -@JRuleName("Notify if thing stays offline") -@JRuleWhenThingTrigger(thing = remoteopenhab_thing.ID, from = JRuleThingStatus.ONLINE) -public void warnIfThingStaysOffline() { - createOrReplaceTimer("MY_TIMER", Duration.ofMinutes(3), () -> { - if (JRuleThings.remoteopenhab_thing.getStatus() != JRuleThingStatus.ONLINE) { - logWarn("Thing {} is still offline, restarting",remoteopenhab_thing.ID); - JRuleThings.remoteopenhab_thing.restart(); - } - }); -} -``` +whereby gt, gte, lt, lte just working with numeric Items -## Example 33 +### Logging -Use case: Listen for thing status events on _all_ things +* A rule may log any activity to a separate logger if `@JRuleLogName` is added. +* Provide additional MDC tags by providing a `@JRuleTag`. -```java -@JRuleName("Log every thing that goes offline") -@JRuleWhenThingTrigger(from = JRuleThingStatus.ONLINE) -public void startTrackingNonOnlineThing(JRuleEvent event) { - String offlineThingUID = event.getThing(); - // ... -} -``` - -## Example 34 +Logging from rule can be done in 3 different ways -Use case: Thing actions, send message with pushover and other services. -Note that you will have to set up a pusheover account as thing in openHAB. +1. Not specifying anything will result in the usage of JRuleName as prefix when calling JRule.logInfo/Debug/Error etc. + See Example 20 +2. Overriding method JRule.getRuleLogName will result in the same log prefix for all rules defined in that file. See + Example 21 +3. Specifically add dependency on log4j and define your own logger to do logging -```java -@JRuleName("PushOverTest") -@JRuleWhenItemChange(item = MyTestSendPushOverButton, to = ON) -public void testPower(JRuleEvent event) { - logInfo("Sending Test message using pushover via actions"); - JRuleActions.pushoverPushoverAccountXYZ.sendMessage("MyMessage", "MyTitle"); -} -``` +### Other annotations -## Example 35 +| Annotation | Description | +|------------------|---------------------------------------------------------| +| `@JRuleDelayed` | Delay rule method invocation by a given amount of time | +| `@JRuleDebounce` | Limit rule execution to once every given amount of time | -Use case: Want to listen on all Item events of a group (without the groupstate must change). - Alternatively you could just listen to just Group changes or (real) Item changes +## Generated code -```java - @JRuleName("MemberOfUpdateTrigger") - @JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.All) - //@JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.Items) - //@JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.Groups) - public synchronized void memberOfUpdateTrigger(JRuleItemEvent event) { - final String memberThatChangedStatus = event.getMemberName(); - logInfo("Member that changed the status of the Group of switches: {}", memberThatChangedStatus); - } -``` +### JRuleItems and JRuleItemNames -## Example 36 +* `org.openhab.automation.jrule.generated.items.JRuleItems` contains a field for each `Item`. You can send + commands/updates and access state/label/metadata +* `org.openhab.automation.jrule.generated.items.JRuleItemNames` contains an `Item` name field for each item for item + name safety in annotations etc. -Use case: Want to listen just on changes where the state is now greater/equals then 12 and was before less then 12. - Without the previous condition the rule will be triggered every time the state is greater/equals then 12. +### JRuleThings -```java - @JRuleName("Change from something less to something greater") - @JRuleWhenItemChange(item = ITEM_FROM_TO, previousCondition = @JRuleCondition(lt = 12), condition = @JRuleCondition(gte = 12)) - public void itemChangeFromTo(JRuleEvent event) { - logInfo("state change to something >= 12 and was before < 12"); - } -``` +* `org.openhab.automation.jrule.generated.things.JRuleThings` contains a field for each `Thing`. On a `Thing` may get + status, any bridge (if present) or child things (if it is a bridge) -## Example 37 +### JRuleThingActions -Use case: Chain timers. Execute one and after this is expired, execute the next one. +* `org.openhab.automation.jrule.generated.things` package contains a class for each `Thing` that support thing-specific + actions -```java -@JRuleName("Notify if thing stays offline") -@JRuleWhenItemChange(item = MySwitchGroup) -public void chainSomeTimers() { - createTimer(Duration.ofSeconds(3), () -> { - logInfo("First timer finished after 3 seconds"); - }).createTimerAfter(Duration.ofSeconds(10), () -> { - logInfo("Second timer finished after 10 more seconds"); - }); -} -``` - -## Example 38 - -Use case: Do not execute a rule too often - -```java -@JRuleDebounce(10) -@JRuleName("Notify if thing stays offline") -@JRuleWhenItemChange(item = MySwitchGroup) -public void debounceMethod() { - // super critical stuff which shouldn't be called too often -} -``` - -## Example 39 +See example #34 -Use case: Send some requests to http endpoints +## Other built-in actions -```java -@JRuleName("send http methods") -@JRuleWhenItemReceivedCommand(item = MyHttpTrigger, command = "send http calls") -public void sendHttpCalls() { - String responseGet = sendHttpGetRequest("http://http-mock:8080" + HTTP_GET_SOMETHING, null); - logInfo("send Http: {}", responseGet); - sendHttpDeleteRequest("http://http-mock:8080" + HTTP_DELETE_SOMETHING, Duration.ofSeconds(5)); -} -``` +These are method inherited from the JRule superclass. -## Example 40 +| Action | Description | +|--------------------------------------|-------------------------------------------------------------------------| +| `say` | Will use VoiceManager to synthesize voice. See Example 13 | +| `executeCommandLine` | Execute a command line program (fire and forget) See Example 14 | +| `executeCommandLineAndAwaitResponse` | Execute a command line program (wait for response) See Example 14 | +| `transform` | Perform a value transformation using openHAB tranformation services | +| `sendHttpXXXRequest` | Call HTTP endpoint using GET/POST/PUT/DELETE method | +| `createTimer` | Schedule a future callback | +| `createOrReplaceTimer` | Schedule a future callback, cancel existing callback if exists | +| `createRepeatingTimer` | Schedule repeating future callbacks | +| `createOrReplaceRepeatingTimer` | Schedule repeating future callbacks, cancel existing callback if exists | +| `cancelTimer` | Cancel an existing future callback | +| `isTimerRunning` | Check if existing future callback exist | +| `getTimedLock` | Create a lock to be held for a certain amount of time | +| `sendCommand` | Send a command to an item | +| `postUpdate` | Post a state update to an item | +| `logXXX` | Log a Debug/Info/Warn/Error message | -Use case: Execute a rule delayed +# Examples -```java -@JRuleDelayed(10) -@JRuleName("Execute after ten seconds") -@JRuleWhenItemChange(item = MySwitchGroup) -public void delayedMethod() { - // delay the execution of this -} -``` +[Examples can be found here](doc/EXAMPLES.md) -# Changelog -## BETA15 -- BREAKING: All JRuleWhen has to be change to corresponding JRuleWhenItemChanged (as an example, look at JRule Examples documentation) -- JRule When refactoring by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/61 -- Thing Channel triggers by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/62 -- Generate Actions by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/63 -- Add option to get groupMembers as Items by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/65 -- Memberof Trigger by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/66 -- Fix buffer being read twice and breaking classloading by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/67 -- Fix missing precondition support for timer rules by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/68 -- Fix timer trigger by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/70 -- Initial tests for JRuleWhenItemChange triggers by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/73 -- Threadlocal logging - some improvements by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/79 -- Junit test for duplicate rule invocations by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/75 -- Add docker integration test by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/77 -- Include old thing status in event by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/80 -- Use thread safe list instead of arraylist by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/81 -- Defer to parent classloader if file not found by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/83 -- Fix inheritance in actions by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/87 -- Fix mqtt for tests by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/91 -- Fix ConcurrentModificationException in test by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/92 -- Added typing for thing channel triggers, ie `JRuleWhen(channel = binding_thing.triggerChannel)` instead of typing the channel id string - -## BETA14 -- Thing support in rules by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/59 - - BREAKING: jrule-items.jar has been renamed to jrule-generated.jar -- Added missing sendCommand for StopMove commands by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/57 -- Fixed parsing of double value for Quantity type by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/56 -- Added generic action handler by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/55 see exampe #34 -- Refactoring of event for channel plus cleanup by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/52 -- Refactoring of persistance functions and item handling with exceptions by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/51 -- Added item id and fixes for generated items by [LumnitzF](https://github.com/LumnitzF) pr https://github.com/seaside1/jrule/pull/50 -- Added MDC Logging tags to be used with elastic search (logstash,kibana and similar) by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/49 -- Fixed parsing of double values in rule conditions by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/48 -- Fixed parsing of UNDEF by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/45 - -## BETA13 -- Fixed a bug with naming of JRuleItems.java -- Fixed issues with post and sendCommand to groups - -## BETA12 - - Major refactoring by seime https://github.com/seaside1/jrule/pull/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 - -## BETA11 - - Wrap TransformationException in JRuleExecutionException by seime https://github.com/seaside1/jrule/pull/39 - - Add equivalent postUpdate logging as sendCommand by seime https://github.com/seaside1/jrule/pull/38 - - Fix group sendCommand for UpDown by seime https://github.com/seaside1/jrule/pull/36 - - Added eq and neq to channel event by gerrieg https://github.com/seaside1/jrule/pull/35 - - Added support for ZonedDateTime in DateTimeItem by gerrieg https://github.com/seaside1/jrule/pull/34 - - Fixed issued with undef item for state - - Added mocked eventbus for testing rules with junit -## BETA10 - - Optimized items by gerrieg https://github.com/seaside1/jrule/pull/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) -## BETA9 - - Fixed bug with item generation and forName overloading -## BETA8 - - Added forName for items see example 27 https://github.com/seaside1/jrule/commit/0952ae497d998c5728a85df407bfbf3f1909f8e9 - - Added itemName and OldState by gerrieg https://github.com/seaside1/jrule/pull/31 -## 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: https://github.com/seaside1/jrule/pull/30 -- Added possibility to use subdirs and packages for rules by seime: https://github.com/seaside1/jrule/pull/30 -## BETA6 -- Added seprate thread executors supplied by seime: https://github.com/seaside1/jrule/pull/23 -- Added Windows Support by LumnitzF https://github.com/seaside1/jrule/pull/24 -- Added cancellation of repeating timers by seime: https://github.com/seaside1/jrule/pull/26 -- Null check on timers with futures by seime https://github.com/seaside1/jrule/pull/29 -- Update to Openhab 3.3.0 -- Fixed rescheduling of timers by seime: https://github.com/seaside1/jrule/pull/28 -- Added check for repeating timers by seime: https://github.com/seaside1/jrule/pull/27 -- Fixed repeated timer executing directly by seime: https://github.com/seaside1/jrule/pull/25 -- Added transformation sevice example 23 by seime: https://github.com/seaside1/jrule/pull/20 -- Improved compiler error logging by seime: https://github.com/seaside1/jrule/pull/12 -- Added UP/Down support for group items by seime: https://github.com/seaside1/jrule/pull/13 -- Quantity Type support by seime: https://github.com/seaside1/jrule/pull/14 -- Configurable item-prefix by weberjn https://github.com/seaside1/jrule/pull/16 -- Improve exception handling by No3x https://github.com/seaside1/jrule/pull/19 -- Made thread executors configurable, default disabled -- Possible to build jrule standalone https://github.com/seaside1/jrule/pull/24 or together with Openhab-addons -- Cleaned up repostiory from binary files. Remove and old clones of jrule and clone a fresh repo -## BETA5 -- Addes support for adding rules in jar-files, as an alternative. -## BETA4 -- Added config for character to be used when generating items files -## BETA3 -- Major refactoring of logging -## 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 -## BETA1 -- Added color item see example 20 -- Moved org.openhab.automation.jrule.rules.JRuleOnOffvalue, JRulePlayPause etc to org.openhab.automation.jrule.rules.value - -## ALPHA12 -- Fix some language typos, some refactor of java classes, improved initialization of singletons due to concurrency aspects -## ALPHA11 -- Added check for working dir via system properties -## ALPHA10 -- Added LatUpdate via JRulePersistenceExtentions see example 19 - -## ALPHA9 -- Added cron expressions for rules see example 18 -- Bug fix by @roth for reloading channel triggers - -## ALPHA8 -- Channel triggers provided by @roth see example 17 - -## ALPHA7 -- Fixed bug with group member value was null for non StringType types - -## ALPHA6 -- Added group functionality getMember will return who triggered a change for a group - -## 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 - -## 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 - -## ALPHA3 -- Fixed issue when reloading rules if they are changed with monitored items -- Fixed classpath issue when executing rules using 3rd party libraries - -## ALPHA2 -- Added possibility to include 3rd party libraries when developing rules - -## ALPHA1 -- Refactored internal jar dependencies and jar-generation -- Added eq comparator for number triggers in rules - -# Roadmap -- Locks and timers by annotation -- Built in expire functionality diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md new file mode 100644 index 00000000..a6e1e2e3 --- /dev/null +++ b/doc/CHANGELOG.md @@ -0,0 +1,215 @@ +## BETA15 + +- BREAKING: All JRuleWhen has to be change to corresponding JRuleWhenItemChanged (as an example, look at JRule Examples + documentation) +- JRule When refactoring by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/61 +- Thing Channel triggers by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/62 +- Generate Actions by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/63 +- Add option to get groupMembers as Items by [querdenker2k](https://github.com/querdenker2k) + PR https://github.com/seaside1/jrule/pull/65 +- Memberof Trigger by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/66 +- Fix buffer being read twice and breaking classloading by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/67 +- Fix missing precondition support for timer rules by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/68 +- Fix timer trigger by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/70 +- Initial tests for JRuleWhenItemChange triggers by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/73 +- Threadlocal logging - some improvements by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/79 +- Junit test for duplicate rule invocations by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/75 +- Add docker integration test by [querdenker2k](https://github.com/querdenker2k) + PR https://github.com/seaside1/jrule/pull/77 +- Include old thing status in event by [seime](https://github.com/seime) PR https://github.com/seaside1/jrule/pull/80 +- Use thread safe list instead of arraylist by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/81 +- Defer to parent classloader if file not found by [seime](https://github.com/seime) + PR https://github.com/seaside1/jrule/pull/83 +- Fix inheritance in actions by [querdenker2k](https://github.com/querdenker2k) + PR https://github.com/seaside1/jrule/pull/87 +- Fix mqtt for tests by [querdenker2k](https://github.com/querdenker2k) PR https://github.com/seaside1/jrule/pull/91 +- Fix ConcurrentModificationException in test by [querdenker2k](https://github.com/querdenker2k) + PR https://github.com/seaside1/jrule/pull/92 +- Added typing for thing channel triggers, ie `JRuleWhen(channel = binding_thing.triggerChannel)` instead of typing the + channel id string + +## BETA14 + +- Thing support in rules by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/59 + - BREAKING: jrule-items.jar has been renamed to jrule-generated.jar +- Added missing sendCommand for StopMove commands by [seime](https://github.com/seime) + pr https://github.com/seaside1/jrule/pull/57 +- Fixed parsing of double value for Quantity type by [seime](https://github.com/seime) + pr https://github.com/seaside1/jrule/pull/56 +- Added generic action handler by [querdenker2k](https://github.com/querdenker2k) + pr https://github.com/seaside1/jrule/pull/55 see exampe #34 +- Refactoring of event for channel plus cleanup by [querdenker2k](https://github.com/querdenker2k) + pr https://github.com/seaside1/jrule/pull/52 +- Refactoring of persistance functions and item handling with exceptions + by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/51 +- Added item id and fixes for generated items by [LumnitzF](https://github.com/LumnitzF) + pr https://github.com/seaside1/jrule/pull/50 +- Added MDC Logging tags to be used with elastic search (logstash,kibana and similar) + by [querdenker2k](https://github.com/querdenker2k) pr https://github.com/seaside1/jrule/pull/49 +- Fixed parsing of double values in rule conditions by [seime](https://github.com/seime) + pr https://github.com/seaside1/jrule/pull/48 +- Fixed parsing of UNDEF by [seime](https://github.com/seime) pr https://github.com/seaside1/jrule/pull/45 + +## BETA13 + +- Fixed a bug with naming of JRuleItems.java +- Fixed issues with post and sendCommand to groups + +## BETA12 + +- Major refactoring by seime https://github.com/seaside1/jrule/pull/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 + +## BETA11 + +- Wrap TransformationException in JRuleExecutionException by seime https://github.com/seaside1/jrule/pull/39 +- Add equivalent postUpdate logging as sendCommand by seime https://github.com/seaside1/jrule/pull/38 +- Fix group sendCommand for UpDown by seime https://github.com/seaside1/jrule/pull/36 +- Added eq and neq to channel event by gerrieg https://github.com/seaside1/jrule/pull/35 +- Added support for ZonedDateTime in DateTimeItem by gerrieg https://github.com/seaside1/jrule/pull/34 +- Fixed issued with undef item for state +- Added mocked eventbus for testing rules with junit + +## BETA10 + +- Optimized items by gerrieg https://github.com/seaside1/jrule/pull/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) + +## BETA9 + +- Fixed bug with item generation and forName overloading + +## BETA8 + +- Added forName for items see example + 27 https://github.com/seaside1/jrule/commit/0952ae497d998c5728a85df407bfbf3f1909f8e9 +- Added itemName and OldState by gerrieg https://github.com/seaside1/jrule/pull/31 + +## 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: https://github.com/seaside1/jrule/pull/30 +- Added possibility to use subdirs and packages for rules by seime: https://github.com/seaside1/jrule/pull/30 + +## BETA6 + +- Added seprate thread executors supplied by seime: https://github.com/seaside1/jrule/pull/23 +- Added Windows Support by LumnitzF https://github.com/seaside1/jrule/pull/24 +- Added cancellation of repeating timers by seime: https://github.com/seaside1/jrule/pull/26 +- Null check on timers with futures by seime https://github.com/seaside1/jrule/pull/29 +- Update to Openhab 3.3.0 +- Fixed rescheduling of timers by seime: https://github.com/seaside1/jrule/pull/28 +- Added check for repeating timers by seime: https://github.com/seaside1/jrule/pull/27 +- Fixed repeated timer executing directly by seime: https://github.com/seaside1/jrule/pull/25 +- Added transformation sevice example 23 by seime: https://github.com/seaside1/jrule/pull/20 +- Improved compiler error logging by seime: https://github.com/seaside1/jrule/pull/12 +- Added UP/Down support for group items by seime: https://github.com/seaside1/jrule/pull/13 +- Quantity Type support by seime: https://github.com/seaside1/jrule/pull/14 +- Configurable item-prefix by weberjn https://github.com/seaside1/jrule/pull/16 +- Improve exception handling by No3x https://github.com/seaside1/jrule/pull/19 +- Made thread executors configurable, default disabled +- Possible to build jrule standalone https://github.com/seaside1/jrule/pull/24 or together with Openhab-addons +- Cleaned up repostiory from binary files. Remove and old clones of jrule and clone a fresh repo + +## BETA5 + +- Addes support for adding rules in jar-files, as an alternative. + +## BETA4 + +- Added config for character to be used when generating items files + +## BETA3 + +- Major refactoring of logging + +## 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 + +## BETA1 + +- Added color item see example 20 +- Moved org.openhab.automation.jrule.rules.JRuleOnOffvalue, JRulePlayPause etc to + org.openhab.automation.jrule.rules.value + +## ALPHA12 + +- Fix some language typos, some refactor of java classes, improved initialization of singletons due to concurrency + aspects + +## ALPHA11 + +- Added check for working dir via system properties + +## ALPHA10 + +- Added LatUpdate via JRulePersistenceExtentions see example 19 + +## ALPHA9 + +- Added cron expressions for rules see example 18 +- Bug fix by @roth for reloading channel triggers + +## ALPHA8 + +- Channel triggers provided by @roth see example 17 + +## ALPHA7 + +- Fixed bug with group member value was null for non StringType types + +## ALPHA6 + +- Added group functionality getMember will return who triggered a change for a group + +## 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 + +## 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 + +## ALPHA3 + +- Fixed issue when reloading rules if they are changed with monitored items +- Fixed classpath issue when executing rules using 3rd party libraries + +## ALPHA2 + +- Added possibility to include 3rd party libraries when developing rules + +## ALPHA1 + +- Refactored internal jar dependencies and jar-generation +- Added eq comparator for number triggers in rules diff --git a/doc/EXAMPLES.md b/doc/EXAMPLES.md new file mode 100644 index 00000000..f3efd286 --- /dev/null +++ b/doc/EXAMPLES.md @@ -0,0 +1,1040 @@ +# Examples + +- [Examples](#examples) + + [Example 1 - Invoke another item Switch from rule](#example-1---invoke-another-item-switch-from-rule) + + [Example 2 - Using a timed lock](#example-2---using-a-timed-lock) + + [Example 3 - Using event data](#example-3---using-event-data) + + [Example 4 - Multiple rule triggers](#example-4---multiple-rule-triggers) + + [Example 5 - Creating a custom parent rule class](#example-5---creating-a-custom-parent-rule-class) + + [Example 6 - Using a custom parent rule class](#example-6---using-a-custom-parent-rule-class) + + [Example 7 - Reusing rule functionality](#example-7---reusing-rule-functionality) + + [Example 8 - Creating a timer](#example-8---creating-a-timer) + + [Example 9 - Creating a repeating timer](#example-9---creating-a-repeating-timer) + + [Example 10 - Creating a timer #2](#example-10---creating-a-timer--2) + + [Example 11 - Using JRuleWhenTimeTrigger](#example-11---using-jrulewhentimetrigger) + + [Example 12 - Condition on trigger](#example-12---condition-on-trigger) + + [Example 13 - Text to speech](#example-13---text-to-speech) + + [Example 14 - Executing a shell command](#example-14---executing-a-shell-command) + + [Example 15 - Group items](#example-15---group-items) + + [Example 16 - Group items #2](#example-16---group-items--2) + + [Example 17 - Channel triggers](#example-17---channel-triggers) + + [Example 18 - Cron based trigger](#example-18---cron-based-trigger) + + [Example 19 - Persistence and lastUpdated](#example-19---persistence-and-lastupdated) + + [Example 20 - Color item](#example-20---color-item) + + [Example 21 - Set logging name for a specific rule](#example-21---set-logging-name-for-a-specific-rule) + + [Example 22 - Override logging for all rules defined in one file](#example-22---override-logging-for-all-rules-defined-in-one-file) + + [Example 23 - Apply transformation using openHAB transformation service](#example-23---apply-transformation-using-openhab-transformation-service) + + [Example 24 - Preconditions #1](#example-24---preconditions--1) + + [Example 25 - Preconditions #2](#example-25---preconditions--2) + + [Example 26 - Send Quantity type Watt (W) from rule](#example-26---send-quantity-type-watt--w--from-rule) + + [Example 27 - Use forName to create and item and send commands and get status](#example-27---use-forname-to-create-and-item-and-send-commands-and-get-status) + + [Example 27b - Use forNameOptional to create and item and send commands and get status](#example-27b---use-fornameoptional-to-create-and-item-and-send-commands-and-get-status) + + [Example 28 - Get the name of the item that triggered the rule as well as new and old state value](#example-28---get-the-name-of-the-item-that-triggered-the-rule-as-well-as-new-and-old-state-value) + + [Example 29 - Get average value for a Number item last hour](#example-29---get-average-value-for-a-number-item-last-hour) + + [Example 30 - Use generated JRuleItems.java to get hold of items](#example-30---use-generated-jruleitemsjava-to-get-hold-of-items) + + [Example 31 - Restart thing every night due to binding flakyness](#example-31---restart-thing-every-night-due-to-binding-flakyness) + + [Example 32 - Detect if a specific thing goes offline, wait for it to come online again within a given time](#example-32---detect-if-a-specific-thing-goes-offline--wait-for-it-to-come-online-again-within-a-given-time) + + [Example 33 - Listen for thing status events on _all_ things](#example-33---listen-for-thing-status-events-on--all--things) + + [Example 34 - Thing actions, send message with pushover and other services](#example-34---thing-actions--send-message-with-pushover-and-other-services) + + [Example 35 - Listen on all Item events of a group (without the groupstate must change)](#example-35---listen-on-all-item-events-of-a-group--without-the-groupstate-must-change-) + + [Example 36 - Listen for group changes - with conditions](#example-36---listen-for-group-changes---with-conditions) + + [Example 37 - Timer chaining](#example-37---timer-chaining) + + [Example 38 - Debounce](#example-38---debounce) + + [Example 39 - HTTP requests](#example-39---http-requests) + + [Example 40 - Delay rule execution](#example-40---delay-rule-execution) + +### Example 1 - Invoke another item Switch from rule + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("MyRuleTurnSwich2On") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void execChangedToRule() { + logInfo("||||| --> Executing rule MyRule: changed to on"); + JRuleItems.MySwitch2.sendCommand(ON); + } +} +``` + +### Example 2 - Using a timed lock + +Use case: Invoke a Doorbell, but only allow the rule to be invoked once every 20 seconds. +This is done by acquiring a lock getTimedLock("MyLockTestRule1", 20). + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.OFF; +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch2; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("MyLockTestRule1") + @JRuleWhenItemChange(item = MyTestSwitch2, from = OFF, to = ON) + public void execLockTestRule() { + if (getTimedLock("MyLockTestRule1", 20)) { + JRuleItems.MyDoorBellItem.sendCommand(ON); + logInfo("||||| --> Got Lock! Ding-dong !"); + } else { + logInfo("||||| --> Ignoring call to rule it is locked!"); + } + } +} +``` + +### Example 3 - Using event data + +Use case: Use the value that caused the trigger +When the rule is triggered, the triggered value is stored in the event. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch2; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("MyEventValueTest") + @JRuleWhenItemReceivedCommand(item = MyTestSwitch2) + public void myEventValueTest(JRuleEvent event) { + logInfo("Got value from event: {}", event.getState().getValue()); + } +} +``` + +### Example 4 - Multiple rule triggers + +Use case: Or statement for rule trigger +To add an OR statement we simply add multiple @JRuleWhenXXX statements + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestNumber; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("MyNumberRule1") + @JRuleWhenItemChange(item = MyTestNumber, from = "14", to = "10") + @JRuleWhenItemChange(item = MyTestNumber, from = "10", to = "12") + public void myOrRuleNumber(JRuleEvent event) { + logInfo("Got change number: {}", event.getState().asStringValue()); + // or + logInfo("Got change number: {}", event.getItem().getState().asStringValue()); + } +} +``` + +### Example 5 - Creating a custom parent rule class + +Use case: Define your own functionality +Create a Rules class that extends: JRuleUser.java +JRuleUser.java should be placed in the same folder as your rules +The JRuleUser class can contain common functions and functionality you want to reuse in your rules: + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRule; + +public abstract class JRuleUser extends JRule { + +} +``` + +### Example 6 - Using a custom parent rule class + +Your class rules can now extend the JRuleUser +package org.openhab.automation.jrule.rules.user; + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import org.openhab.automation.jrule.rules.user.JRuleUser; + +public class MySwitchRule extends JRuleUser { + +} +``` + +### Example 7 - Reusing rule functionality + +Let's say we want to add a common function that should be available for all user rules. +We want to add a function that checks if it is ok to send notifications depends on what time it is. +We'll do this: + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRule; + +public class JRuleUser extends JRule { + + private static final int startDay = 8; + private static final int endDay = 21; + + protected boolean timeIsOkforDisturbance() { + return nowHour() >= startDay && nowHour() <= endDay; + } +} +``` + +We then extend the rule from the Java Rules file: + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.OFF; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; + +public class MyTestUserRule extends JRuleUser { + + @JRuleName("TestUserDefinedRule") + @JRuleWhenItemChange(item = MyTestSwitch, from = OFF, to = ON) + public void mySendNotificationRUle(JRuleEvent event) { + if (timeIsOkforDisturbance()) { + logInfo("It's ok to send a disturbing notification"); + } + } +} +``` + +### Example 8 - Creating a timer + +Use case create a timer for automatically turning off a light when it is turned on. If it's running cancel it and +schedule a new one. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyLightSwitch; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("myTimerRule") + @JRuleWhenItemChange(item = MyLightSwitch, to = ON) + public synchronized void myTimerRule(JRuleEvent event) { + logInfo("Turning on light it will be turned off in 2 mins"); + createOrReplaceTimer(MyLightSwitch, Duration.ofMinutes(2), new Runnable() { + @Override + public void run() { + logInfo("Time is up! Turning off lights"); + JRuleItems.MyLightSwitch.sendCommand(OFF); + } + }); + } +} +``` + +### Example 9 - Creating a repeating timer + +Use case: Let's say we have a 433 MHz wall socket with no ON/OFF feedback and a bit of bad radio reception. We can then +create a repeating timer +to send multiple ON statements to be sure it actually turns on. +createOrReplaceRepeatingTimer("myRepeatingTimer", 7, 4, will create a repeating timer that will trigger after 0 seconds, +7s, 14s and 21s +If the Timer is already running it will cancel it and create a new one. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("repeatRuleExample") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public synchronized void repeatRuleExample(JRuleEvent event) { + createOrReplaceRepeatingTimer("myRepeatingTimer", 7, Duration.ofSeconds(10), new Runnable() { + @Override + public void run() { + String messageOn = "repeatRuleExample Repeating....."; + logInfo(messageOn); + JRuleItems.MyBad433Switch.sendCommand(ON); + } + }); + } +} +``` + +### Example 10 - Creating a timer #2 + +Use case Create a simple timer. When MyTestSwitch turns on it will wait 10 seconds and then turn MyTestSwitch2 to on. +Note that it will not reschedule the timer, if the timer is already running it won't reschedule it. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("timerRuleExample") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public synchronized void timerRuleExample(JRuleEvent event) { + createTimer("myTimer", Duration.ofSeconds(10), new Runnable() { + @Override + public void run() { + String messageOn = "timer example."; + logInfo(messageOn); + JRuleItems.MyTestWitch2.sendCommand(ON); + } + }); + } +} +``` + +### Example 11 - Using JRuleWhenTimeTrigger + +Use case trigger a rule at 22:30 in the evening to set initial brightness for a ZwaveDimmer to 30% + +```java +package org.openhab.automation.jrule.rules.user; + +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("setDayBrightness") + @JRuleWhenTimeTrigger(hours = 22, minutes = 30) + public synchronized void setDayBrightness(JRuleEvent event) { + logInfo("Setting night brightness to 30%"); + int dimLevel = 30; + JRuleItems.MyDimmerBrightness.sendCommand(dimLevel); + } +} +``` + +### Example 12 - Condition on trigger + +Use case: If temperature is below or equals to 20 degrees send command on to a heating fan +It is possible to use: +lte = less than or equals +lt = less than +gt = greater than +gte = greater than or equals +eq = equals +neq = not equals + +```java +package org.openhab.automation.jrule.rules.user; + +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("turnOnFanIfTemperatureIsLow") + @JRuleWhenItemChange(item = MyTemperatureSensor, condition = @JRuleCondition(lte = 20)) + public synchronized void turnOnFanIfTemperatureIsLow(JRuleEvent event) { + logInfo("Starting fan since temperature dropped below 20"); + JRuleItems.MyHeatingFanSwitch.sendCommand(JRuleOnOffValue.ON); + } +} +``` + +### Example 13 - Text to speech + +Use case: Using say command for tts + +```java +package org.openhab.automation.jrule.rules.user; + +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("testSystemTts") + @JRuleWhenItemChange(item = TestSystemTts, to = ON) + public synchronized void testSystemTts(JRuleEvent event) { + logInfo("System TTS Test"); + String message = "Testing tts! I hope you can hear it!"; + say(message, null, "sonos:PLAY5:RINCON_XXYY5857B06E0ZZOO"); + } +} +``` + +### Example 14 - Executing a shell command + +Use case: Executing command from CLI + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MySwitchGroup; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("TestExecutingCommandLine") + @JRuleWhenItemReceivedCommand(item = MySwitchGroup) + public synchronized void testExecutingCommandLine(JRuleEvent event) { + logInfo("Creating dummy file using CLI"); + executeCommandLine("touch", "/openhab/userdata/example.txt"); + } +} +``` + +### Example 15 - Group items + +Use case: A group of switches, see if status is changed, and also which member in the group changed state + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MySwitchGroup; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("groupMySwitchesChanged") + @JRuleWhenItemChange(item = MySwitchGroup) + public synchronized void groupMySwitchGroupChanged(JRuleEvent event) { + final boolean groupIsOnline = ((JRuleItemEvent) event).getState().getValueAsOnOffValue() == JRuleOnOffValue.ON; + final String memberThatChangedStatus = ((JRuleItemEvent) event).getMemberItem.getName(); + logInfo("Member that changed the status of the Group of switches: {}", memberThatChangedStatus); + } +} +``` + +### Example 16 - Group items #2 + +Use case: A group of switches , trigger when it's changed from OFF to ON + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MySwitchGroup; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("groupMySwitchesChangedOffToOn") + @JRuleWhenItemChange(item = MySwitchGroup, from = OFF, to = ON) + public synchronized void groupMySwitchesChangedOffToOn(JRuleEvent event) { + logInfo("Member that changed the status of the Group from OFF to ON: {}", event.getMemberItem().getName()); + } +} +``` + +### Example 17 - Channel triggers + +Use case: Listen for a Channel Trigger Event + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenChannelTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("ChannelTriggered") + @JRuleWhenChannelTrigger(channel = binding_thing.buttonevent) + public synchronized void channelTriggered(JRuleEvent event) { + logInfo("Channel triggered with value: {}", ((JRuleChannelEvent) event).getEvent()); + } +} +``` + +### Example 18 - Cron based trigger + +Use case: Cron based expression to trigger rule + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenCromTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("testCron") + @JRuleWhenCronTrigger(cron = "*/5 * * * * *") + public void testCron(JRuleEvent event) { + logInfo("CRON: Running cron from string every 5 seconds: {}", event); + } +} +``` + +### Example 19 - Persistence and lastUpdated + +Use case: getLastUpdated for an item +Note that `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM).getLastUpdated("mapdb");` +can be called without serviceId +argument: `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM).getLastUpdated();` + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenCronTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("testLastUpdate") + @JRuleWhenCronTrigger(cron = "4 * * * * *") + public void testLastUpdate(JRuleEvent event) { + logInfo("CRON: Running cron from string: {}", event.getState().getValue()); + ZonedDateTime lastUpdate = JRuleItems.MyCoolItem.getLastUpdated("mapdb"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd - HH:mm:ss Z"); + String lastUpdateFormatted = lastUpdate.format(formatter); + logInfo("Last Update: {}", lastUpdateFormatted); + } +} +``` + +### Example 20 - Color item + +Use case: Get the brigtness from a color item, set a color item to white (HSB 0, 0, 100) + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestColorItem; +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.JRule; + +public class DemoRule extends JRule { + + @JRuleName("testBrightnessFromColorItem") + @JRuleWhenItemChange(item = MyTestColorItem) + public void testBrightnessFromColorItem(JRuleEvent event) { + JRuleColorValue color = JRuleItems.MyTestColorItem.getState(); + int brightness = color.getHsbValue().getBrightness(); + } + + @JRuleWhenItemChange(item = MyTestColorItem) + public void testSetWhiteOnColorItem(JRuleEvent event) { + JRuleItems.MyTestColorItem.sendCommand(JRuleColorValue.fromHsb(0, 0, 100)); + } +} +``` + +### Example 21 - Set logging name for a specific rule + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +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.JRule; + +public class DemoRule extends JRule { + + @JRuleName("MyCustomLoggingRule") + @JRuleLogName("MYLOG") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void execChangedToRule() { + logInfo("||||| --> Executing rule MyRule: changed to on"); + JRuleItems.MySwitch2.sendCommand(ON); + } +} +``` + +### Example 22 - Override logging for all rules defined in one file + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class ColorRules extends JRule { + @JRuleName("MyCustomLoggingRuleOnClass") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void execChangedToRule() { + logInfo("||||| --> Executing rule MyRule: changed to on"); + JRuleItems.MySwitch2.sendCommand(ON); + } + + @Override + protected String getRuleLogName() { + return "CustomLogExample"; + } +} +``` + +### Example 23 - Apply transformation using openHAB transformation service + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyStringValue; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedCommand; +import org.openhab.automation.jrule.rules.JRule; + +public class TransformationRule extends JRule { + @JRuleName("MyTransformation") + @JRuleWhenItemReceivedCommand(item = MyStringValue) + public void applyTransformation(JRuleEvent event) { + String transformedValue = transform("MAP(my.map):%s", event.getState().getValue()); + logInfo("Transformed {} to {}", event.getState().getValue(), transformedValue); + JRuleItems.MyTransformationReceiver.sendCommand(transformedValue); + } +} +``` + +### Example 24 - Preconditions #1 + +Use case: Use precondition annotation in order to create "AND" logic. Example we have a switch that will tell +if it is ok for disturbance. If it is ok the switch is set to ON and we can send a notification if the notification +message is updated. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyDisturbanceSwitch; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedCommand; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + + @JRulePrecondition(item = MyTestDisturbanceSwitch, condition = @JRuleCondition(eq = "ON")) + @JRuleName("MyTestPreConditionRule1") + @JRuleWhenItemReceivedCommand(item = MyMessageNotification) + public void testPrecondition(JRuleEvent event) { + String notificationMessage = ((JRuleItemEvent) event).getState().getValue(); + logInfo("It is ok to send notification: {}", notificationMessage); + // JRuleItems.MySendNoticationItemMqtt.sendCommand(notificationMessage); + } +} +``` + +### Example 25 - Preconditions #2 + +Use case: Use precondition annotation in order to create "AND" logic. Example when the temperature is above 30 degrees ( +celcius probably) and +a motion detector is triggered we will turn on a fan. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyMotionDetector; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.ON; +import static org.openhab.automation.jrule.rules.JRuleOnOffValue.OFF; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRulePrecondition(item = MyTestTemperatureSensor, gt = 30) + @JRuleName("MyTestPreConditionRuleTemperature") + @JRuleWhenItemChange(item = MyMotionDetector, from = OFF, to = ON) + public void testPrecondition(JRuleEvent event) { + logInfo("Temperature is above 30 and we should start the fan since the motiondetector is triggered"); + JRuleItems.MyFan.sendCommand(ON); + } +} +``` + +### Example 26 - Send Quantity type Watt (W) from rule + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestMeterPower; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("testQuantityPowerWatt") + @JRuleWhenItemChange(item = MyTestMeterPower) + public void testQuantityPower(JRuleEvent event) { + logInfo("TestQuantity power will send this value as Watt: {}", event.getState().getValue()); + JRuleItems.TestPowerQuantityType.sendCommand(event.getState().getValueAsDouble(), "W"); + } +} +``` + +### Example 27 - Use forName to create and item and send commands and get status + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("testForName") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void testForName(JRuleEvent event) { + JRuleSwitchItem switchItem = JRuleSwitchItem.forName("MyOtherTestSwitch"); + switchItem.sendItemCommand(OFF); + if (switchItem.getItemStatus == ON) { + switchItem.sendItemCommand(OFF); + } + } +} +``` + +### Example 27b - Use forNameOptional to create and item and send commands and get status + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("testForNameOptional") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void testForName(JRuleEvent event) { + JRuleSwitchItem.forNameOptional("MyOtherTestSwitch").ifPresent(item -> item.sendCommand(true)); + } +} +``` + +### Example 28 - Get the name of the item that triggered the rule as well as new and old state value + +This can be useful if you have multiple JRuleWhen with different items, and you want to know which item +triggered the rule. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch1; +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch2; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("triggerNameExample") + @JRuleWhenItemChange(item = MyTestSwitch1, to = ON) + @JRuleWhenItemChange(item = MyTestSwitch2, to = ON) + public void triggerNameExample(JRuleEvent event) { + logInfo("The rule was triggered by the following item: {}", event.getItem().getName()); + logInfo("The rule was Old Value was: {} and new value: {}", event.getOldState().getValue(), event.getState().getValue()); + } +} +``` + +### Example 29 - Get average value for a Number item last hour + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenCronTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("testAverageLastHour") + @JRuleWhenCronTrigger(cron = "4 * * * * *") + public void testAverage(JRuleEvent event) { + Double average = JRuleNumberItem.forName(_MyNumberItem.ITEM).averageSince(ZonedDateTime.now().minus(1, ChronoUnit.HOURS)); + logInfo("Average value last hour: {}", average); + } +} +``` + +### Example 30 - Use generated JRuleItems.java to get hold of items + +For instance get state of an item. + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.items.JRuleItemNames.MyTestSwitch; +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("testItems") + @JRuleWhenItemChange(item = MyTestSwitch, to = ON) + public void testItems(JRuleEvent event) { + JRuleItems.MyOtherTestSwitch.getState(); + } +} +``` + +### Example 31 - Restart thing every night due to binding flakyness + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenTimeTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("Restart thing every night") + @JRuleWhenTimeTrigger(hours = 3) + public void restartThing() { + JRuleThings.my_flaky_thing.restart(); + } +} +``` + +### Example 32 - Detect if a specific thing goes offline, wait for it to come online again within a given time + +```java +package org.openhab.automation.jrule.rules.user; + +import static org.openhab.automation.jrule.generated.things.JRuleThings.remoteopenhab_thing; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenThingTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("Notify if thing stays offline") + @JRuleWhenThingTrigger(thing = remoteopenhab_thing.ID, from = JRuleThingStatus.ONLINE) + public void warnIfThingStaysOffline() { + createOrReplaceTimer("MY_TIMER", Duration.ofMinutes(3), () -> { + if (JRuleThings.remoteopenhab_thing.getStatus() != JRuleThingStatus.ONLINE) { + logWarn("Thing {} is still offline, restarting", remoteopenhab_thing.ID); + JRuleThings.remoteopenhab_thing.restart(); + } + }); + } +} +``` + +### Example 33 - Listen for thing status events on _all_ things + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenThingTrigger; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("Log every thing that goes offline") + @JRuleWhenThingTrigger(from = JRuleThingStatus.ONLINE) + public void startTrackingNonOnlineThing(JRuleEvent event) { + String offlineThingUID = event.getThing(); + // ... + } +} +``` + +### Example 34 - Thing actions, send message with pushover and other services + +Note that you will have to set up a pushover account as thing in openHAB. + +```java +package org.openhab.automation.jrule.rules.user; + +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("PushOverTest") + @JRuleWhenItemChange(item = MyTestSendPushOverButton, to = ON) + public void testPower(JRuleEvent event) { + logInfo("Sending Test message using pushover via actions"); + JRuleActions.pushoverPushoverAccountXYZ.sendMessage("MyMessage", "MyTitle"); + } +} +``` + +### Example 35 - Listen on all Item events of a group (without the groupstate must change) + +Alternatively you could just listen to just Group changes or (real) Item changes + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedUpdate; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("MemberOfUpdateTrigger") + @JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.All) +//@JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.Items) // .. or this +//@JRuleWhenItemReceivedUpdate(item = MySwitchGroup, memberOf = JRuleMemberOf.Groups) // .. or this + public synchronized void memberOfUpdateTrigger(JRuleItemEvent event) { + final String memberThatChangedStatus = event.getMemberName(); + logInfo("Member that changed the status of the Group of switches: {}", memberThatChangedStatus); + } +} +``` + +### Example 36 - Listen for group changes - with conditions + +Use case: Want to listen just on changes where the state is now greater/equals then 12 and was before less then 12. +Without the previous condition the rule will be triggered every time the state is greater/equals then 12. + +```java +package org.openhab.automation.jrule.rules.user; + +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.JRule; + +public class DemoRule extends JRule { + @JRuleName("Change from something less to something greater") + @JRuleWhenItemChange(item = ITEM_FROM_TO, previousCondition = @JRuleCondition(lt = 12), condition = @JRuleCondition(gte = 12)) + public void itemChangeFromTo(JRuleEvent event) { + logInfo("state change to something >= 12 and was before < 12"); + } +} +``` + +### Example 37 - Timer chaining + +Use case: Chain timers. Execute one and after this is expired, execute the next one. + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("Notify if thing stays offline") + @JRuleWhenItemChange(item = MySwitchGroup) + public void chainSomeTimers() { + createTimer(Duration.ofSeconds(3), () -> { + logInfo("First timer finished after 3 seconds"); + }).createTimerAfter(Duration.ofSeconds(10), () -> { + logInfo("Second timer finished after 10 more seconds"); + }); + } +} +``` + +### Example 38 - Debounce + +Use case: Do not execute a rule too often + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleDebounce; + +public class DemoRule extends JRule { + @JRuleDebounce(10) + @JRuleName("Notify if thing stays offline") + @JRuleWhenItemChange(item = MySwitchGroup) + public void debounceMethod() { + // super critical stuff which shouldn't be called too often + } +} +``` + +### Example 39 - HTTP requests + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedCommand; +import org.openhab.automation.jrule.rules.JRule; + +public class DemoRule extends JRule { + @JRuleName("send http methods") + @JRuleWhenItemReceivedCommand(item = MyHttpTrigger, command = "send http calls") + public void sendHttpCalls() { + String responseGet = sendHttpGetRequest("http://http-mock:8080" + HTTP_GET_SOMETHING, null); + logInfo("send Http: {}", responseGet); + sendHttpDeleteRequest("http://http-mock:8080" + HTTP_DELETE_SOMETHING, Duration.ofSeconds(5)); + } +} +``` + +### Example 40 - Delay rule execution + +Use case: Execute a rule delayed without manually creating a timer + +```java +package org.openhab.automation.jrule.rules.user; + +import org.openhab.automation.jrule.rules.JRuleName; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleDelayed; + +public class DemoRule extends JRule { + @JRuleDelayed(10) + @JRuleName("Execute after ten seconds") + @JRuleWhenItemChange(item = MySwitchGroup) + public void delayedMethod() { + // delay the execution of this + } +} +``` diff --git a/src/test/java/org/openhab/binding/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java b/src/test/java/org/openhab/binding/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java index 5eb690b8..ab3b56ae 100644 --- a/src/test/java/org/openhab/binding/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java +++ b/src/test/java/org/openhab/binding/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java @@ -37,6 +37,7 @@ import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; import org.openhab.automation.jrule.internal.handler.JRuleEventHandler; import org.openhab.automation.jrule.items.JRuleItemClassGenerator; +import org.openhab.automation.jrule.items.JRuleItemRegistry; import org.openhab.automation.jrule.test_utils.JRuleItemTestUtils; import org.openhab.core.items.GenericItem; import org.openhab.core.items.GroupItem; @@ -79,6 +80,7 @@ public void setup() { JRuleConfig config = new JRuleConfig(map); sourceFileGenerator = new JRuleItemClassGenerator(config); compiler = new JRuleCompiler(config); + JRuleItemRegistry.clear(); } @BeforeEach