diff --git a/MIGRATE.md b/MIGRATE.md new file mode 100644 index 00000000..2cfd7a2b --- /dev/null +++ b/MIGRATE.md @@ -0,0 +1,87 @@ +# Migrate JRule to JRuleXXX + +## Triggers + +### Item changed to ON +@JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + +-> + +@JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) + +### Item changed from OFF to ON +@JRuleWhen(item = _MyTestSwitch2.ITEM, trigger = _MyTestSwitch2.TRIGGER_CHANGED_FROM_OFF_TO_ON) + +-> + +@JRuleWhenItemChange(item = _MyTestSwitch2.ITEM, from = JRuleSwitchItem.OFF, to = JRuleSwitchItem.ON) + +### Item received command +@JRuleWhen(item = __MyTestSwitch2.ITEM, trigger = __MyTestSwitch2.TRIGGER_RECEIVED_COMMAND) + +-> + +@JRuleWhenItemReceivedCommand(item = _MyTestSwitch2.ITEM) + +### Item changed with conditional check +@JRuleWhen(item = _MyTemperatureSensor.ITEM, trigger = _MyTemperatureSensor.TRIGGER_RECEIVED_UPDATE, lte = 20) + +-> + +@JRuleWhenItemChange(item = _MyTemperatureSensor.ITEM, condition = @JRuleCondition(lte = 20)) + +### Channel changed +@JRuleWhen(channel = "binding:thing:buttonevent") + +-> + +@JRuleWhenChannelTrigger(channel = "mqtt:topic:mqtt:generic:numberTrigger") + +### Cron +@JRuleWhen(cron = "4 * * * * *") + +-> + +@JRuleWhenCronTrigger(cron = "*/5 * * * * *") + +### Time +@JRuleWhen(hours = 3) + +-> + +@JRuleWhenTimeTrigger(hours=3) + +### Thing changed +@JRuleWhen(thing = "*", trigger = JRuleThingStatusTrigger.TRIGGER_CHANGED, from = "ONLINE") + +-> + +@JRuleWhenThingTrigger(from = JRuleThingStatus.ONLINE) + +## Precondition + +### Item must be equal to +@JRulePrecondition(item=_MyTestDisturbanceSwitch.ITEM, eq = "ON") + +-> + +@JRulePrecondition(item = _MyTestDisturbanceSwitch.ITEM, condition = @JRuleCondition(eq = "ON")) + +## JRuleEvent +JRuleEvent is now abstract with 4 concrete implementations +- JRuleItemEvent +- JRuleChannelEvent +- JRuleTimerEvent +- JRuleThingEvent +Depending on the trigger that was fired, the respective event will be thrown. +You can use the abstract JRuleEvent as method parameter or a concrete one, but take care to be sure that just this event can be thrown. +Otherwise an exception will occur. + +### Examples +#### Correct +Trigger for an Item will inject an JRuleItemEvent as method parameter +For the method you can use JRuleEvent (has to be cast to get the interesting information) or JRuleItemEvent + +#### Incorrect +- Trigger for a Thing and use JRuleItemEvent as method parameter -> Exception +- Trigger for an Item and a Cron and use JRuleItemEvent -> Exception when the method will be trigger for cron \ No newline at end of file diff --git a/README.md b/README.md index a03a5cf4..9ad33b26 100644 --- a/README.md +++ b/README.md @@ -87,16 +87,14 @@ Switch MyTestSwitch2 "Test Switch 2" (JRule) ```java package org.openhab.automation.jrule.rules.user; -import static org.openhab.automation.jrule.rules.value.JRuleOnOffValue.ON; + import org.openhab.automation.jrule.items.generated._MyTestSwitch; import org.openhab.automation.jrule.rules.JRule; import org.openhab.automation.jrule.rules.JRuleName; -import org.openhab.automation.jrule.rules.JRuleWhen; public class MySwitchRule extends JRule { - + @JRuleName("MySwitchRule") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) public void execOffToOnRule() { logInfo("||||| --> Hello World!"); } @@ -164,7 +162,7 @@ See example 12, 25 and 26 Use Case: Invoke another item Switch from rule ```java @JRuleName("MyRuleTurnSwich2On") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public void execChangedToRule() { logInfo("||||| --> Executing rule MyRule: changed to on"); JRuleItems.MySwitch2.sendCommand(ON); @@ -178,7 +176,7 @@ This is done by acquiring a lock getTimedLock("MyLockTestRule1", 20). ```java @JRuleName("MyLockTestRule1") - @JRuleWhen(item = _MyTestSwitch2.ITEM, trigger = _MyTestSwitch2.TRIGGER_CHANGED_FROM_OFF_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch2.ITEM, from = JRuleSwitchItem.OFF, to = JRuleSwitchItem.ON) public void execLockTestRule() { if (getTimedLock("MyLockTestRule1", 20)) { JRuleItems.MyDoorBellItem.sendCommand(ON); @@ -194,11 +192,11 @@ 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") - @JRuleWhen(item = __MyTestSwitch2.ITEM, trigger = __MyTestSwitch2.TRIGGER_RECEIVED_COMMAND) - public void myEventValueTest(JRuleEvent event) { - logInfo("Got value from event: {}", event.getState().getValue()); - } + @JRuleName("MyEventValueTest") + @JRuleWhenItemReceivedCommand(item = _MyTestSwitch2.ITEM) + public void myEventValueTest(JRuleEvent event) { + logInfo("Got value from event: {}", event.getState().getValue()); + } ``` ## Example 4 @@ -206,12 +204,12 @@ Use case: Or statement for rule trigger To add an OR statement we simply add multiple @JRuleWhen statements ```java - @JRuleName("MyNumberRule1") - @JRuleWhen(item = _MyTestNumber.ITEM, trigger = _MyTestNumber.TRIGGER_CHANGED, from = "14", to = "10") - @JRuleWhen(item = _MyTestNumber.ITEM, trigger = _MyTestNumber.TRIGGER_CHANGED, from = "10", to = "12") - public void myOrRuleNumber(JRuleEvent event) { - logInfo("Got change number: {}", event.getState().getValue()); - } + @JRuleName("MyNumberRule1") + @JRuleWhenItemChange(item = _MyTestNumber.ITEM, from = "14", to = "10") + @JRuleWhenItemChange(item = _MyTestNumber.ITEM, from = "10", to = "12") + public void myOrRuleNumber(JRuleEvent event) { + logInfo("Got change number: {}", event.getState().getValue()); + } ``` ## Example 5 @@ -235,16 +233,15 @@ public class JRuleUser extends JRule { 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.items.generated._MyTestSwitch; -import org.openhab.automation.jrule.rules.JRule; import org.openhab.automation.jrule.rules.user.JRuleUser; -import org.openhab.automation.jrule.rules.JRuleName; -import org.openhab.automation.jrule.rules.JRuleWhen; public class MySwitchRule extends JRuleUser { - + } ``` @@ -276,14 +273,12 @@ We then extend the rule from the Java Rules file: package org.openhab.automation.jrule.rules.user; import org.openhab.automation.jrule.items.generated._MyTestSwitch; -import org.openhab.automation.jrule.rules.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleEvent; import org.openhab.automation.jrule.rules.JRuleName; -import org.openhab.automation.jrule.rules.JRuleWhen; public class MyTestUserRule extends JRuleUser { - + @JRuleName("TestUserDefinedRule") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_RECEIVED_COMMAND) public void mySendNotificationRUle(JRuleEvent event) { if (timeIsOkforDisturbance()) { logInfo("It's ok to send a disturbing notification"); @@ -296,7 +291,7 @@ public class MyTestUserRule extends JRuleUser { 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") - @JRuleWhen(item = _MyLightSwitch.ITEM, trigger = _MyLightSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyLightSwitch.ITEM, to = JRuleSwitchItem.ON) public synchronized void myTimerRule(JRuleEvent event) { logInfo("Turning on light it will be turned off in 2 mins"); createOrReplaceTimer(_MyLightSwitch.ITEM, 2 * 60, (Void) -> { // Lambda Expression @@ -314,7 +309,7 @@ to send multiple ON statements to be sure it actually turns on. ```java @JRuleName("repeatRuleExample") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public synchronized void repeatRuleExample(JRuleEvent event) { createOrReplaceRepeatingTimer("myRepeatingTimer", 7, 10, (Void) -> { // Lambda Expression final String messageOn = "repeatRuleExample Repeating....."; @@ -330,7 +325,7 @@ Use case Create a simple timer. When MyTestSwitch turns on it will wait 10 secon it will not reschedule the timer, if the timer is already running it won't reschedule it. ```java @JRuleName("timerRuleExample") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public synchronized void timerRuleExample(JRuleEvent event) { createTimer("myTimer", 10, (Void) -> { // Lambda Expression final String messageOn = "timer example."; @@ -343,13 +338,13 @@ it will not reschedule the timer, if the timer is already running it won't resch Use case trigger a rule at 22:30 in the evening to set initial brightness for a ZwaveDimmer to 30% ```java - @JRuleName("setDayBrightness") - @JRuleWhen(hours=22, minutes=30) - public synchronized void setDayBrightness(JRuleEvent event) { - logInfo("Setting night brightness to 30%"); - int dimLevel = 30; - JRuleItems.ZwaveDimmerBrightness.sendCommand(dimLevel); - } + @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 @@ -362,12 +357,12 @@ gt = greater than gte = greater than or equals eq = equals ```java - @JRuleName("turnOnFanIfTemperatureIsLow") - @JRuleWhen(item = _MyTemperatureSensor.ITEM, trigger = _MyTemperatureSensor.TRIGGER_RECEIVED_UPDATE, lte = 20) - public synchronized void turnOnFanIfTemperatureIsLow(JRuleEvent event) { - logInfo("Starting fan since temeprature dropped below 20"); - JRuleItems.MyHeatinFanSwitch.sendCommand(ON); - } + @JRuleName("turnOnFanIfTemperatureIsLow") + @JRuleWhenItemChange(item = _MyTemperatureSensor.ITEM, 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 @@ -375,7 +370,7 @@ eq = equals Use case: Using say command for tts ```java @JRuleName("testSystemTts") - @JRuleWhen(item = _TestSystemTts.ITEM, trigger = _TestSystemTts.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _TestSystemTts.ITEM, to = JRuleSwitchItem.ON) public synchronized void testSystemTts(JRuleEvent event) { logInfo("System TTS Test"); String message = "Testing tts! I hope you can hear it!"; @@ -388,11 +383,11 @@ Use case: Using say command for tts Use case: Executing command from CLI ```java - @JRuleName("testExecutingCommandLine") - @JRuleWhen(item = _gMySwitchGroup.ITEM, trigger = _gMySwitchGroup.TRIGGER_CHANGED) + @JRuleName("TestExecutingCommandLine") + @JRuleWhenItemReceivedCommand(item = _MySwitchGroup.ITEM) public synchronized void testExecutingCommandLine(JRuleEvent event) { logInfo("Creating dummy file using CLI"); - executeCommandLine("touch ~/example.txt"); + executeCommandLine("touch", "/openhab/userdata/example.txt"); } ``` ## Example 15 @@ -400,10 +395,10 @@ Use case: Executing command from CLI Use case: A group of switches, see if status is changed, and also which member in the group changed state ```java @JRuleName("groupMySwitchesChanged") - @JRuleWhen(item = _gMySwitchGroup.ITEM, trigger = _gMySwitchGroup.TRIGGER_CHANGED) + @JRuleWhenItemChange(item = _MySwitchGroup.ITEM) public synchronized void groupMySwitchGroupChanged(JRuleEvent event) { - final boolean groupIsOnline = event.getState().getValueAsOnOffValue() == ON; - final String memberThatChangedStatus = event.getMemberName(); + 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); } ``` @@ -413,7 +408,7 @@ Use case: A group of switches, see if status is changed, and also which member i Use case: A group of switches , trigger when it's changed from OFF to ON ```java @JRuleName("groupMySwitchesChangedOffToOn") - @JRuleWhen(item = _gMySwitchGroup.ITEM, trigger = _gMySwitchGroup.TRIGGER_CHANGED, from="OFF", to="ON") + @JRuleWhenItemChange(item = _MySwitchGroup.ITEM, from = JRuleSwitchItem.OFF, to = JRuleSwitchItem.ON) public synchronized void groupMySwitchesChangedOffToOn(JRuleEvent event) { logInfo("Member that changed the status of the Group from OFF to ON: {}", event.getMemberName()); } @@ -423,10 +418,10 @@ Use case: A group of switches , trigger when it's changed from OFF to ON Use case: Listen for a Channel Trigger Event ```java - @JRuleName("channelTriggered") - @JRuleWhen(channel = "binding:thing:buttonevent") + @JRuleName("ChannelTriggered") + @JRuleWhenChannelTrigger(channel = "mqtt:topic:mqtt:generic:numberTrigger") public synchronized void channelTriggered(JRuleEvent event) { - logInfo("Channel triggered with value: {}", event.getState().getValue()); + logInfo("Channel triggered with value: {}", ((JRuleChannelEvent) event).getEvent()); } ``` @@ -435,9 +430,9 @@ Use case: Listen for a Channel Trigger Event Use case: Cron based expression to trigger rule ```java @JRuleName("testCron") - @JRuleWhen(cron = "4 * * * * *") + @JRuleWhenCronTrigger(cron = "*/5 * * * * *") public void testCron(JRuleEvent event) { - logInfo("CRON: Running cron from string every minute when seconds is at 4: {}", event.getState().getValue()); + logInfo("CRON: Running cron from string every 5 seconds: {}", event); } ``` @@ -448,7 +443,7 @@ Note that `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM). can be called without serviceId argument: `ZonedDateTime lastUpdate = JRuleStringItem.forName(_MyCoolItem.ITEM).getLastUpdated();` ```java @JRuleName("testLastUpdate") -@JRuleWhen(cron = "4 * * * * *") +@JRuleWhenCronTrigger(cron = "4 * * * * *") public void testLastUpdate(JRuleEvent event){ logInfo("CRON: Running cron from string: {}",event.getState().getValue()); ZonedDateTime lastUpdate = JRuleItems.MyCoolItem.getLastUpdated("mapdb"); @@ -464,13 +459,13 @@ Use case: Get the brigtness from a color item, set a color item to white (HSB 0, ```java @JRuleName("testBrightnessFromColorItem") - @JRuleWhen(item = _MyTestColorItem.ITEM, trigger = _MyTestColorItem.TRIGGER_CHANGED) + @JRuleWhenItemChange(item = _MyTestColorItem.ITEM) public void testBrightnessFromColorItem(JRuleEvent event) { JRuleColorValue color = JRuleItems.MyTestColorItem.getState(); int brightness = color.getHsbValue().getBrightness(); } - - @JRuleWhen(item = _MyTestColorItem.ITEM, trigger = _MyTestColorItem.TRIGGER_CHANGED) + + @JRuleWhenItemChange(item = _MyTestColorItem.ITEM) public void testSetWhiteOnColorItem(JRuleEvent event) { JRuleItems.MyTestColorItem.sendCommand(JRuleColorValue.fromHsb(0,0,100)); } @@ -481,7 +476,7 @@ Use case: Set logging name for a specific rule ```java @JRuleName("MyCustomLoggingRule") @JRuleLogName("MYLOG") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public void execChangedToRule() { logInfo("||||| --> Executing rule MyRule: changed to on"); JRuleItems.MySwitch2.sendCommand(ON); @@ -494,7 +489,7 @@ Use case: Override logging for all rules defined in one file public class ColorRules extends JRule { @JRuleName("MyCustomLoggingRuleOnClass") - @JRuleWhen(item = _MyTestSwitch.ITEM, trigger = _MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public void execChangedToRule() { logInfo("||||| --> Executing rule MyRule: changed to on"); JRuleItems.MySwitch2.sendCommand(ON); @@ -515,7 +510,7 @@ Use case: Apply transformation using openHAB transformation service public class TransformationRule extends JRule { @JRuleName("MyTransformation") - @JRuleWhen(item = _MyStringValue.ITEM, trigger = _MyStringValue.TRIGGER_RECEIVED_COMMAND) + @JRuleWhenItemReceivedCommand(item = _MyStringValue.ITEM) public void applyTransformation(JRuleEvent event) { String transformedValue = transform("MAP(my.map):%s", event.getState().getValue()); logInfo("Transformed {} to {}", event.getState().getValue(), transformedValue); @@ -531,17 +526,14 @@ if it is ok for disturbance. If it is ok the switch is set to ON and we can send message is updated. ```java - public class PreConditionTestDisturbance extends JRule { - - @JRulePrecondition(item=_MyTestDisturbanceSwitch.ITEM, eq = "ON") + @JRulePrecondition(item = _MyTestDisturbanceSwitch.ITEM, condition = @JRuleCondition(eq = "ON")) @JRuleName("MyTestPreConditionRule1") - @JRuleWhen(item = _MyMessageNotification.ITEM, trigger = _MyMessageNotification.TRIGGER_RECEIVED_COMMAND) + @JRuleWhenItemReceivedCommand(item = _MyMessageNotification.ITEM) public void testPrecondition(JRuleEvent event) { - String notificationMessage = event.getState().getValue(); + String notificationMessage = ((JRuleItemEvent) event).getState().getValue(); logInfo("It is ok to send notification: {}", notificationMessage); - JRuleItems.MySendNoticationItemMqtt.sendCommand(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 @@ -552,7 +544,7 @@ a motion detector is triggered we will turn on a fan. @JRulePrecondition(item=_MyTestTemperatureSensor.ITEM, gt = 30) @JRuleName("MyTestPreConditionRuleTemperature") - @JRuleWhen(item = _MyMotionDetector.ITEM, trigger = _MyMotionDectetor.TRIGGER_CHANGED_FROM_OFF_TO_ON) + @JRuleWhenItemChange(item = _MyMotionDetector.ITEM, from = JRuleSwitchItem.OFF, to = JRuleSwitchItem.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); @@ -568,7 +560,7 @@ Use case: Send Quantity type Watt (W) from rule. public class QuantityTypeRule extends JRule { @JRuleName("testQuantityPowerWatt") - @JRuleWhen(item=_MyTestMeterPower.ITEM, trigger=_MyTestMeterPower.TRIGGER_CHANGED) + @JRuleWhenItemChange(item = _MyTestMeterPower.ITEM) public void testQuantityPower(JRuleEvent event) { logInfo("TestQuantity power will send this value as Watt: {}", event.getState().getValue()); JRuleItems.TestPowerQuantityType.sendCommand(event.getState().getValueAsDouble(), "W"); @@ -583,7 +575,7 @@ Use case: Use forName to create and item and send commands and get status public class ForNameExampleRule extends JRule { @JRuleName("testForName") - @JRuleWhen(item=_MyTestSwitch.ITEM, trigger=_MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public void testForName(JRuleEvent event) { JRuleSwitchItem switchItem = JRuleSwitchItem.forName("MyOtherTestSwitch"); switchItem.sendItemCommand(OFF); @@ -604,8 +596,8 @@ triggered the rule. public class TriggerNameExample extends JRule { @JRuleName("triggerNameExample") - @JRuleWhen(item=_MyTestSwitch1.ITEM, trigger=_MyTestSwitch1.TRIGGER_CHANGED_TO_ON) - @JRuleWhen(item=_MyTestSwitch2.ITEM, trigger=_MyTestSwitch2.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch1.ITEM, to = JRuleSwitchItem.ON) + @JRuleWhenItemChange(item = _MyTestSwitch2.ITEM, to = JRuleSwitchItem.ON) public void triggerNameExample(JRuleEvent event) { logInfo("The rule was triggered by the following item: {}", event.getItemName()); logInfo("The rule was Old Value was: {} and new value: {}", event.getOldState().getValue(), event.getState().getValue()); @@ -619,7 +611,7 @@ triggered the rule. Use case: get average value for a Number item last hour ```java @JRuleName("testAverageLastHour") -@JRuleWhen(cron = "4 * * * * *") +@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); @@ -634,7 +626,7 @@ Use case: Use generated JRuleItems.java to get hold of items. For instance get s public class ItemsExampleRule extends JRule { @JRuleName("testItems") - @JRuleWhen(item=_MyTestSwitch.ITEM, trigger=_MyTestSwitch.TRIGGER_CHANGED_TO_ON) + @JRuleWhenItemChange(item = _MyTestSwitch.ITEM, to = JRuleSwitchItem.ON) public void testItems(JRuleEvent event) { JRuleItems.MyOtherTestSwitch.getState(); } @@ -648,7 +640,7 @@ Use case: Restart thing every night due to binding flakyness ```java @JRuleName("Restart thing every night") -@JRuleWhen(hours = 3) +@JRuleWhenTimeTrigger(hours=3) public void restartThing() { JRuleThings.my_flaky_thing.restart(); } @@ -660,7 +652,7 @@ Use case: Detect if a specific thing goes offline, wait for it to come online ag ```java @JRuleName("Notify if thing stays offline") -@JRuleWhen(thing = remoteopenhab_thing.ID, trigger = remoteopenhab_thing.TRIGGER_CHANGED, from = "ONLINE") +@JRuleWhenThingTrigger(thing = remoteopenhab_thing.ID, from = JRuleThingStatus.ONLINE) public void warnIfThingStaysOffline() { createOrReplaceTimer("MY_TIMER", 3 * 60, unused -> { if (JRuleThings.remoteopenhab_thing.getStatus() != JRuleThingStatus.ONLINE) { @@ -677,7 +669,7 @@ Use case: Listen for thing status events on _all_ things ```java @JRuleName("Log every thing that goes offline") -@JRuleWhen(thing = "*", trigger = JRuleThingStatusTrigger.TRIGGER_CHANGED, from = "ONLINE") +@JRuleWhenThingTrigger(from = JRuleThingStatus.ONLINE) public void startTrackingNonOnlineThing(JRuleEvent event) { String offlineThingUID = event.getThing(); // ... @@ -690,7 +682,7 @@ Use case: Thing actions, send message with pushover and other services ```java @JRuleName("PushOverTest") -@JRuleWhen(item = _MyTestSendPushOverButton.ITEM, trigger = __MyTestSendPushOverButton.TRIGGER_CHANGED_TO_ON) +@JRuleWhenItemChange(item = _MyTestSendPushOverButton.ITEM, to = JRuleSwitchItem.ON) public void sendPushover(JRuleEvent event) { logInfo("Sending Test message using pushover via actions"); JRuleAddonActionHandler action = getAction("pushover", "pushover:pushover-account:myaccount"); diff --git a/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java b/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java index dc8f8a7a..1c860719 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java +++ b/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java @@ -22,6 +22,7 @@ import org.openhab.automation.jrule.internal.handler.JRuleHandler; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.ItemRegistry; +import org.openhab.core.scheduler.CronScheduler; import org.openhab.core.thing.ThingManager; import org.openhab.core.thing.ThingRegistry; import org.openhab.core.voice.VoiceManager; @@ -57,21 +58,24 @@ public class JRuleFactory { public JRuleFactory(Map properties, final @Reference JRuleEventSubscriber eventSubscriber, final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry, final @Reference ThingManager thingManager, final @Reference EventPublisher eventPublisher, - final @Reference VoiceManager voiceManager, final ComponentContext componentContext) { + final @Reference VoiceManager voiceManager, final ComponentContext componentContext, + final @Reference CronScheduler cronScheduler) { JRuleConfig config = new JRuleConfig(properties); config.initConfig(); jRuleEngine = JRuleEngine.get(); jRuleEngine.setConfig(config); jRuleEngine.setItemRegistry(itemRegistry); + jRuleEngine.setCronScheduler(cronScheduler); + jRuleEngine.initialize(); + jRuleHandler = new JRuleHandler(config, itemRegistry, thingRegistry, thingManager, eventPublisher, - eventSubscriber, voiceManager, componentContext.getBundleContext()); + eventSubscriber, voiceManager, cronScheduler, componentContext.getBundleContext()); delayedInit.call(this::init); } @Nullable private Boolean init() { JRuleLog.info(logger, LOG_NAME_FACTORY, "Initializing Java Rules Engine v{}", getBundleVersion()); - jRuleEngine.initialize(); jRuleHandler.initialize(); return Boolean.TRUE; } @@ -84,6 +88,5 @@ private String getBundleVersion() { public synchronized void dispose() { delayedInit.cancel(); jRuleHandler.dispose(); - jRuleEngine.dispose(); } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java b/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java index 340ee11e..8208a165 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java +++ b/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java @@ -124,7 +124,7 @@ private String relativePathToFullClassname(String path) { } public void loadClass(ClassLoader classLoader, String className, boolean createInstance) { - Class loadedClass = null; + Class loadedClass; try { loadedClass = classLoader.loadClass(className); } catch (ClassNotFoundException e) { diff --git a/src/main/java/org/openhab/automation/jrule/internal/cron/JRuleCronExpression.java b/src/main/java/org/openhab/automation/jrule/internal/cron/JRuleCronExpression.java deleted file mode 100644 index 7294812c..00000000 --- a/src/main/java/org/openhab/automation/jrule/internal/cron/JRuleCronExpression.java +++ /dev/null @@ -1,556 +0,0 @@ -/** - * Copyright (c) 2010-2022 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.automation.jrule.internal.cron; - -import java.time.DayOfWeek; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.YearMonth; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class modified and copied from https://github.com/frode-carlsen/cron - * - * @author Joseph (Seaside) Hagberg - */ -public class JRuleCronExpression { - - enum CronFieldType { - SECOND(0, 59, null) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getSecond(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - return dateTime.withSecond(value).withNano(0); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - return dateTime.plusMinutes(1).withSecond(0).withNano(0); - } - }, - MINUTE(0, 59, null) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getMinute(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - return dateTime.withMinute(value).withSecond(0).withNano(0); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - return dateTime.plusHours(1).withMinute(0).withSecond(0).withNano(0); - } - }, - HOUR(0, 23, null) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getHour(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - return dateTime.withHour(value).withMinute(0).withSecond(0).withNano(0); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - return dateTime.plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0); - } - }, - DAY_OF_MONTH(1, 31, null) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getDayOfMonth(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - return dateTime.withDayOfMonth(value).withHour(0).withMinute(0).withSecond(0).withNano(0); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - return dateTime.plusMonths(1).withDayOfMonth(0).withHour(0).withMinute(0).withSecond(0).withNano(0); - } - }, - MONTH(1, 12, - Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getMonthValue(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - return dateTime.withMonth(value).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - return dateTime.plusYears(1).withMonth(1).withHour(0).withDayOfMonth(1).withMinute(0).withSecond(0) - .withNano(0); - } - }, - DAY_OF_WEEK(1, 7, Arrays.asList("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN")) { - @Override - int getValue(ZonedDateTime dateTime) { - return dateTime.getDayOfWeek().getValue(); - } - - @Override - ZonedDateTime setValue(ZonedDateTime dateTime, int value) { - throw new UnsupportedOperationException(); - } - - @Override - ZonedDateTime overflow(ZonedDateTime dateTime) { - throw new UnsupportedOperationException(); - } - }; - - final int from, to; - final List names; - - CronFieldType(int from, int to, List names) { - this.from = from; - this.to = to; - this.names = names; - } - - /** - * @param dateTime {@link ZonedDateTime} instance - * @return The field time or date value from {@code dateTime} - */ - abstract int getValue(ZonedDateTime dateTime); - - /** - * @param dateTime Initial {@link ZonedDateTime} instance to use - * @param value to set for this field in {@code dateTime} - * @return {@link ZonedDateTime} with {@code value} set for this field and all smaller fields cleared - */ - abstract ZonedDateTime setValue(ZonedDateTime dateTime, int value); - - /** - * Handle when this field overflows and the next higher field should be incremented - * - * @param dateTime Initial {@link ZonedDateTime} instance to use - * @return {@link ZonedDateTime} with the next greater field incremented and all smaller fields cleared - */ - abstract ZonedDateTime overflow(ZonedDateTime dateTime); - } - - private final String expr; - private final SimpleField secondField; - private final SimpleField minuteField; - private final SimpleField hourField; - private final DayOfWeekField dayOfWeekField; - private final SimpleField monthField; - private final DayOfMonthField dayOfMonthField; - - public JRuleCronExpression(final String expr) { - this(expr, true); - } - - public JRuleCronExpression(final String expr, final boolean withSeconds) { - if (expr == null) { - throw new IllegalArgumentException("expr is null"); //$NON-NLS-1$ - } - - this.expr = expr; - - final int expectedParts = withSeconds ? 6 : 5; - final String[] parts = expr.split("\\s+"); //$NON-NLS-1$ - if (parts.length != expectedParts) { - throw new IllegalArgumentException(String.format("Invalid cron expression [%s], expected %s field, got %s", - expr, expectedParts, parts.length)); - } - - int ix = withSeconds ? 1 : 0; - this.secondField = new SimpleField(CronFieldType.SECOND, withSeconds ? parts[0] : "0"); - this.minuteField = new SimpleField(CronFieldType.MINUTE, parts[ix++]); - this.hourField = new SimpleField(CronFieldType.HOUR, parts[ix++]); - this.dayOfMonthField = new DayOfMonthField(parts[ix++]); - this.monthField = new SimpleField(CronFieldType.MONTH, parts[ix++]); - this.dayOfWeekField = new DayOfWeekField(parts[ix++]); - } - - public static JRuleCronExpression create(final String expr) { - return new JRuleCronExpression(expr, true); - } - - public static JRuleCronExpression createWithoutSeconds(final String expr) { - return new JRuleCronExpression(expr, false); - } - - public ZonedDateTime nextTimeAfter(ZonedDateTime afterTime) { - // will search for the next time within the next 4 years. If there is no - // time matching, an InvalidArgumentException will be thrown (it is very - // likely that the cron expression is invalid, like the February 30th). - return nextTimeAfter(afterTime, afterTime.plusYears(4)); - } - - public LocalDateTime nextLocalDateTimeAfter(LocalDateTime dateTime) { - return nextTimeAfter(ZonedDateTime.of(dateTime, ZoneId.systemDefault())).toLocalDateTime(); - } - - public ZonedDateTime nextTimeAfter(ZonedDateTime afterTime, long durationInMillis) { - // will search for the next time within the next durationInMillis - // millisecond. Be aware that the duration is specified in millis, - // but in fact the limit is checked on a day-to-day basis. - return nextTimeAfter(afterTime, afterTime.plus(Duration.ofMillis(durationInMillis))); - } - - public ZonedDateTime nextTimeAfter(ZonedDateTime afterTime, ZonedDateTime dateTimeBarrier) { - ZonedDateTime[] nextDateTime = { afterTime.plusSeconds(1).withNano(0) }; - - while (true) { - checkIfDateTimeBarrierIsReached(nextDateTime[0], dateTimeBarrier); - if (!monthField.nextMatch(nextDateTime)) { - continue; - } - if (!findDay(nextDateTime, dateTimeBarrier)) { - continue; - } - if (!hourField.nextMatch(nextDateTime)) { - continue; - } - if (!minuteField.nextMatch(nextDateTime)) { - continue; - } - if (!secondField.nextMatch(nextDateTime)) { - continue; - } - - checkIfDateTimeBarrierIsReached(nextDateTime[0], dateTimeBarrier); - return nextDateTime[0]; - } - } - - /** - * Find the next match for the day field. - *

- * This is handled different than all other fields because there are two ways to describe the day and it is easier - * to handle them together in the same method. - * - * @param dateTime Initial {@link ZonedDateTime} instance to start from - * @param dateTimeBarrier At which point stop searching for next execution time - * @return {@code true} if a match was found for this field or {@code false} if the field overflowed - * @see {@link SimpleField#nextMatch(ZonedDateTime[])} - */ - private boolean findDay(ZonedDateTime[] dateTime, ZonedDateTime dateTimeBarrier) { - int month = dateTime[0].getMonthValue(); - - while (!(dayOfMonthField.matches(dateTime[0].toLocalDate()) - && dayOfWeekField.matches(dateTime[0].toLocalDate()))) { - dateTime[0] = dateTime[0].plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0); - if (dateTime[0].getMonthValue() != month) { - return false; - } - } - return true; - } - - private static void checkIfDateTimeBarrierIsReached(ZonedDateTime nextTime, ZonedDateTime dateTimeBarrier) { - if (nextTime.isAfter(dateTimeBarrier)) { - throw new IllegalArgumentException( - "No next execution time could be determined that is before the limit of " + dateTimeBarrier); - } - } - - @Override - public String toString() { - return getClass().getSimpleName() + "<" + expr + ">"; - } - - static class FieldPart implements Comparable { - private int from = -1, to = -1, increment = -1; - private String modifier, incrementModifier; - - @Override - public int compareTo(FieldPart o) { - return Integer.compare(from, o.from); - } - } - - abstract static class BasicField { - private static final Pattern CRON_FIELD_REGEXP = Pattern.compile( - "(?: # start of group 1\n" - + " (?:(?\\*)|(?\\?)|(?L)) # global flag (L, ?, *)\n" - + " | (?[0-9]{1,2}|[a-z]{3,3}) # or start number or symbol\n" - + " (?: # start of group 2\n" - + " (?L|W) # modifier (L,W)\n" - + " | -(?[0-9]{1,2}|[a-z]{3,3}) # or end nummer or symbol (in range)\n" - + " )? # end of group 2\n" - + ") # end of group 1\n" - + "(?:(?/|\\#)(?[0-9]{1,7}))? # increment and increment modifier (/ or \\#)\n", - Pattern.CASE_INSENSITIVE | Pattern.COMMENTS); - - final CronFieldType fieldType; - final List parts = new ArrayList<>(); - - private BasicField(CronFieldType fieldType, String fieldExpr) { - this.fieldType = fieldType; - parse(fieldExpr); - } - - private void parse(String fieldExpr) { // NOSONAR - String[] rangeParts = fieldExpr.split(","); - for (String rangePart : rangeParts) { - Matcher m = CRON_FIELD_REGEXP.matcher(rangePart); - if (!m.matches()) { - throw new IllegalArgumentException( - "Invalid cron field '" + rangePart + "' for field [" + fieldType + "]"); - } - String startNummer = m.group("start"); - String modifier = m.group("mod"); - String sluttNummer = m.group("end"); - String incrementModifier = m.group("incmod"); - String increment = m.group("inc"); - - FieldPart part = new FieldPart(); - part.increment = 999; - if (startNummer != null) { - part.from = mapValue(startNummer); - part.modifier = modifier; - if (sluttNummer != null) { - part.to = mapValue(sluttNummer); - part.increment = 1; - } else if (increment != null) { - part.to = fieldType.to; - } else { - part.to = part.from; - } - } else if (m.group("all") != null) { - part.from = fieldType.from; - part.to = fieldType.to; - part.increment = 1; - } else if (m.group("ignore") != null) { - part.modifier = m.group("ignore"); - } else if (m.group("last") != null) { - part.modifier = m.group("last"); - } else { - throw new IllegalArgumentException("Invalid cron part: " + rangePart); - } - - if (increment != null) { - part.incrementModifier = incrementModifier; - part.increment = Integer.parseInt(increment); - } - - validateRange(part); - validatePart(part); - parts.add(part); - } - - Collections.sort(parts); - } - - protected void validatePart(FieldPart part) { - if (part.modifier != null) { - throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier)); - } else if (part.incrementModifier != null && !"/".equals(part.incrementModifier)) { - throw new IllegalArgumentException( - String.format("Invalid increment modifier [%s]", part.incrementModifier)); - } - } - - private void validateRange(FieldPart part) { - if ((part.from != -1 && part.from < fieldType.from) || (part.to != -1 && part.to > fieldType.to)) { - throw new IllegalArgumentException(String.format("Invalid interval [%s-%s], must be %s<=_<=%s", - part.from, part.to, fieldType.from, fieldType.to)); - } else if (part.from != -1 && part.to != -1 && part.from > part.to) { - throw new IllegalArgumentException(String.format( - "Invalid interval [%s-%s]. Rolling periods are not supported (ex. 5-1, only 1-5) since this won't give a deterministic result. Must be %s<=_<=%s", - part.from, part.to, fieldType.from, fieldType.to)); - } - } - - protected int mapValue(String value) { - int idx; - if (fieldType.names != null - && (idx = fieldType.names.indexOf(value.toUpperCase(Locale.getDefault()))) >= 0) { - return idx + fieldType.from; - } - return Integer.parseInt(value); - } - - protected boolean matches(int val, FieldPart part) { - return val >= part.from && val <= part.to && (val - part.from) % part.increment == 0; - } - - protected int nextMatch(int val, FieldPart part) { - if (val > part.to) { - return -1; - } - int nextPotential = Math.max(val, part.from); - if (part.increment == 1 || nextPotential == part.from) { - return nextPotential; - } - - int remainder = ((nextPotential - part.from) % part.increment); - if (remainder != 0) { - nextPotential += part.increment - remainder; - } - - return nextPotential <= part.to ? nextPotential : -1; - } - } - - static class SimpleField extends BasicField { - SimpleField(CronFieldType fieldType, String fieldExpr) { - super(fieldType, fieldExpr); - } - - public boolean matches(int val) { - if (val >= fieldType.from && val <= fieldType.to) { - for (FieldPart part : parts) { - if (matches(val, part)) { - return true; - } - } - } - return false; - } - - /** - * Find the next match for this field. If a match cannot be found force an overflow and increase the next - * greatest field. - * - * @param dateTime {@link ZonedDateTime} array so the reference can be modified - * @return {@code true} if a match was found for this field or {@code false} if the field overflowed - */ - protected boolean nextMatch(ZonedDateTime[] dateTime) { - int value = fieldType.getValue(dateTime[0]); - - for (FieldPart part : parts) { - int nextMatch = nextMatch(value, part); - if (nextMatch > -1) { - if (nextMatch != value) { - dateTime[0] = fieldType.setValue(dateTime[0], nextMatch); - } - return true; - } - } - - dateTime[0] = fieldType.overflow(dateTime[0]); - return false; - } - } - - static class DayOfWeekField extends BasicField { - - DayOfWeekField(String fieldExpr) { - super(CronFieldType.DAY_OF_WEEK, fieldExpr); - } - - boolean matches(LocalDate dato) { - for (FieldPart part : parts) { - if ("L".equals(part.modifier)) { - YearMonth ym = YearMonth.of(dato.getYear(), dato.getMonth().getValue()); - return dato.getDayOfWeek() == DayOfWeek.of(part.from) - && dato.getDayOfMonth() > (ym.lengthOfMonth() - 7); - } else if ("#".equals(part.incrementModifier)) { - if (dato.getDayOfWeek() == DayOfWeek.of(part.from)) { - int num = dato.getDayOfMonth() / 7; - return part.increment == (dato.getDayOfMonth() % 7 == 0 ? num : num + 1); - } - return false; - } else if (matches(dato.getDayOfWeek().getValue(), part)) { - return true; - } - } - return false; - } - - @Override - protected int mapValue(String value) { - // Use 1-7 for weedays, but 0 will also represent sunday (linux practice) - return "0".equals(value) ? 7 : super.mapValue(value); - } - - @Override - protected boolean matches(int val, FieldPart part) { - return "?".equals(part.modifier) || super.matches(val, part); - } - - @Override - protected void validatePart(FieldPart part) { - if (part.modifier != null && Arrays.asList("L", "?").indexOf(part.modifier) == -1) { - throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier)); - } else if (part.incrementModifier != null - && Arrays.asList("/", "#").indexOf(part.incrementModifier) == -1) { - throw new IllegalArgumentException( - String.format("Invalid increment modifier [%s]", part.incrementModifier)); - } - } - } - - static class DayOfMonthField extends BasicField { - DayOfMonthField(String fieldExpr) { - super(CronFieldType.DAY_OF_MONTH, fieldExpr); - } - - boolean matches(LocalDate dato) { - for (FieldPart part : parts) { - if ("L".equals(part.modifier)) { - YearMonth ym = YearMonth.of(dato.getYear(), dato.getMonth().getValue()); - return dato.getDayOfMonth() == (ym.lengthOfMonth() - (part.from == -1 ? 0 : part.from)); - } else if ("W".equals(part.modifier)) { - if (dato.getDayOfWeek().getValue() <= 5) { - if (dato.getDayOfMonth() == part.from) { - return true; - } else if (dato.getDayOfWeek().getValue() == 5) { - return dato.plusDays(1).getDayOfMonth() == part.from; - } else if (dato.getDayOfWeek().getValue() == 1) { - return dato.minusDays(1).getDayOfMonth() == part.from; - } - } - } else if (matches(dato.getDayOfMonth(), part)) { - return true; - } - } - return false; - } - - @Override - protected void validatePart(FieldPart part) { - if (part.modifier != null && Arrays.asList("L", "W", "?").indexOf(part.modifier) == -1) { - throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier)); - } else if (part.incrementModifier != null && !"/".equals(part.incrementModifier)) { - throw new IllegalArgumentException( - String.format("Invalid increment modifier [%s]", part.incrementModifier)); - } - } - - @Override - protected boolean matches(int val, FieldPart part) { - return "?".equals(part.modifier) || super.matches(val, part); - } - } -} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java index 6601e77a..aff0c390 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/JRuleEngine.java @@ -14,120 +14,82 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; +import java.util.Optional; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; +import org.openhab.automation.jrule.exception.JRuleItemNotFoundException; import org.openhab.automation.jrule.internal.JRuleConfig; -import org.openhab.automation.jrule.internal.JRuleConstants; import org.openhab.automation.jrule.internal.JRuleLog; -import org.openhab.automation.jrule.internal.JRuleUtil; -import org.openhab.automation.jrule.internal.cron.JRuleCronExpression; import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleChannelExecutionContext; -import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleContextValueComparators; import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleItemChangeExecutionContext; import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleItemExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleItemReceivedCommandExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleItemReceivedUpdateExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRulePreconditionContext; import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleThingExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleTimeTimerExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleTimedCronExecutionContext; import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleTimedExecutionContext; +import org.openhab.automation.jrule.internal.engine.timer.TimerExecutor; import org.openhab.automation.jrule.internal.events.JRuleEventSubscriber; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRuleEvent; +import org.openhab.automation.jrule.rules.JRuleCondition; import org.openhab.automation.jrule.rules.JRuleLogName; import org.openhab.automation.jrule.rules.JRuleName; import org.openhab.automation.jrule.rules.JRulePrecondition; import org.openhab.automation.jrule.rules.JRuleTag; -import org.openhab.automation.jrule.rules.JRuleWhen; -import org.openhab.core.common.ThreadPoolManager; -import org.openhab.core.events.Event; +import org.openhab.automation.jrule.rules.JRuleWhenChannelTrigger; +import org.openhab.automation.jrule.rules.JRuleWhenCronTrigger; +import org.openhab.automation.jrule.rules.JRuleWhenItemChange; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedCommand; +import org.openhab.automation.jrule.rules.JRuleWhenItemReceivedUpdate; +import org.openhab.automation.jrule.rules.JRuleWhenThingTrigger; +import org.openhab.automation.jrule.rules.JRuleWhenTimeTrigger; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.things.JRuleThingStatus; +import org.openhab.core.events.AbstractEvent; import org.openhab.core.items.Item; import org.openhab.core.items.ItemNotFoundException; import org.openhab.core.items.ItemRegistry; -import org.openhab.core.items.events.GroupItemStateChangedEvent; -import org.openhab.core.items.events.ItemCommandEvent; -import org.openhab.core.items.events.ItemEvent; -import org.openhab.core.items.events.ItemStateChangedEvent; -import org.openhab.core.items.events.ItemStateEvent; import org.openhab.core.library.types.QuantityType; -import org.openhab.core.thing.events.ChannelTriggeredEvent; -import org.openhab.core.thing.events.ThingStatusInfoChangedEvent; +import org.openhab.core.scheduler.CronScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; /** - * The {@link JRuleConstants} class defines common constants, which are - * used across the Java Rule automation. + * The {@link JRuleEngine} * - * @author Joseph (Seaside) Hagberg - Initial contribution + * @author Robert Delbrück */ public class JRuleEngine implements PropertyChangeListener { - - private static final String MDC_KEY_RULE = "rule"; - private static final int AWAIT_TERMINATION_THREAD_SECONDS = 2; - private static final String RECEIVED_COMMAND = "received command"; - private static final String RECEIVED_COMMAND_APPEND = RECEIVED_COMMAND + " "; - - private static final String CHANGED_FROM_TO_PATTERN = "Changed from %1$s to %2$s"; - private static final String CHANGED_FROM = "Changed from "; - private static final String CHANGED_TO = "Changed to "; - private static final String CHANGED = "Changed"; - - private static final String RECEIVED_UPDATE = "received update"; - private static final String RECEIVED_UPDATE_APPEND = RECEIVED_UPDATE + " "; - - private static final String LOG_NAME_ENGINE = "JRuleEngine"; private static final String[] EMPTY_LOG_TAGS = new String[0]; - - private static volatile JRuleEngine instance; - - private ThreadPoolExecutor ruleExecutorService; - - private JRuleConfig config; - - private final Map> itemToRules = new HashMap<>(); - - private final Map> itemToExecutionContexts = new HashMap<>(); - - private final Map> channelToExecutionContexts = new HashMap<>(); - private final Map> thingToExecutionContexts = new HashMap<>(); - - private final Set itemNames = new HashSet<>(); + private static final int AWAIT_TERMINATION_THREAD_SECONDS = 2; + private List contextList = new ArrayList<>(); + private TimerExecutor timerExecutor = new TimerExecutor(this); + private static final String MDC_KEY_RULE = "rule"; + protected ThreadPoolExecutor ruleExecutorService; + protected JRuleConfig config; private final Logger logger = LoggerFactory.getLogger(JRuleEngine.class); - private final Set> timers = new HashSet<>(); - protected final ScheduledExecutorService scheduler = ThreadPoolManager - .getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON); - private ItemRegistry itemRegistry; - - private JRuleLoadingStatistics ruleLoadingStatistics; - - private JRuleEngine() { - ruleLoadingStatistics = new JRuleLoadingStatistics(null); - } + protected ItemRegistry itemRegistry; + protected JRuleLoadingStatistics ruleLoadingStatistics; + private static volatile JRuleEngine instance; public static JRuleEngine get() { if (instance == null) { @@ -140,567 +102,284 @@ public static JRuleEngine get() { return instance; } - public Set getItemNames() { - return itemNames; - } - - public Set getChannelNames() { - return channelToExecutionContexts.keySet(); - } - - public Set getThingUIDs() { - return thingToExecutionContexts.keySet(); - } - - public synchronized void reset() { - itemNames.clear(); - itemToExecutionContexts.clear(); - channelToExecutionContexts.clear(); - itemToRules.clear(); - thingToExecutionContexts.clear(); - clearTimers(); - - ruleLoadingStatistics = new JRuleLoadingStatistics(ruleLoadingStatistics); - } - - private synchronized void clearTimers() { - timers.forEach(timer -> timer.cancel(true)); - timers.clear(); - } - - private void logInfo(String message, Object... parameters) { - JRuleLog.info(logger, LOG_NAME_ENGINE, message, parameters); - } - - private void logDebug(String message, Object... parameters) { - JRuleLog.debug(logger, LOG_NAME_ENGINE, message, parameters); - } - - private void logWarn(String message, Object... parameters) { - JRuleLog.warn(logger, LOG_NAME_ENGINE, message, parameters); - } - - private void logError(String message, Object... parameters) { - JRuleLog.error(logger, LOG_NAME_ENGINE, message, parameters); + private JRuleEngine() { + this.ruleLoadingStatistics = new JRuleLoadingStatistics(null); } public void add(JRule jRule) { logDebug("Adding rule: {}", jRule); - Class clazz = jRule.getClass(); - for (Method method : clazz.getDeclaredMethods()) { - logDebug("Adding rule method: {}", method.getName()); - if (!method.isAnnotationPresent(JRuleName.class)) { - logDebug("Rule method ignored since JRuleName annotation is missing: {}", method.getName()); - continue; - } - // Check if method is public, else execution will fail at runtime - boolean isPublic = (method.getModifiers() & Modifier.PUBLIC) != 0; - if (!isPublic) { - logWarn("Rule method ignored since method isn't public: {}", method.getName()); - continue; - } - - if (method.getAnnotationsByType(JRuleWhen.class).length == 0) { - logWarn("Rule ignored since JWhens annotation is missing"); - logWarn("Rule JWhen present: {}", method.isAnnotationPresent(JRuleWhen.class)); - final JRuleWhen[] jRuleWhens = method.getAnnotationsByType(JRuleWhen.class); - logDebug("Got jrule whens size: {}", jRuleWhens.length); - continue; - } - final JRuleName jRuleName = method.getDeclaredAnnotation(JRuleName.class); - final JRuleLogName jRuleLogName = method.isAnnotationPresent(JRuleLogName.class) - ? method.getDeclaredAnnotation(JRuleLogName.class) - : null; - - final JRulePrecondition[] preconditions = method.getAnnotationsByType(JRulePrecondition.class); - - final JRuleWhen[] jRuleWhens = method.getAnnotationsByType(JRuleWhen.class); - logDebug("Got jrule whens size: {}", jRuleWhens.length); - final Parameter[] parameters = method.getParameters(); - boolean jRuleEventPresent = Arrays.stream(parameters) - .anyMatch(param -> (param.getType().equals(JRuleEvent.class))); - - String logName = (jRuleLogName != null && !jRuleLogName.value().isEmpty()) ? jRuleLogName.value() - : jRuleName.value(); - - final JRuleTag jRuleTags = method.isAnnotationPresent(JRuleTag.class) - ? method.getDeclaredAnnotation(JRuleTag.class) - : null; - final String[] loggingTags = jRuleTags != null ? jRuleTags.value() : EMPTY_LOG_TAGS; - // TODO: Do validation on syntax in when annotations - // Loop for other ORs - for (JRuleWhen jRuleWhen : jRuleWhens) { - JRuleLog.debug(logger, logName, "Processing jRule when: {}", jRuleWhen); - if (!jRuleWhen.item().isEmpty()) { - // JRuleWhen for an item - - String itemPackage = config.getGeneratedItemPackage(); - String prefix = config.getGeneratedItemPrefix(); - String itemClass = String.format("%s.%s%s", itemPackage, prefix, jRuleWhen.item()); - - JRuleLog.debug(logger, logName, "Got item class: {}", itemClass); - JRuleLog.info(logger, logName, "Validating JRule item: {} trigger: {} ", jRuleWhen.item(), - jRuleWhen.trigger()); - addItemExecutionContext(jRule, logName, loggingTags, itemClass, jRuleName.value(), - jRuleWhen.trigger(), jRuleWhen.from(), jRuleWhen.to(), jRuleWhen.update(), jRuleWhen.item(), - method, jRuleEventPresent, getDoubleFromAnnotation(jRuleWhen.lt()), - getDoubleFromAnnotation(jRuleWhen.lte()), getDoubleFromAnnotation(jRuleWhen.gt()), - getDoubleFromAnnotation(jRuleWhen.gte()), getStringFromAnnotation(jRuleWhen.eq()), - getStringFromAnnotation(jRuleWhen.neq()), preconditions); - itemNames.add(jRuleWhen.item()); - - ruleLoadingStatistics.addItemStateTrigger(); - } else if (jRuleWhen.hours() != -1 || jRuleWhen.minutes() != -1 || jRuleWhen.seconds() != -1 - || !jRuleWhen.cron().isEmpty()) { - // JRuleWhen for a time trigger - JRuleLog.info(logger, logName, - "Validating JRule: Scheduling timer for hours: {} minutes: {} seconds: {} cron: {}", - jRuleWhen.hours(), jRuleWhen.minutes(), jRuleWhen.seconds(), jRuleWhen.cron()); - addTimedExecution(jRule, logName, loggingTags, jRuleName.value(), jRuleWhen, method, - jRuleEventPresent, preconditions); - ruleLoadingStatistics.addTimedTrigger(); - } else if (!jRuleWhen.channel().isEmpty()) { - // JRuleWhen for a channel - JRuleLog.info(logger, logName, "Validating JRule channel: {} trigger: {} ", jRuleWhen.channel(), - jRuleWhen.trigger()); - addChannelExecutionContext(jRule, logName, loggingTags, jRuleWhen.channel(), jRuleName.value(), - method, jRuleEventPresent, getStringFromAnnotation(jRuleWhen.eq()), - getStringFromAnnotation(jRuleWhen.neq()), preconditions); - ruleLoadingStatistics.addChannelTrigger(); - } else if (!jRuleWhen.thing().isEmpty()) { - // JRuleWhen for a thing - JRuleLog.info(logger, logName, "Validating JRule thing: {} trigger: {} ", jRuleWhen.thing(), - jRuleWhen.trigger()); - if (JRuleUtil.isNotEmpty(jRuleWhen.trigger())) { - addThingExecutionContext(jRule, logName, loggingTags, jRuleWhen.thing(), jRuleName.value(), - jRuleWhen.trigger(), jRuleWhen.from(), jRuleWhen.to(), method, jRuleEventPresent, - preconditions); - ruleLoadingStatistics.addThingTrigger(); - } else { - JRuleLog.warn(logger, logName, "Ignoring rule as no trigger is specified"); - } - } - } - if (jRuleWhens.length > 0) { - ruleLoadingStatistics.addRuleMethod(); - } - - } ruleLoadingStatistics.addRuleClass(); - } - - private Double getDoubleFromAnnotation(double d) { - if (d == Double.MIN_VALUE) { - return null; - } - return d; - } - - private String getStringFromAnnotation(String s) { - return s.isEmpty() ? null : s; - } - - private CompletableFuture createTimer(String logName, String cronExpressionStr) { - try { - final JRuleCronExpression cronExpression = new JRuleCronExpression(cronExpressionStr); - final ZonedDateTime nextTimeAfter = cronExpression.nextTimeAfter(ZonedDateTime.now()); - Date futureTime = Date.from(nextTimeAfter.toInstant()); - return createTimer(logName, futureTime); - } catch (IllegalArgumentException x) { - JRuleLog.error(logger, logName, "Failed to parse cron expression for cron: {} message: {}", - cronExpressionStr, x.getMessage()); - return null; - } - } - - private CompletableFuture createTimer(String logName, int hours, int minutes, int seconds) { - Calendar calFuture = Calendar.getInstance(); - Calendar now = Calendar.getInstance(); - calFuture.set(Calendar.HOUR_OF_DAY, hours == -1 ? 0 : hours); - calFuture.set(Calendar.MINUTE, minutes == -1 ? 0 : minutes); - calFuture.set(Calendar.SECOND, seconds == -1 ? 0 : seconds); - calFuture.set(Calendar.MILLISECOND, 0); - calFuture.set(Calendar.HOUR_OF_DAY, hours); - if (calFuture.before(now)) { - if (hours != -1) { - calFuture.add(Calendar.DAY_OF_MONTH, 1); - } else if (minutes != -1) { - calFuture.add(Calendar.HOUR, 1); - } else if (seconds != -1) { - calFuture.add(Calendar.MINUTE, 1); - } - } - return createTimer(logName, calFuture.getTime()); - } - - private CompletableFuture createTimer(String logName, Date date) { - long initialDelay = new Date(date.getTime() - System.currentTimeMillis()).getTime(); - JRuleLog.debug(logger, logName, "Schedule cron: {} initialDelay: {}", date, initialDelay); - Executor delayedExecutor = CompletableFuture.delayedExecutor(initialDelay, TimeUnit.MILLISECONDS, scheduler); - return CompletableFuture.supplyAsync(() -> null, delayedExecutor); - } - - private synchronized void addTimedExecution(JRule jRule, String logName, String[] loggingTags, String jRuleName, - JRuleWhen jRuleWhen, Method method, boolean jRuleEventPresent, JRulePrecondition[] preconditions) { - CompletableFuture future = (!jRuleWhen.cron().isEmpty()) ? createTimer(logName, jRuleWhen.cron()) - : createTimer(logName, jRuleWhen.hours(), jRuleWhen.minutes(), jRuleWhen.seconds()); - if (future != null) { - // If ie cron expression fails to parse, null will be returned - timers.add(future); - JRuleLog.info(logger, logName, "Scheduling timer for rule: {} hours: {} minutes: {} seconds: {} cron: {}", - jRuleWhen.hours(), jRuleWhen.minutes(), jRuleWhen.seconds(), jRuleWhen.cron()); - JRuleTimedExecutionContext executionContext = new JRuleTimedExecutionContext(jRule, logName, loggingTags, - method, jRuleName, jRuleEventPresent, preconditions); - Consumer consumer = t -> { - try { - invokeRule(executionContext, jRuleEventPresent ? new JRuleEvent("") : null); - } finally { - timers.remove(future); - } - }; - future.thenAccept(consumer).thenAccept(s -> { - JRuleLog.info(logger, logName, "Timer has finished"); - addTimedExecution(jRule, logName, loggingTags, jRuleName, jRuleWhen, method, jRuleEventPresent, - preconditions); - }); - } else { - JRuleLog.error(logger, logName, "Failed to add timed execution - check previous log statements"); + for (Method method : jRule.getClass().getDeclaredMethods()) { + this.add(method, jRule); } } - private void addItemExecutionContext(JRule jRule, String logName, String[] loggingTags, String itemClass, - String ruleName, String trigger, String from, String to, String update, String itemName, Method method, - boolean eventParameterPresent, Double lt, Double lte, Double gt, Double gte, String eq, String neq, - JRulePrecondition[] preconditions) { - List contextList = itemToExecutionContexts.computeIfAbsent(itemName, - k -> new ArrayList<>()); - final JRuleItemExecutionContext context = new JRuleItemExecutionContext(jRule, logName, loggingTags, trigger, - from, to, update, ruleName, itemClass, itemName, method, eventParameterPresent, lt, lte, gt, gte, eq, - neq, preconditions); - JRuleLog.debug(logger, logName, "ItemContextList add context: {}", context); - contextList.add(context); - } - - private void addChannelExecutionContext(JRule jRule, String logName, String[] loggingTags, String channel, - String ruleName, Method method, boolean eventParameterPresent, String eq, String neq, - JRulePrecondition[] preconditions) { - List contextList = channelToExecutionContexts.computeIfAbsent(channel, - k -> new ArrayList<>()); - final JRuleChannelExecutionContext context = new JRuleChannelExecutionContext(jRule, logName, loggingTags, - ruleName, method, eventParameterPresent, preconditions, channel, eq, neq); - JRuleLog.debug(logger, logName, "ChannelContextList add context: {}", context); - contextList.add(context); - } - - private void addThingExecutionContext(JRule jRule, String logName, String[] loggingTags, String thing, - String ruleName, String trigger, String from, String to, Method method, boolean eventParameterPresent, - JRulePrecondition[] preconditions) { - List contextList = thingToExecutionContexts.computeIfAbsent(thing, - k -> new ArrayList<>()); - final JRuleThingExecutionContext context = new JRuleThingExecutionContext(jRule, logName, loggingTags, trigger, - from, to, ruleName, thing, method, eventParameterPresent, preconditions); - JRuleLog.debug(logger, logName, "ThingContextList add context: {}", context); - contextList.add(context); - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_ITEM_EVENT)) { - logDebug("Property change item event! : {}", ((Event) evt.getNewValue()).getTopic()); - handleItemEvent((Event) evt.getNewValue()); - } else if (evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_CHANNEL_EVENT)) { - logDebug("Channel event! : {}", ((Event) evt.getNewValue()).getTopic()); - handleChannelEvent((ChannelTriggeredEvent) evt.getNewValue()); - } else if (evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_THING_STATUS_EVENT)) { - logDebug("Thing status event! : {}", ((Event) evt.getNewValue()).getTopic()); - handleThingStatusChangedEvent((ThingStatusInfoChangedEvent) evt.getNewValue()); - } - } + private void add(Method method, JRule jRule) { + logDebug("Adding rule method: {}", method.getName()); - private void handleThingStatusChangedEvent(ThingStatusInfoChangedEvent thingStatusChangedEvent) { - List thingExecutionContexts = thingToExecutionContexts - .get(thingStatusChangedEvent.getThingUID().toString()); - List executionContextsWildcard = thingToExecutionContexts - .get(JRuleThingExecutionContext.ANY_THING_UID); - List executionContexts = new ArrayList<>(); - if (executionContextsWildcard != null) { - executionContexts.addAll(executionContextsWildcard); - } - if (thingExecutionContexts != null) { - executionContexts.addAll(thingExecutionContexts); + if (!method.isAnnotationPresent(JRuleName.class)) { + logWarn("Skipping method {} on class {} since JRuleName annotation is missing", method.getName(), + jRule.getClass().getName()); + return; } - if (executionContexts == null || executionContexts.isEmpty()) { - logDebug("No execution context for thingStatusEvent: {}", thingStatusChangedEvent); + // Check if method is public, else execution will fail at runtime + boolean isPublic = (method.getModifiers() & Modifier.PUBLIC) != 0; + if (!isPublic) { + logWarn("Skipping non-public method {} on class {}", method.getName(), jRule.getClass().getName()); return; } - final Set triggerValues = new HashSet<>(5); - String newStatus = thingStatusChangedEvent.getStatusInfo().getStatus().toString(); - String oldStatus = thingStatusChangedEvent.getOldStatusInfo().getStatus().toString(); - - if (JRuleUtil.isNotEmpty(oldStatus) && JRuleUtil.isNotEmpty(newStatus)) { - triggerValues.add(String.format(CHANGED_FROM_TO_PATTERN, oldStatus, newStatus)); - triggerValues.add(CHANGED_FROM.concat(oldStatus)); - triggerValues.add(CHANGED_TO.concat(newStatus)); - triggerValues.add(CHANGED); - } else if (JRuleUtil.isNotEmpty(oldStatus)) { - triggerValues.add(CHANGED_FROM.concat(oldStatus)); - triggerValues.add(CHANGED); - } else if (JRuleUtil.isNotEmpty(newStatus)) { - triggerValues.add(CHANGED_TO.concat(newStatus)); - triggerValues.add(CHANGED); - } else { - triggerValues.add(CHANGED); + // Check if method is has none or a single parameter + if (method.getParameterCount() > 1) { + logWarn("Skipping method {} on class {}. Rule methods should have none or a single parameter", + method.getName(), jRule.getClass().getName()); + return; } - if (!triggerValues.isEmpty()) { - executionContexts.stream().filter(context -> triggerValues.contains(context.getTriggerFullString())) - .forEach(context -> invokeWhenMatchParameters(context, - new JRuleEvent(newStatus, thingStatusChangedEvent.getThingUID().toString(), newStatus))); - } else { - logDebug("Execution ignored, no trigger values for thing: {}", thingStatusChangedEvent.getThingUID()); + final String logName = Optional.ofNullable(method.getDeclaredAnnotation(JRuleLogName.class)) + .map(JRuleLogName::value).orElse(method.getDeclaredAnnotation(JRuleName.class).value()); + + List jRulePreconditionContexts = Arrays + .stream(method.getAnnotationsByType(JRulePrecondition.class)).map(jRulePrecondition -> { + JRuleCondition jRuleCondition = jRulePrecondition.condition(); + return new JRulePreconditionContext(jRulePrecondition.item(), + Optional.of(jRuleCondition.lt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.lte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.eq()).filter(StringUtils::isNotEmpty), + Optional.of(jRuleCondition.neq()).filter(StringUtils::isNotEmpty)); + }).collect(Collectors.toList()); + + final String[] loggingTags = Optional.ofNullable(method.getDeclaredAnnotation(JRuleTag.class)) + .map(JRuleTag::value).orElse(EMPTY_LOG_TAGS); + + ruleLoadingStatistics.addRuleMethod(); + AtomicBoolean addedToContext = new AtomicBoolean(false); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenItemReceivedUpdate.class)).forEach(jRuleWhen -> { + JRuleCondition jRuleCondition = jRuleWhen.condition(); + addToContext(new JRuleItemReceivedUpdateExecutionContext(jRule, logName, loggingTags, method, + jRuleWhen.item(), Optional.of(jRuleCondition.lt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.lte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.eq()).filter(StringUtils::isNotEmpty), + Optional.of(jRuleCondition.neq()).filter(StringUtils::isNotEmpty), jRulePreconditionContexts, + Optional.of(jRuleWhen.state()).filter(StringUtils::isNotEmpty))); + ruleLoadingStatistics.addItemStateTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenItemReceivedCommand.class)).forEach(jRuleWhen -> { + JRuleCondition jRuleCondition = jRuleWhen.condition(); + addToContext(new JRuleItemReceivedCommandExecutionContext(jRule, logName, loggingTags, method, + jRuleWhen.item(), Optional.of(jRuleCondition.lt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.lte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.eq()).filter(StringUtils::isNotEmpty), + Optional.of(jRuleCondition.neq()).filter(StringUtils::isNotEmpty), jRulePreconditionContexts, + Optional.of(jRuleWhen.command()).filter(StringUtils::isNotEmpty))); + ruleLoadingStatistics.addItemStateTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenItemChange.class)).forEach(jRuleWhen -> { + JRuleCondition jRuleCondition = jRuleWhen.condition(); + addToContext(new JRuleItemChangeExecutionContext(jRule, logName, loggingTags, method, jRuleWhen.item(), + Optional.of(jRuleCondition.lt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.lte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gt()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.gte()).filter(aDouble -> aDouble != Double.MIN_VALUE), + Optional.of(jRuleCondition.eq()).filter(StringUtils::isNotEmpty), + Optional.of(jRuleCondition.neq()).filter(StringUtils::isNotEmpty), jRulePreconditionContexts, + Optional.of(jRuleWhen.from()).filter(StringUtils::isNotEmpty), + Optional.of(jRuleWhen.to()).filter(StringUtils::isNotEmpty))); + ruleLoadingStatistics.addItemStateTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenChannelTrigger.class)).forEach(jRuleWhen -> { + addToContext( + new JRuleChannelExecutionContext(jRule, logName, loggingTags, method, jRulePreconditionContexts, + jRuleWhen.channel(), Optional.of(jRuleWhen.event()).filter(StringUtils::isNotEmpty))); + ruleLoadingStatistics.addChannelTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenCronTrigger.class)).forEach(jRuleWhen -> { + addToContext(new JRuleTimedCronExecutionContext(jRule, logName, loggingTags, method, + jRulePreconditionContexts, jRuleWhen.cron())); + ruleLoadingStatistics.addTimedTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenTimeTrigger.class)).forEach(jRuleWhen -> { + addToContext(new JRuleTimeTimerExecutionContext(jRule, logName, loggingTags, method, + jRulePreconditionContexts, Optional.of(jRuleWhen.hours()).filter(i -> i != -1), + Optional.of(jRuleWhen.minutes()).filter(i -> i != -1), + Optional.of(jRuleWhen.seconds()).filter(i -> i != -1))); + ruleLoadingStatistics.addTimedTrigger(); + addedToContext.set(true); + }); + + Arrays.stream(method.getAnnotationsByType(JRuleWhenThingTrigger.class)).forEach(jRuleWhen -> { + ruleLoadingStatistics.addThingTrigger(); + addToContext(new JRuleThingExecutionContext(jRule, logName, loggingTags, method, + Optional.of(jRuleWhen.thing()).filter(StringUtils::isNotEmpty).filter(s -> !s.equals("*")), + Optional.of(jRuleWhen.from()).filter(s -> s != JRuleThingStatus.THING_UNKNOWN), + Optional.of(jRuleWhen.to()).filter(s -> s != JRuleThingStatus.THING_UNKNOWN), + jRulePreconditionContexts)); + addedToContext.set(true); + }); + + // Check if any rule triggers are present + if (!addedToContext.get()) { + logWarn("Skipping rule method {} on class {} with no JRuleWhenXXX annotation triggers", method.getName(), + jRule.getClass().getName()); } } - private void handleChannelEvent(ChannelTriggeredEvent channelEvent) { - List executionContexts = channelToExecutionContexts - .get(channelEvent.getChannel().toString()); - if (executionContexts == null || executionContexts.isEmpty()) { - logDebug("No execution context for channelEvent: {}", channelEvent); - return; + private boolean addToContext(JRuleExecutionContext context) { + logDebug("add to context: {}", context); + if (context instanceof JRuleTimedExecutionContext) { + timerExecutor.add(context); + } else { + contextList.add(context); } - executionContexts.stream().filter(context -> context.getChannel().equals(channelEvent.getChannel().toString())) - .filter(context -> matchesChannelEvent(context, channelEvent)).forEach(context -> { - JRuleLog.debug(logger, context.getLogName(), "invoke when context matches"); - invokeRule(context, new JRuleEvent(channelEvent.getEvent(), channelEvent.getChannel().toString(), - channelEvent.getEvent())); - }); + return true; } - private boolean matchesChannelEvent(JRuleChannelExecutionContext context, ChannelTriggeredEvent event) { - return (context.getEq() != null && context.getEq().equals(event.getEvent())) - || (context.getNeq() != null && !context.getNeq().equals(event.getEvent())) - || context.getEq() == null && context.getNeq() == null; + public void fire(AbstractEvent event) { + contextList.stream().filter(context -> context.match(event)).filter(this::matchPrecondition) + .forEach(context -> invokeRule(context, context.createJRuleEvent(event))); } - private void handleItemEvent(Event event) { - final String itemName = getItemNameFromEvent(event); - final List executionContexts = itemToExecutionContexts.get(itemName); - if (executionContexts == null || executionContexts.isEmpty()) { - logDebug("No execution context for changeEvent "); - return; - } - final String type = event.getType(); - - final Set triggerValues = new HashSet<>(5); - final String stringNewValue; - final String stringOldValue; - String memberName = null; - if (event instanceof GroupItemStateChangedEvent) { - memberName = ((GroupItemStateChangedEvent) event).getMemberName(); - } - - if (event instanceof ItemStateEvent) { - stringNewValue = ((ItemStateEvent) event).getItemState().toFullString(); - stringOldValue = null; - triggerValues.add(RECEIVED_UPDATE); - triggerValues.add(RECEIVED_UPDATE_APPEND.concat(stringNewValue)); - } else if (event instanceof ItemCommandEvent) { - stringNewValue = ((ItemCommandEvent) event).getItemCommand().toFullString(); - stringOldValue = null; - triggerValues.add(RECEIVED_COMMAND); - triggerValues.add(RECEIVED_COMMAND_APPEND.concat(stringNewValue)); - } else if (event instanceof ItemStateChangedEvent) { - stringNewValue = ((ItemStateChangedEvent) event).getItemState().toFullString(); - stringOldValue = ((ItemStateChangedEvent) event).getOldItemState().toFullString(); - - if (JRuleUtil.isNotEmpty(stringOldValue) && JRuleUtil.isNotEmpty(stringNewValue)) { - triggerValues.add(String.format(CHANGED_FROM_TO_PATTERN, stringOldValue, stringNewValue)); - triggerValues.add(CHANGED_FROM.concat(stringOldValue)); - triggerValues.add(CHANGED_TO.concat(stringNewValue)); - triggerValues.add(CHANGED); + private boolean matchPrecondition(JRuleExecutionContext jRuleExecutionContext) { + return jRuleExecutionContext.getPreconditionContextList().stream().allMatch(context -> { + final Item item; + try { + item = itemRegistry.getItem(context.getItem()); + } catch (ItemNotFoundException e) { + throw new JRuleItemNotFoundException("Cannot find item for precondition", e); } + final String state = item.getState().toString(); + if (context.getEq().isPresent() && context.getEq().filter(state::equals).isEmpty()) { + logDebug("precondition mismatch: {} = {}", state, context.getEq()); + return false; + } + if (context.getNeq().isPresent() && context.getNeq().filter(ref -> !state.equals(ref)).isEmpty()) { + logDebug("precondition mismatch: {} != {}", state, context.getEq()); + return false; + } + if (context.getLt().isPresent() + && context.getLt().filter(ref -> QuantityType.valueOf(state).doubleValue() < ref).isEmpty()) { + logDebug("precondition mismatch: {} < {}", state, context.getEq()); + return false; + } + if (context.getLte().isPresent() + && context.getLte().filter(ref -> QuantityType.valueOf(state).doubleValue() <= ref).isEmpty()) { + logDebug("precondition mismatch: {} <= {}", state, context.getEq()); + return false; + } + if (context.getGt().isPresent() + && context.getGt().filter(ref -> QuantityType.valueOf(state).doubleValue() > ref).isEmpty()) { + logDebug("precondition mismatch: {} > {}", state, context.getEq()); + return false; + } + if (context.getGte().isPresent() + && context.getGte().filter(ref -> QuantityType.valueOf(state).doubleValue() >= ref).isEmpty()) { + logDebug("precondition mismatch: {} >= {}", state, context.getEq()); + return false; + } + return true; + }); + } - logDebug("newValue: {} oldValue: {} type: {}", stringNewValue, stringOldValue, type); - logDebug("Invoked execution contexts: {}", executionContexts.size()); - logDebug("Execution topic Topic: {}", event.getTopic()); - logDebug("Execution topic Payload: {}", event.getPayload()); - logDebug("Execution topic Source: {}", event.getSource()); - logDebug("Execution topic Type: {}", event.getType()); - logDebug("Execution eventToString: {}", event); - } else { - logDebug("Unhandled case: {}", event.getClass()); - return; - } - - if (!triggerValues.isEmpty()) { - String member = memberName == null ? "" : memberName; - executionContexts.stream().filter(context -> triggerValues.contains(context.getTriggerFullString())) - .forEach(context -> invokeWhenMatchParameters(context, - new JRuleEvent(stringNewValue, stringOldValue, itemName, member))); - } else { - logDebug("Execution ignored, no trigger values for itemName: {} eventType: {}", itemName, type); + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_ITEM_EVENT) + || evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_CHANNEL_EVENT) + || evt.getPropertyName().equals(JRuleEventSubscriber.PROPERTY_THING_STATUS_EVENT)) { + fire((AbstractEvent) evt.getNewValue()); } } - private Boolean evaluateComparatorParameters(Double gt, Double gte, Double lt, Double lte, String eq, String neq, - String stateValue) { - if (eq != null) { - return stateValue.equals(eq); - } else if (neq != null) { - return !stateValue.equals(neq); - } else { - // valueAsDouble may be null if unparseable ("NULL" or "UNDEF") - Double valueAsDouble = getValueAsDouble(stateValue); - if (valueAsDouble == null) { - return null; - } else if (gt != null) { - return valueAsDouble > gt; - } else if (gte != null) { - return valueAsDouble >= gte; - } else if (lt != null) { - return valueAsDouble < lt; - } else if (lte != null) { - return valueAsDouble <= lte; + public void dispose() { + if (config.isExecutorsEnabled()) { + ruleExecutorService.shutdownNow(); + try { + ruleExecutorService.awaitTermination(AWAIT_TERMINATION_THREAD_SECONDS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + logWarn("Not all rules ran to completion before rule engine shutdown", e); } } - return null; } - private void invokeWhenMatchParameters(JRuleExecutionContext context, @NonNull JRuleEvent jRuleEvent) { - JRuleLog.debug(logger, context.getLogName(), "invoke when context matches"); - - if (context instanceof JRuleContextValueComparators) { - JRuleContextValueComparators comparators = (JRuleContextValueComparators) context; - if (comparators.hasCompartorsSet()) { + public synchronized void reset() { + contextList.clear(); + timerExecutor.clear(); - final Boolean evalCompare = evaluateComparatorParameters(comparators.getGt(), comparators.getGte(), - comparators.getLt(), comparators.getLte(), comparators.getEq(), comparators.getNeq(), - jRuleEvent.getState().getValue()); - if (evalCompare == null) { - logError("Failed to compare values for context: {} event: {}", context, jRuleEvent); - return; - } - if (!evalCompare) { - logDebug("Not invoking rule since comparator compare is false context: {} event: {}", context, - jRuleEvent); - return; - } - } - } - invokeRule(context, jRuleEvent); + ruleLoadingStatistics = new JRuleLoadingStatistics(ruleLoadingStatistics); } - private Double getValueAsDouble(String value) { - double parseDouble = 0; - if (value == null || value.isEmpty()) { - return null; - } - try { - parseDouble = QuantityType.valueOf(value).doubleValue(); - } catch (Exception x) { - logError("Failed to parse value: {} as double", value, x); - return null; - } - return parseDouble; + public boolean watchingForItem(String itemName) { + boolean b = this.contextList.stream().filter(context -> context instanceof JRuleItemExecutionContext) + .map(context -> ((JRuleItemExecutionContext) context)) + .anyMatch(context -> context.getItemName().equals(itemName)); + logDebug("watching for item: '{}'? -> {}", itemName, b); + return b; } - @Nullable - private String getItemNameFromEvent(Event event) { - if (event instanceof ItemEvent) { - return ((ItemEvent) event).getItemName(); - } - return null; + public boolean watchingForChannel(String channel) { + boolean b = this.contextList.stream().filter(context -> context instanceof JRuleChannelExecutionContext) + .map(context -> ((JRuleChannelExecutionContext) context)) + .anyMatch(context -> context.getChannel().equals(channel)); + logDebug("watching for channel: '{}'? -> {}", channel, b); + return b; } - private void invokeRule(JRuleExecutionContext context, JRuleEvent event) { - Object invocationResult = config.isExecutorsEnabled() ? invokeRuleInSeparateThread(context, event) - : invokeRuleSingleThread(context, event); + public boolean watchingForThing(String thing) { + boolean b = this.contextList.stream().filter(context -> context instanceof JRuleThingExecutionContext) + .map(context -> ((JRuleThingExecutionContext) context)) + .anyMatch(context -> context.getThing().map(s -> s.equals(thing)).orElse(true)); + logDebug("watching for thing: '{}'? -> {}", thing, b); + return b; } - private Object invokeRuleInSeparateThread(JRuleExecutionContext context, JRuleEvent event) { - return ruleExecutorService.submit(() -> invokeRuleInternal(context, event)); + public void setCronScheduler(CronScheduler cronScheduler) { + this.timerExecutor.setCronScheduler(cronScheduler); } - private synchronized Object invokeRuleSingleThread(JRuleExecutionContext context, JRuleEvent event) { - return invokeRuleInternal(context, event); + public JRuleLoadingStatistics getRuleLoadingStatistics() { + return this.ruleLoadingStatistics; } - private Object invokeRuleInternal(JRuleExecutionContext context, JRuleEvent event) { - JRuleLog.debug(logger, context.getLogName(), "Invoking rule for context: {}", context); - - // Check preconditions - boolean preconditionsSatisified = true; - JRulePrecondition[] preconditions = context.getPreconditions(); - if (preconditions != null) { - for (JRulePrecondition precondition : preconditions) { - preconditionsSatisified &= evaluatePrecondition(context, precondition); - } - } - - if (preconditionsSatisified) { - final JRule rule = context.getJrule(); - final Method method = context.getMethod(); - rule.setRuleLogName(context.getLogName()); - try { - JRuleLog.debug(logger, context.getRuleName(), "setting mdc tags: {}", context.getLoggingTags()); - MDC.put(MDC_KEY_RULE, context.getRuleName()); - Arrays.stream(context.getLoggingTags()).forEach(s -> MDC.put(s, s)); - return context.isEventParameterPresent() ? method.invoke(rule, event) : method.invoke(rule); - } catch (InvocationTargetException e) { - Throwable ex = e.getCause() != null ? e.getCause() : null; - JRuleLog.error(logger, context.getRuleName(), "Error message: {}", ex.getMessage()); - JRuleLog.error(logger, context.getRuleName(), "Error Stacktrace: {}", getStackTraceAsString(ex)); - } catch (Exception e) { - JRuleLog.error(logger, context.getRuleName(), "Error {}", e); - } finally { - Arrays.stream(context.getLoggingTags()).forEach(MDC::remove); - MDC.remove(MDC_KEY_RULE); - } - } else { - JRuleLog.debug(logger, context.getLogName(), "Preconditions failed for context: {}", context); - } - return null; + protected void logInfo(String message, Object... parameters) { + JRuleLog.info(logger, JRuleEngine.class.getSimpleName(), message, parameters); } - private boolean evaluatePrecondition(JRuleExecutionContext context, JRulePrecondition precondition) { - try { - final Item item = itemRegistry.getItem(precondition.item()); - final String state = item.getState().toString(); - Boolean evalComparatorParams = evaluateComparatorParameters(getDoubleFromAnnotation(precondition.gt()), - getDoubleFromAnnotation(precondition.gte()), getDoubleFromAnnotation(precondition.lt()), - getDoubleFromAnnotation(precondition.lte()), getStringFromAnnotation(precondition.eq()), - getStringFromAnnotation(precondition.neq()), state); - if (evalComparatorParams == null) { - logError("Failed to evaluate precondition context: {} precondition: {} state: {}", context, - toString(precondition), state); - } - return evalComparatorParams == null || evalComparatorParams; - } catch (ItemNotFoundException e) { - JRuleLog.error(logger, context.getRuleName(), "Precondition item not found: {}", precondition.item()); - } - return true; // For now + protected void logDebug(String message, Object... parameters) { + JRuleLog.debug(logger, JRuleEngine.class.getSimpleName(), message, parameters); } - private String toString(JRulePrecondition precondition) { - return "item=" + precondition.item() + " eq=" + precondition.eq() + " neq=" + precondition.neq() + " gt=" - + precondition.gt() + " gte=" + precondition.gte() + " lt=" + precondition.lt() + " lte=" - + precondition.lte(); + protected void logWarn(String message, Object... parameters) { + JRuleLog.warn(logger, JRuleEngine.class.getSimpleName(), message, parameters); } - private synchronized static String getStackTraceAsString(Throwable throwable) { - try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) { - throwable.printStackTrace(pw); - return sw.toString(); - } catch (IOException ioe) { - throw new IllegalStateException(ioe); - } + protected void logError(String message, Object... parameters) { + JRuleLog.error(logger, JRuleEngine.class.getSimpleName(), message, parameters); } public void setConfig(@NonNull JRuleConfig config) { this.config = config; } + public void setItemRegistry(ItemRegistry itemRegistry) { + this.itemRegistry = itemRegistry; + } + public void initialize() { if (config.isExecutorsEnabled()) { logInfo("Initializing Java Rule Engine with Separate Thread Executors min: {} max: {}", @@ -725,22 +404,39 @@ public Thread newThread(Runnable runnable) { } } - public void dispose() { + public void invokeRule(JRuleExecutionContext context, JRuleEvent event) { if (config.isExecutorsEnabled()) { - ruleExecutorService.shutdownNow(); - try { - ruleExecutorService.awaitTermination(AWAIT_TERMINATION_THREAD_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - logWarn("Not all rules ran to completion before rule engine shutdown", e); - } + ruleExecutorService.submit(() -> invokeRuleInternal(context, event)); + } else { + invokeRuleInternal(context, event); } } - public void setItemRegistry(ItemRegistry itemRegistry) { - this.itemRegistry = itemRegistry; - } + private void invokeRuleInternal(JRuleExecutionContext context, JRuleEvent event) { + JRuleLog.debug(logger, context.getLogName(), "Invoking rule for context: {}", context); - public JRuleLoadingStatistics getRuleLoadingStatistics() { - return ruleLoadingStatistics; + final JRule rule = context.getJrule(); + final Method method = context.getMethod(); + rule.setRuleLogName(context.getLogName()); + try { + JRuleLog.debug(logger, context.getMethod().getName(), "setting mdc tags: {}", context.getLoggingTags()); + MDC.put(MDC_KEY_RULE, context.getMethod().getName()); + Arrays.stream(context.getLoggingTags()).forEach(s -> MDC.put(s, s)); + if (context.hasEventParameterPresent()) { + method.invoke(rule, event); + } else { + method.invoke(rule); + } + } catch (IllegalAccessException | IllegalArgumentException | SecurityException e) { + JRuleLog.error(logger, context.getMethod().getName(), "Error {}", e); + } catch (InvocationTargetException e) { + Throwable ex = e.getCause() != null ? e.getCause() : null; + JRuleLog.error(logger, context.getMethod().getName(), "Error message", ex); + JRuleLog.error(logger, context.getMethod().getName(), "Error Stacktrace: {}", + ExceptionUtils.getStackTrace(ex)); + } finally { + Arrays.stream(context.getLoggingTags()).forEach(MDC::remove); + MDC.remove(MDC_KEY_RULE); + } } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleChannelExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleChannelExecutionContext.java index 4d0559f7..d68d12fa 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleChannelExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleChannelExecutionContext.java @@ -13,9 +13,14 @@ package org.openhab.automation.jrule.internal.engine.excutioncontext; import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRulePrecondition; +import org.openhab.automation.jrule.rules.event.JRuleChannelEvent; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.core.events.AbstractEvent; +import org.openhab.core.thing.events.ChannelTriggeredEvent; /** * The {@link JRuleChannelExecutionContext} @@ -24,29 +29,33 @@ */ public class JRuleChannelExecutionContext extends JRuleExecutionContext { private final String channel; + private final Optional event; - private String eq; - - private String neq; - - public JRuleChannelExecutionContext(JRule jRule, String logName, String[] loggingTags, String ruleName, - Method method, boolean eventParameterPresent, JRulePrecondition[] preconditions, String channel, String eq, - String neq) { - super(jRule, logName, loggingTags, ruleName, method, eventParameterPresent, preconditions); + public JRuleChannelExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + List preconditionContextList, String channel, Optional event) { + super(jRule, logName, loggingTags, method, preconditionContextList); this.channel = channel; - this.eq = eq; - this.neq = neq; + this.event = event; } public String getChannel() { return channel; } - public String getEq() { - return eq; + public Optional getEvent() { + return this.event; + } + + @Override + public boolean match(AbstractEvent event) { + return event instanceof ChannelTriggeredEvent + && ((ChannelTriggeredEvent) event).getChannel().getAsString().equals(this.channel) + && this.event.map(e -> e.equals(((ChannelTriggeredEvent) event).getEvent())).orElse(true); } - public String getNeq() { - return neq; + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleChannelEvent(((ChannelTriggeredEvent) event).getChannel().getAsString(), + ((ChannelTriggeredEvent) event).getEvent()); } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleContextValueComparators.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleContextValueComparators.java deleted file mode 100644 index 024f18d6..00000000 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleContextValueComparators.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2010-2022 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.automation.jrule.internal.engine.excutioncontext; - -/** - * The {@link JRuleContextValueComparators} interface defines the common comparison methods (less than, greater than - * etc) - * - * @author Arne Seime - Initial contribution - */ -public interface JRuleContextValueComparators { - - Double getGt(); - - Double getGte(); - - Double getLt(); - - Double getLte(); - - String getEq(); - - String getNeq(); - - default boolean hasCompartorsSet() { - return getGt() != null || getGte() != null || getLt() != null || getLte() != null || getEq() != null - || getNeq() != null; - } -} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleExecutionContext.java index 9bf4fca2..975bde4f 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleExecutionContext.java @@ -13,9 +13,12 @@ package org.openhab.automation.jrule.internal.engine.excutioncontext; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRulePrecondition; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.core.events.AbstractEvent; /** * The {@link JRuleExecutionContext} @@ -24,31 +27,18 @@ */ public abstract class JRuleExecutionContext { private final String logName; - protected final String ruleName; protected final JRule jRule; protected final Method method; - protected final boolean eventParameterPresent; private final String[] loggingTags; + private final List preconditionContextList; - public JRulePrecondition[] getPreconditions() { - return preconditions; - } - - private final JRulePrecondition[] preconditions; - - public JRuleExecutionContext(JRule jRule, String logName, String[] loggingTags, String ruleName, Method method, - boolean eventParameterPresent, JRulePrecondition[] preconditions) { + public JRuleExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + List preconditionContextList) { this.logName = logName; this.loggingTags = loggingTags; this.jRule = jRule; - this.ruleName = ruleName; this.method = method; - this.eventParameterPresent = eventParameterPresent; - this.preconditions = preconditions; - } - - public String getRuleName() { - return ruleName; + this.preconditionContextList = preconditionContextList; } public JRule getJrule() { @@ -59,8 +49,9 @@ public Method getMethod() { return method; } - public boolean isEventParameterPresent() { - return eventParameterPresent; + public boolean hasEventParameterPresent() { + return Arrays.stream(method.getParameters()) + .anyMatch(param -> (JRuleEvent.class.isAssignableFrom(param.getType()))); } public String getLogName() { @@ -70,4 +61,12 @@ public String getLogName() { public String[] getLoggingTags() { return loggingTags; } + + public abstract boolean match(AbstractEvent event); + + public abstract JRuleEvent createJRuleEvent(AbstractEvent event); + + public List getPreconditionContextList() { + return preconditionContextList; + } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemChangeExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemChangeExecutionContext.java new file mode 100644 index 00000000..e594bcb7 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemChangeExecutionContext.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleEventState; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleItemEvent; +import org.openhab.core.events.AbstractEvent; +import org.openhab.core.items.events.GroupItemStateChangedEvent; +import org.openhab.core.items.events.ItemStateChangedEvent; + +/** + * The {@link JRuleItemChangeExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleItemChangeExecutionContext extends JRuleItemExecutionContext { + private final Optional from; + private final Optional to; + + public JRuleItemChangeExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + String itemName, Optional lt, Optional lte, Optional gt, Optional gte, + Optional eq, Optional neq, List preconditionContextList, + Optional from, Optional to) { + super(jRule, logName, loggingTags, method, itemName, lt, lte, gt, gte, eq, neq, preconditionContextList); + this.from = from; + this.to = to; + } + + @Override + public boolean match(AbstractEvent event) { + return event instanceof ItemStateChangedEvent + && ((ItemStateChangedEvent) event).getItemName().equals(this.getItemName()) + && from.map(s -> ((ItemStateChangedEvent) event).getOldItemState().toString().equals(s)).orElse(true) + && to.map(s -> ((ItemStateChangedEvent) event).getItemState().toString().equals(s)).orElse(true) + && super.matchCondition(((ItemStateChangedEvent) event).getItemState().toString()); + } + + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + String memberName = event instanceof GroupItemStateChangedEvent + ? ((GroupItemStateChangedEvent) event).getMemberName() + : null; + return new JRuleItemEvent(this.getItemName(), memberName, + new JRuleEventState(((ItemStateChangedEvent) event).getItemState().toString()), + new JRuleEventState(((ItemStateChangedEvent) event).getOldItemState().toString())); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemExecutionContext.java index 0e234a7a..85d45a55 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemExecutionContext.java @@ -13,44 +13,31 @@ package org.openhab.automation.jrule.internal.engine.excutioncontext; import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRulePrecondition; +import org.openhab.core.library.types.QuantityType; /** * The {@link JRuleItemExecutionContext} * * @author Robert Delbrück - Initial contribution */ -public class JRuleItemExecutionContext extends JRuleExecutionContext implements JRuleContextValueComparators { - private static final String FROM_PREFIX = " from "; - private static final String TO_PREFIX = " to "; - private static final String SPACE = " "; - - private final String itemClass; +public abstract class JRuleItemExecutionContext extends JRuleExecutionContext { private final String itemName; - private final String trigger; - private final String update; - private final String from; - private final String to; - private final Double gt; - private final Double gte; - private final Double lt; - private final Double lte; - protected final String eq; - protected final String neq; - - public JRuleItemExecutionContext(JRule jRule, String logName, String[] loggingTags, String trigger, String from, - String to, String update, String ruleName, String itemClass, String itemName, Method method, - boolean eventParameterPresent, Double lt, Double lte, Double gt, Double gte, String eq, String neq, - JRulePrecondition[] preconditions) { - super(jRule, logName, loggingTags, ruleName, method, eventParameterPresent, preconditions); - this.itemClass = itemClass; + private final Optional gt; + private final Optional gte; + private final Optional lt; + private final Optional lte; + protected final Optional eq; + protected final Optional neq; + + public JRuleItemExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, String itemName, + Optional lt, Optional lte, Optional gt, Optional gte, Optional eq, + Optional neq, List preconditionContextList) { + super(jRule, logName, loggingTags, method, preconditionContextList); this.itemName = itemName; - this.trigger = trigger; - this.update = update; - this.from = from; - this.to = to; this.gt = gt; this.gte = gte; this.lt = lt; @@ -63,73 +50,51 @@ public String getItemName() { return itemName; } - public Double getGt() { + public boolean matchCondition(String state) { + if (getEq().isPresent() && getEq().filter(state::equals).isEmpty()) { + return false; + } + if (getNeq().isPresent() && getNeq().filter(ref -> !state.equals(ref)).isEmpty()) { + return false; + } + if (getLt().isPresent() && getLt().filter(ref -> QuantityType.valueOf(state).doubleValue() < ref).isEmpty()) { + return false; + } + if (getLte().isPresent() + && getLte().filter(ref -> QuantityType.valueOf(state).doubleValue() <= ref).isEmpty()) { + return false; + } + if (getGt().isPresent() && getGt().filter(ref -> QuantityType.valueOf(state).doubleValue() > ref).isEmpty()) { + return false; + } + if (getGte().isPresent() + && getGte().filter(ref -> QuantityType.valueOf(state).doubleValue() >= ref).isEmpty()) { + return false; + } + return true; + } + + public Optional getGt() { return gt; } - public Double getGte() { + public Optional getGte() { return gte; } - public Double getLt() { + public Optional getLt() { return lt; } - public Double getLte() { + public Optional getLte() { return lte; } - public String getEq() { + public Optional getEq() { return eq; } - public String getNeq() { + public Optional getNeq() { return neq; } - - public String getTrigger() { - return trigger; - } - - private String buildFromToString(String trigger, String from, String to) { - final StringBuilder builder = new StringBuilder(); - builder.append(trigger); - if (from != null) { - builder.append(FROM_PREFIX); - builder.append(from); - } - if (to != null) { - builder.append(TO_PREFIX); - builder.append(to); - } - return builder.toString(); - } - - private String buildUpdateString(String trigger, String update) { - return trigger + SPACE + update; - } - - public String getTriggerFullString() { - if (from != null && !from.isEmpty() && to != null && !to.isEmpty()) { - return buildFromToString(trigger, from, to); - } - if (from != null && !from.isEmpty()) { - return buildFromToString(trigger, from, null); - } - if (to != null && !to.isEmpty()) { - return buildFromToString(trigger, null, to); - } - if (update != null && !update.isEmpty()) { - return buildUpdateString(trigger, update); - } - return trigger; - } - - @Override - public String toString() { - return "JRuleExecutionContext [trigger=" + trigger + ", ruleName=" + ruleName + ", itemClass=" + itemClass - + ", itemName=" + itemName + ", from=" + from + ", to=" + to + ", gt=" + gt + ", gte=" + gte + ", lt=" - + lt + ", lte=" + lte + ", eq=" + eq + ", update=" + update + ", jRule=" + jRule + ", method=" + method - + ", eventParameterPresent=" + eventParameterPresent + "]"; - } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedCommandExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedCommandExecutionContext.java new file mode 100644 index 00000000..b8ef0bbb --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedCommandExecutionContext.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleEventState; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleItemEvent; +import org.openhab.core.events.AbstractEvent; +import org.openhab.core.items.events.ItemCommandEvent; + +/** + * The {@link JRuleItemReceivedCommandExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleItemReceivedCommandExecutionContext extends JRuleItemExecutionContext { + private final Optional command; + + public JRuleItemReceivedCommandExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + String itemName, Optional lt, Optional lte, Optional gt, Optional gte, + Optional eq, Optional neq, List preconditionContextList, + Optional command) { + super(jRule, logName, loggingTags, method, itemName, lt, lte, gt, gte, eq, neq, preconditionContextList); + this.command = command; + } + + @Override + public boolean match(AbstractEvent event) { + return event instanceof ItemCommandEvent && ((ItemCommandEvent) event).getItemName().equals(this.getItemName()) + && command.map(s -> ((ItemCommandEvent) event).getItemCommand().toString().equals(s)).orElse(true) + && super.matchCondition(((ItemCommandEvent) event).getItemCommand().toString()); + } + + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleItemEvent(this.getItemName(), null, + new JRuleEventState(((ItemCommandEvent) event).getItemCommand().toString()), null); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedUpdateExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedUpdateExecutionContext.java new file mode 100644 index 00000000..611ac11a --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleItemReceivedUpdateExecutionContext.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.JRuleEventState; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleItemEvent; +import org.openhab.core.events.AbstractEvent; +import org.openhab.core.items.events.ItemStateEvent; + +/** + * The {@link JRuleItemReceivedUpdateExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleItemReceivedUpdateExecutionContext extends JRuleItemExecutionContext { + private final Optional state; + + public JRuleItemReceivedUpdateExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + String itemName, Optional lt, Optional lte, Optional gt, Optional gte, + Optional eq, Optional neq, List preconditionContextList, + Optional state) { + super(jRule, logName, loggingTags, method, itemName, lt, lte, gt, gte, eq, neq, preconditionContextList); + this.state = state; + } + + @Override + public boolean match(AbstractEvent event) { + return event instanceof ItemStateEvent && ((ItemStateEvent) event).getItemName().equals(this.getItemName()) + && state.map(s -> ((ItemStateEvent) event).getItemState().toString().equals(s)).orElse(true) + && super.matchCondition(((ItemStateEvent) event).getItemState().toString()); + } + + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleItemEvent(this.getItemName(), null, + new JRuleEventState(((ItemStateEvent) event).getItemState().toString()), null); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRulePreconditionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRulePreconditionContext.java new file mode 100644 index 00000000..8870be11 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRulePreconditionContext.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.util.Optional; + +/** + * The {@link JRulePreconditionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRulePreconditionContext { + + private final String item; + private final Optional lt; + private final Optional lte; + private final Optional gt; + private final Optional gte; + private final Optional eq; + private final Optional neq; + + public JRulePreconditionContext(String item, Optional lt, Optional lte, Optional gt, + Optional gte, Optional eq, Optional neq) { + this.item = item; + this.lt = lt; + this.lte = lte; + this.gt = gt; + this.gte = gte; + this.eq = eq; + this.neq = neq; + } + + public String getItem() { + return item; + } + + public Optional getLt() { + return lt; + } + + public Optional getLte() { + return lte; + } + + public Optional getGt() { + return gt; + } + + public Optional getGte() { + return gte; + } + + public Optional getEq() { + return eq; + } + + public Optional getNeq() { + return neq; + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleThingExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleThingExecutionContext.java index 63b3e8c9..502afab5 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleThingExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleThingExecutionContext.java @@ -13,9 +13,15 @@ package org.openhab.automation.jrule.internal.engine.excutioncontext; import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRulePrecondition; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleThingEvent; +import org.openhab.automation.jrule.things.JRuleThingStatus; +import org.openhab.core.events.AbstractEvent; +import org.openhab.core.thing.events.ThingStatusInfoChangedEvent; /** * The {@link JRuleThingExecutionContext} - execution context for thing triggers @@ -23,64 +29,43 @@ * @author Arne Seime - Initial contribution */ public class JRuleThingExecutionContext extends JRuleExecutionContext { - public static final String ANY_THING_UID = "*"; // Wildcard, allows registering rules that listen for all thing - private static final String FROM_PREFIX = " from "; - private static final String TO_PREFIX = " to "; + private final Optional thing; + private final Optional from; + private final Optional to; - private final String thing; - private final String trigger; - private final String from; - private final String to; - - public JRuleThingExecutionContext(JRule jRule, String logName, String[] loggingTags, String trigger, String from, - String to, String ruleName, String thing, Method method, boolean eventParameterPresent, - JRulePrecondition[] preconditions) { - super(jRule, logName, loggingTags, ruleName, method, eventParameterPresent, preconditions); + public JRuleThingExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + Optional thing, Optional from, Optional to, + List preconditions) { + super(jRule, logName, loggingTags, method, preconditions); this.thing = thing; - this.trigger = trigger; this.from = from; this.to = to; } - public String getThing() { + public Optional getThing() { return thing; } - public String getTrigger() { - return trigger; - } - - public String getTriggerFullString() { - if (from != null && !from.isEmpty() && to != null && !to.isEmpty()) { - return buildFromToString(trigger, from, to); - } - if (from != null && !from.isEmpty()) { - return buildFromToString(trigger, from, null); - } - if (to != null && !to.isEmpty()) { - return buildFromToString(trigger, null, to); - } - - return trigger; + @Override + public String toString() { + return "JRuleThingExecutionContext{" + "thing='" + thing + '\'' + ", from='" + from + '\'' + ", to='" + to + + '\'' + '}'; } - private String buildFromToString(String trigger, String from, String to) { - final StringBuilder builder = new StringBuilder(); - builder.append(trigger); - if (from != null) { - builder.append(FROM_PREFIX); - builder.append(from); - } - if (to != null) { - builder.append(TO_PREFIX); - builder.append(to); + @Override + public boolean match(AbstractEvent event) { + if (!(event instanceof ThingStatusInfoChangedEvent)) { + return false; } - return builder.toString(); + ThingStatusInfoChangedEvent evt = (ThingStatusInfoChangedEvent) event; + return thing.map(s -> evt.getThingUID().toString().equals(s)).orElse(true) + && from.map(s -> evt.getOldStatusInfo().getStatus().name().equals(s.name())).orElse(true) + && to.map(s -> evt.getStatusInfo().getStatus().name().equals(s.name())).orElse(true); } @Override - public String toString() { - return "JRuleThingExecutionContext{" + "thing='" + thing + '\'' + ", trigger='" + trigger + '\'' + ", from='" - + from + '\'' + ", to='" + to + '\'' + '}'; + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleThingEvent(((ThingStatusInfoChangedEvent) event).getThingUID().toString(), + ((ThingStatusInfoChangedEvent) event).getStatusInfo().getStatus().name()); } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimeTimerExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimeTimerExecutionContext.java new file mode 100644 index 00000000..eff7a1be --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimeTimerExecutionContext.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleTimerEvent; +import org.openhab.core.events.AbstractEvent; + +/** + * The {@link JRuleTimeTimerExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleTimeTimerExecutionContext extends JRuleTimedExecutionContext { + private final Optional hour; + private final Optional minute; + private final Optional second; + + public JRuleTimeTimerExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + List preconditionContextList, Optional hour, Optional minute, + Optional second) { + super(jRule, logName, loggingTags, method, preconditionContextList); + this.hour = hour; + this.minute = minute; + this.second = second; + } + + @Override + public boolean match(AbstractEvent event) { + return false; + } + + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleTimerEvent(); + } + + public Optional getHour() { + return hour; + } + + public Optional getMinute() { + return minute; + } + + public Optional getSecond() { + return second; + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedCronExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedCronExecutionContext.java new file mode 100644 index 00000000..92652d8d --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedCronExecutionContext.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.excutioncontext; + +import java.lang.reflect.Method; +import java.util.List; + +import org.openhab.automation.jrule.rules.JRule; +import org.openhab.automation.jrule.rules.event.JRuleEvent; +import org.openhab.automation.jrule.rules.event.JRuleTimerEvent; +import org.openhab.core.events.AbstractEvent; + +/** + * The {@link JRuleTimedCronExecutionContext} + * + * @author Robert Delbrück - Initial contribution + */ +public class JRuleTimedCronExecutionContext extends JRuleTimedExecutionContext { + private final String cron; + + public JRuleTimedCronExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + List preconditionContextList, String cron) { + super(jRule, logName, loggingTags, method, preconditionContextList); + this.cron = cron; + } + + @Override + public boolean match(AbstractEvent event) { + return false; + } + + @Override + public JRuleEvent createJRuleEvent(AbstractEvent event) { + return new JRuleTimerEvent(); + } + + public String getCron() { + return cron; + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedExecutionContext.java b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedExecutionContext.java index 78798deb..996ffb8b 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedExecutionContext.java +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/excutioncontext/JRuleTimedExecutionContext.java @@ -13,18 +13,18 @@ package org.openhab.automation.jrule.internal.engine.excutioncontext; import java.lang.reflect.Method; +import java.util.List; import org.openhab.automation.jrule.rules.JRule; -import org.openhab.automation.jrule.rules.JRulePrecondition; /** * The {@link JRuleTimedExecutionContext} * * @author Robert Delbrück - Initial contribution */ -public class JRuleTimedExecutionContext extends JRuleExecutionContext { - public JRuleTimedExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, String ruleName, - boolean jRuleEventPresent, JRulePrecondition[] preconditions) { - super(jRule, logName, loggingTags, ruleName, method, jRuleEventPresent, preconditions); +public abstract class JRuleTimedExecutionContext extends JRuleExecutionContext { + public JRuleTimedExecutionContext(JRule jRule, String logName, String[] loggingTags, Method method, + List preconditionContextList) { + super(jRule, logName, loggingTags, method, preconditionContextList); } } diff --git a/src/main/java/org/openhab/automation/jrule/internal/engine/timer/TimerExecutor.java b/src/main/java/org/openhab/automation/jrule/internal/engine/timer/TimerExecutor.java new file mode 100644 index 00000000..099b52de --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/engine/timer/TimerExecutor.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.engine.timer; + +import java.util.ArrayList; +import java.util.List; + +import org.openhab.automation.jrule.internal.JRuleLog; +import org.openhab.automation.jrule.internal.engine.JRuleEngine; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleTimeTimerExecutionContext; +import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleTimedCronExecutionContext; +import org.openhab.core.scheduler.CronScheduler; +import org.openhab.core.scheduler.ScheduledCompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link TimerExecutor} + * + * @author Robert Delbrück + */ +public class TimerExecutor { + private final Logger logger = LoggerFactory.getLogger(TimerExecutor.class); + private final List> timers = new ArrayList<>(); + private final JRuleEngine jRuleEngine; + private CronScheduler cronScheduler; + + public TimerExecutor(JRuleEngine jRuleEngine) { + this.jRuleEngine = jRuleEngine; + } + + public void add(JRuleTimedCronExecutionContext executionContext) { + timers.add(cronScheduler.schedule( + () -> jRuleEngine.invokeRule(executionContext, executionContext.createJRuleEvent(null)), + executionContext.getCron())); + } + + public void add(JRuleTimeTimerExecutionContext executionContext) { + String cron = String.format("%s %s %s %s %s %s", executionContext.getSecond().map(String::valueOf).orElse("*"), + executionContext.getMinute().map(String::valueOf).orElse("*"), + executionContext.getHour().map(String::valueOf).orElse("*"), "*", "*", "*"); + JRuleLog.info(logger, TimerExecutor.class.getSimpleName(), "Generated cron for timer: {}", cron); + timers.add(cronScheduler.schedule( + () -> jRuleEngine.invokeRule(executionContext, executionContext.createJRuleEvent(null)), cron)); + } + + public void add(JRuleExecutionContext context) { + if (context instanceof JRuleTimedCronExecutionContext) { + this.add((JRuleTimedCronExecutionContext) context); + } else if (context instanceof JRuleTimeTimerExecutionContext) { + this.add((JRuleTimeTimerExecutionContext) context); + } + } + + public void setCronScheduler(CronScheduler cronScheduler) { + this.cronScheduler = cronScheduler; + } + + public void clear() { + timers.forEach(timer -> timer.cancel(true)); + timers.clear(); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/internal/events/JRuleEventSubscriber.java b/src/main/java/org/openhab/automation/jrule/internal/events/JRuleEventSubscriber.java index 1ae2f871..4c2f3d92 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/events/JRuleEventSubscriber.java +++ b/src/main/java/org/openhab/automation/jrule/internal/events/JRuleEventSubscriber.java @@ -24,7 +24,6 @@ import org.openhab.automation.jrule.internal.JRuleLog; import org.openhab.automation.jrule.internal.JRuleUtil; import org.openhab.automation.jrule.internal.engine.JRuleEngine; -import org.openhab.automation.jrule.internal.engine.excutioncontext.JRuleThingExecutionContext; import org.openhab.core.events.Event; import org.openhab.core.events.EventFilter; import org.openhab.core.events.EventSubscriber; @@ -70,17 +69,12 @@ public class JRuleEventSubscriber implements EventSubscriber { private final Set subscribedEventTypes = new HashSet<>(); - private final Set jRuleMonitoredItems = new HashSet<>(); - - private final Set jRuleMonitoredChannels = new HashSet<>(); - - private final Set jRuleMonitoredThings = new HashSet<>(); - private final PropertyChangeSupport propertyChangeSupport; private final Queue eventQueue = new ConcurrentLinkedQueue<>(); private volatile boolean queueEvents = false; + private JRuleEngine jRuleEngine = JRuleEngine.get(); public JRuleEventSubscriber() { propertyChangeSupport = new PropertyChangeSupport(this); @@ -112,25 +106,12 @@ public Set getSubscribedEventTypes() { public void startSubscriber() { JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Starting subscriber"); - registerSubscribedItemsAndChannels(); propertyChangeSupport.addPropertyChangeListener(JRuleEngine.get()); } - public void registerSubscribedItemsAndChannels() { - jRuleMonitoredItems.clear(); - jRuleMonitoredChannels.clear(); - jRuleMonitoredThings.clear(); - - jRuleMonitoredItems.addAll(JRuleEngine.get().getItemNames()); - jRuleMonitoredChannels.addAll(JRuleEngine.get().getChannelNames()); - jRuleMonitoredThings.addAll(JRuleEngine.get().getThingUIDs()); - } - public void stopSubscriber() { - jRuleMonitoredItems.clear(); - jRuleMonitoredChannels.clear(); - jRuleMonitoredThings.clear(); propertyChangeSupport.removePropertyChangeListener(JRuleEngine.get()); + propertyChangeSupport.removePropertyChangeListener(jRuleEngine); } /** @@ -168,7 +149,8 @@ public void receive(Event event) { } private void processEvent(Event event) { - final String itemFromTopic = JRuleUtil.getItemNameFromTopic(event.getTopic()); + JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Received event '{}' with topic '{}' and payload '{}'", + event.getType(), event.getTopic(), event.getPayload()); if (event.getType().equals(ItemAddedEvent.TYPE) // || event.getType().equals(ItemRemovedEvent.TYPE) // || event.getType().equals(ItemUpdatedEvent.TYPE)) { @@ -180,14 +162,19 @@ private void processEvent(Event event) { JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "event processed as {}: topic {} payload: {}", PROPERTY_THING_REGISTRY_EVENT, event.getTopic(), event.getPayload()); propertyChangeSupport.firePropertyChange(PROPERTY_THING_REGISTRY_EVENT, null, event); - } else if (jRuleMonitoredItems.contains(itemFromTopic)) { - JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Event processed as {}: topic {} payload: {}", - PROPERTY_ITEM_EVENT, event.getTopic(), event.getPayload()); - propertyChangeSupport.firePropertyChange(PROPERTY_ITEM_EVENT, null, event); + } else if (event.getType().equals(ItemStateEvent.TYPE) || event.getType().equals(ItemCommandEvent.TYPE) + || event.getType().equals(ItemStateChangedEvent.TYPE) + || event.getType().equals(GroupItemStateChangedEvent.TYPE)) { + final String itemFromTopic = JRuleUtil.getItemNameFromTopic(event.getTopic()); + if (jRuleEngine.watchingForItem(itemFromTopic)) { + JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Event processed as {}: topic {} payload: {}", + PROPERTY_ITEM_EVENT, event.getTopic(), event.getPayload()); + propertyChangeSupport.firePropertyChange(PROPERTY_ITEM_EVENT, null, event); + } } else if (event.getType().equals(ChannelTriggeredEvent.TYPE)) { ChannelTriggeredEvent channelTriggeredEvent = (ChannelTriggeredEvent) event; String channel = channelTriggeredEvent.getChannel().toString(); - if (jRuleMonitoredChannels.contains(channel)) { + if (jRuleEngine.watchingForChannel(channel)) { JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Event processed as {}: topic {} payload: {}", PROPERTY_CHANNEL_EVENT, event.getTopic(), event.getPayload()); propertyChangeSupport.firePropertyChange(PROPERTY_CHANNEL_EVENT, null, event); @@ -196,8 +183,7 @@ private void processEvent(Event event) { ThingStatusInfoChangedEvent thingStatusChangedEvent = (ThingStatusInfoChangedEvent) event; String thingUID = thingStatusChangedEvent.getThingUID().toString(); - if (jRuleMonitoredThings.contains(thingUID) - || jRuleMonitoredThings.contains(JRuleThingExecutionContext.ANY_THING_UID)) { + if (jRuleEngine.watchingForThing(thingUID)) { JRuleLog.debug(logger, LOG_NAME_SUBSCRIBER, "Event processed as {}: topic {} payload: {}", PROPERTY_THING_STATUS_EVENT, event.getTopic(), event.getPayload()); propertyChangeSupport.firePropertyChange(PROPERTY_THING_STATUS_EVENT, null, event); diff --git a/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java index 9cae7450..c4745f77 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java +++ b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java @@ -52,6 +52,7 @@ import org.openhab.core.items.events.ItemAddedEvent; import org.openhab.core.items.events.ItemRemovedEvent; import org.openhab.core.items.events.ItemUpdatedEvent; +import org.openhab.core.scheduler.CronScheduler; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingManager; import org.openhab.core.thing.ThingRegistry; @@ -106,7 +107,7 @@ public class JRuleHandler implements PropertyChangeListener { public JRuleHandler(JRuleConfig config, ItemRegistry itemRegistry, ThingRegistry thingRegistry, ThingManager thingManager, EventPublisher eventPublisher, JRuleEventSubscriber eventSubscriber, - VoiceManager voiceManager, BundleContext bundleContext) { + VoiceManager voiceManager, CronScheduler cronScheduler, BundleContext bundleContext) { this.itemRegistry = itemRegistry; this.thingRegistry = thingRegistry; this.eventSubscriber = eventSubscriber; @@ -197,6 +198,7 @@ public synchronized void dispose() { delayedItemsCompiler.cancel(); delayedItemsCompiler.shutdown(); JRuleEngine.get().reset(); + JRuleEngine.get().dispose(); if (directoryWatcher != null) { directoryWatcher.removePropertyChangeListener(this); } @@ -301,7 +303,6 @@ private synchronized Boolean compileAndReloadRules() { JRuleEngine.get().reset(); createRuleInstances(); logInfo("JRule Engine Rules Reloaded! {}", JRuleEngine.get().getRuleLoadingStatistics()); - eventSubscriber.registerSubscribedItemsAndChannels(); eventSubscriber.resumeEventDelivery(); return true; } diff --git a/src/main/java/org/openhab/automation/jrule/internal/test/JRuleMockedEventBus.java b/src/main/java/org/openhab/automation/jrule/internal/test/JRuleMockedEventBus.java index edf62527..152a9e52 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/test/JRuleMockedEventBus.java +++ b/src/main/java/org/openhab/automation/jrule/internal/test/JRuleMockedEventBus.java @@ -29,7 +29,6 @@ public JRuleMockedEventBus(String eventBusResourceName) { super(); JRuleTestEventLogParser parser = new JRuleTestEventLogParser(eventBusResourceName); eventList = parser.parse(); - registerSubscribedItemsAndChannels(); } public void start() { diff --git a/src/main/java/org/openhab/automation/jrule/items/JRuleDimmerItem.java b/src/main/java/org/openhab/automation/jrule/items/JRuleDimmerItem.java index 50d3a564..405ea5cd 100644 --- a/src/main/java/org/openhab/automation/jrule/items/JRuleDimmerItem.java +++ b/src/main/java/org/openhab/automation/jrule/items/JRuleDimmerItem.java @@ -19,14 +19,14 @@ import org.openhab.automation.jrule.internal.handler.JRuleEventHandler; import org.openhab.automation.jrule.rules.value.JRuleIncreaseDecreaseValue; import org.openhab.automation.jrule.rules.value.JRuleOnOffValue; -import org.openhab.automation.jrule.trigger.JRuleDimmerTrigger; +import org.openhab.automation.jrule.trigger.JRuleSwitchTrigger; /** * The {@link JRuleDimmerItem} Items * * @author Joseph (Seaside) Hagberg - Initial contribution */ -public abstract class JRuleDimmerItem extends JRuleItem implements JRuleDimmerTrigger { +public abstract class JRuleDimmerItem extends JRuleItem implements JRuleSwitchTrigger { protected JRuleDimmerItem(String itemName) { super(itemName); diff --git a/src/main/java/org/openhab/automation/jrule/items/JRuleGroupDimmerItem.java b/src/main/java/org/openhab/automation/jrule/items/JRuleGroupDimmerItem.java index 07f22d57..fe500538 100644 --- a/src/main/java/org/openhab/automation/jrule/items/JRuleGroupDimmerItem.java +++ b/src/main/java/org/openhab/automation/jrule/items/JRuleGroupDimmerItem.java @@ -21,7 +21,7 @@ import org.openhab.automation.jrule.internal.handler.JRuleEventHandler; import org.openhab.automation.jrule.rules.value.JRuleIncreaseDecreaseValue; import org.openhab.automation.jrule.rules.value.JRuleOnOffValue; -import org.openhab.automation.jrule.trigger.JRuleDimmerTrigger; +import org.openhab.automation.jrule.trigger.JRuleSwitchTrigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +30,7 @@ * * @author Arne Seime - Initial contribution */ -public abstract class JRuleGroupDimmerItem extends JRuleGroupItem implements JRuleDimmerTrigger { +public abstract class JRuleGroupDimmerItem extends JRuleGroupItem implements JRuleSwitchTrigger { private static final String LOG_NAME = "JRuleGroupDimmerItem"; private static final Logger logger = LoggerFactory.getLogger(JRuleGroupDimmerItem.class); diff --git a/src/main/java/org/openhab/automation/jrule/items/JRuleItem.java b/src/main/java/org/openhab/automation/jrule/items/JRuleItem.java index a757e7e5..f477641b 100644 --- a/src/main/java/org/openhab/automation/jrule/items/JRuleItem.java +++ b/src/main/java/org/openhab/automation/jrule/items/JRuleItem.java @@ -15,14 +15,12 @@ import java.time.ZonedDateTime; import java.util.Optional; -import org.openhab.automation.jrule.trigger.JRuleCommonTrigger; - /** * The {@link JRuleItem} Items * * @author Joseph (Seaside) Hagberg - Initial contribution */ -public abstract class JRuleItem implements JRuleCommonTrigger { +public abstract class JRuleItem { protected String itemName; diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleCondition.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleCondition.java new file mode 100644 index 00000000..d0f52c3d --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleCondition.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +/** + * The {@link JRuleCondition} + * + * @author Robert Delbrück + */ +public @interface JRuleCondition { + double gt() default Double.MIN_VALUE; + + double lt() default Double.MIN_VALUE; + + double gte() default Double.MIN_VALUE; + + double lte() default Double.MIN_VALUE; + + String eq() default ""; + + String neq() default ""; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRulePrecondition.java b/src/main/java/org/openhab/automation/jrule/rules/JRulePrecondition.java index 062906ff..4021be15 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRulePrecondition.java +++ b/src/main/java/org/openhab/automation/jrule/rules/JRulePrecondition.java @@ -29,15 +29,5 @@ public @interface JRulePrecondition { String item() default ""; - double gt() default Double.MIN_VALUE; - - double lt() default Double.MIN_VALUE; - - double gte() default Double.MIN_VALUE; - - double lte() default Double.MIN_VALUE; - - String eq() default ""; - - String neq() default ""; + JRuleCondition condition() default @JRuleCondition; } diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTrigger.java new file mode 100644 index 00000000..7945c2e2 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTrigger.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenChannelTriggers} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenChannelTriggers.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenChannelTrigger { + String channel() default ""; + + String event() default ""; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTriggers.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTriggers.java new file mode 100644 index 00000000..85cb09b4 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenChannelTriggers.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenChannelTriggers} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenChannelTriggers { + JRuleWhenChannelTrigger[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTrigger.java new file mode 100644 index 00000000..7e792b66 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTrigger.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenCronTriggers} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenCronTriggers.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenCronTrigger { + String cron() default ""; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTriggers.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTriggers.java new file mode 100644 index 00000000..f8a1ced8 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenCronTriggers.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenCronTriggers} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenCronTriggers { + JRuleWhenCronTrigger[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChange.java similarity index 55% rename from src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java rename to src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChange.java index b602acfa..443c5575 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhen.java +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChange.java @@ -19,45 +19,19 @@ import java.lang.annotation.Target; /** - * The {@link JRuleWhen} + * The {@link JRuleWhenItemChange} * - * @author Joseph (Seaside) Hagberg - Initial contribution + * @author Robert Delbrück */ -@Repeatable(JRuleWhens.class) +@Repeatable(JRuleWhenItemChanges.class) @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) -public @interface JRuleWhen { - String cron() default ""; - - int hours() default -1; - - int minutes() default -1; - - int seconds() default -1; - +public @interface JRuleWhenItemChange { String item() default ""; - String channel() default ""; - - String trigger() default ""; - - String thing() default ""; - - String update() default ""; - String from() default ""; String to() default ""; - double gt() default Double.MIN_VALUE; - - double lt() default Double.MIN_VALUE; - - double gte() default Double.MIN_VALUE; - - double lte() default Double.MIN_VALUE; - - String eq() default ""; - - String neq() default ""; + JRuleCondition condition() default @JRuleCondition; } diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhens.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChanges.java similarity index 82% rename from src/main/java/org/openhab/automation/jrule/rules/JRuleWhens.java rename to src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChanges.java index 24bde33d..d3337fad 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhens.java +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemChanges.java @@ -18,12 +18,12 @@ import java.lang.annotation.Target; /** - * The {@link JRuleWhen} + * The {@link JRuleWhenItemChanges} * - * @author Joseph (Seaside) Hagberg - Initial contribution + * @author Robert Delbrück */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) -public @interface JRuleWhens { - JRuleWhen[] value(); +public @interface JRuleWhenItemChanges { + JRuleWhenItemChange[] value(); } diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommand.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommand.java new file mode 100644 index 00000000..2c5846f9 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommand.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenItemReceivedCommand} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenItemReceivedCommands.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenItemReceivedCommand { + String item() default ""; + + String command() default ""; + + JRuleCondition condition() default @JRuleCondition; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommands.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommands.java new file mode 100644 index 00000000..1ddbbf72 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedCommands.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenItemReceivedCommands} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenItemReceivedCommands { + JRuleWhenItemReceivedCommand[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdate.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdate.java new file mode 100644 index 00000000..e93a7f57 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdate.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenItemReceivedUpdate} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenItemReceivedUpdates.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenItemReceivedUpdate { + String item() default ""; + + String state() default ""; + + JRuleCondition condition() default @JRuleCondition; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdates.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdates.java new file mode 100644 index 00000000..ddeee0e3 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenItemReceivedUpdates.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenItemReceivedUpdates} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenItemReceivedUpdates { + JRuleWhenItemReceivedUpdate[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTrigger.java new file mode 100644 index 00000000..267f1d97 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTrigger.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.openhab.automation.jrule.things.JRuleThingStatus; + +/** + * The {@link JRuleWhenThingTrigger} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenThingTriggers.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenThingTrigger { + String thing() default ""; + + JRuleThingStatus from() default JRuleThingStatus.THING_UNKNOWN; + + JRuleThingStatus to() default JRuleThingStatus.THING_UNKNOWN; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTriggers.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTriggers.java new file mode 100644 index 00000000..0cbc77a0 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenThingTriggers.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenThingTriggers} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenThingTriggers { + JRuleWhenThingTrigger[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTrigger.java new file mode 100644 index 00000000..8dbb1769 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTrigger.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenTimeTrigger} + * + * @author Robert Delbrück + */ +@Repeatable(JRuleWhenTimeTriggers.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenTimeTrigger { + int hours() default -1; + + int minutes() default -1; + + int seconds() default -1; +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTriggers.java b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTriggers.java new file mode 100644 index 00000000..543200ef --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/JRuleWhenTimeTriggers.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link JRuleWhenTimeTriggers} + * + * @author Robert Delbrück + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface JRuleWhenTimeTriggers { + JRuleWhenTimeTrigger[] value(); +} diff --git a/src/main/java/org/openhab/automation/jrule/rules/event/JRuleChannelEvent.java b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleChannelEvent.java new file mode 100644 index 00000000..7f712f37 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleChannelEvent.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules.event; + +/** + * The {@link JRuleChannelEvent} + * + * @author Robert Delbrück + */ +public class JRuleChannelEvent extends JRuleEvent { + private String channel; + private String event; + + public String getChannel() { + return channel; + } + + public String getEvent() { + return event; + } + + public JRuleChannelEvent(String channel, String event) { + this.channel = channel; + this.event = event; + } + + @Override + public String toString() { + return String.format("JRuleEvent [channel=%s]", channel); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRuleThingStatusTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleEvent.java similarity index 63% rename from src/main/java/org/openhab/automation/jrule/trigger/JRuleThingStatusTrigger.java rename to src/main/java/org/openhab/automation/jrule/rules/event/JRuleEvent.java index 8a903ba4..c42ce91b 100644 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRuleThingStatusTrigger.java +++ b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleEvent.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.automation.jrule.trigger; +package org.openhab.automation.jrule.rules.event; /** - * The {@link JRuleThingStatusTrigger} Items + * The {@link JRuleEvent} * - * @author Gerhard Riegler - Initial contribution + * @author Joseph (Seaside) Hagberg - Initial contribution */ -public interface JRuleThingStatusTrigger { - String TRIGGER_CHANGED = "Changed"; +public abstract class JRuleEvent { + } diff --git a/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleItemEvent.java similarity index 62% rename from src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java rename to src/main/java/org/openhab/automation/jrule/rules/event/JRuleItemEvent.java index 004c5919..236241f9 100644 --- a/src/main/java/org/openhab/automation/jrule/rules/JRuleEvent.java +++ b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleItemEvent.java @@ -10,45 +10,29 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.automation.jrule.rules; +package org.openhab.automation.jrule.rules.event; +import org.openhab.automation.jrule.rules.JRuleEventState; import org.openhab.automation.jrule.rules.value.JRuleOnOffValue; import org.openhab.automation.jrule.rules.value.JRuleOpenClosedValue; import org.openhab.automation.jrule.rules.value.JRuleUpDownValue; /** - * The {@link JRuleEvent} + * The {@link JRuleItemEvent} * - * @author Joseph (Seaside) Hagberg - Initial contribution + * @author Robert Delbrück */ -public class JRuleEvent { - +public class JRuleItemEvent extends JRuleEvent { + private final String itemName; + private final String memberName; private final JRuleEventState state; private final JRuleEventState oldState; - private String event; - - private String memberName; - - private String itemName; - - private String channel; - - public JRuleEvent(String value) { - this(value, null, null, null); - } - public JRuleEvent(String value, String oldValue, String itemName, String memberName) { + public JRuleItemEvent(String itemName, String memberName, JRuleEventState state, JRuleEventState oldState) { this.itemName = itemName; - this.state = new JRuleEventState(value); - this.oldState = new JRuleEventState(oldValue); this.memberName = memberName; - } - - public JRuleEvent(String value, String channel, String event) { - this.state = new JRuleEventState(value); - this.oldState = null; - this.channel = channel; - this.event = event; + this.state = state; + this.oldState = oldState; } public JRuleEventState getState() { @@ -64,6 +48,10 @@ public String getValue() { return state.getValue(); } + public String getMemberName() { + return memberName; + } + @Deprecated public JRuleOnOffValue getValueAsOnOffValue() { return state.getValueAsOnOffValue(); @@ -89,25 +77,12 @@ public Integer getValueAsInteger() { return state.getValueAsInteger(); } - public String getMemberName() { - return memberName; - } - public String getItemName() { return itemName; } - public String getChannel() { - return channel; - } - - public String getThing() { - return channel; // TODO must refactor JRuleEvent to support all types of events (item, channel, thing) - } - @Override public String toString() { - return String.format("JRuleEvent [state=%s, oldState=%s, memberName=%s, itemName=%s, channel=%s, event=%s]", - state, oldState, memberName, itemName, channel, event); + return String.format("JRuleEvent [state=%s, oldState=%s, itemName=%s]", state, oldState, itemName); } } diff --git a/src/main/java/org/openhab/automation/jrule/rules/event/JRuleThingEvent.java b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleThingEvent.java new file mode 100644 index 00000000..ccddecd3 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleThingEvent.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules.event; + +/** + * The {@link JRuleThingEvent} + * + * @author Robert Delbrück + */ +public class JRuleThingEvent extends JRuleEvent { + private String thing; + private String status; + + public String getThing() { + return thing; + } + + public String getStatus() { + return status; + } + + public JRuleThingEvent(String thing, String status) { + this.thing = thing; + this.status = status; + } + + @Override + public String toString() { + return String.format("JRuleThingEvent [thing=%s, status=%s]", thing, status); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRuleCommonTrigger.java b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleTimerEvent.java similarity index 54% rename from src/main/java/org/openhab/automation/jrule/trigger/JRuleCommonTrigger.java rename to src/main/java/org/openhab/automation/jrule/rules/event/JRuleTimerEvent.java index 6858a033..ab7866fb 100644 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRuleCommonTrigger.java +++ b/src/main/java/org/openhab/automation/jrule/rules/event/JRuleTimerEvent.java @@ -10,15 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.automation.jrule.trigger; +package org.openhab.automation.jrule.rules.event; /** - * The {@link JRuleCommonTrigger} Items + * The {@link JRuleTimerEvent} * - * @author Gerhard Riegler - Initial contribution + * @author Robert Delbrück */ -public interface JRuleCommonTrigger { - String TRIGGER_CHANGED = "Changed"; - String TRIGGER_RECEIVED_COMMAND = "received command"; - String TRIGGER_RECEIVED_UPDATE = "received update"; +public class JRuleTimerEvent extends JRuleEvent { } diff --git a/src/main/java/org/openhab/automation/jrule/things/JRuleAbstractThing.java b/src/main/java/org/openhab/automation/jrule/things/JRuleAbstractThing.java index 2fd55b37..682da7f6 100644 --- a/src/main/java/org/openhab/automation/jrule/things/JRuleAbstractThing.java +++ b/src/main/java/org/openhab/automation/jrule/things/JRuleAbstractThing.java @@ -13,7 +13,6 @@ package org.openhab.automation.jrule.things; import org.openhab.automation.jrule.internal.handler.JRuleThingHandler; -import org.openhab.automation.jrule.trigger.JRuleThingStatusTrigger; /** * The {@link JRuleAbstractThing} represents a thing that is either a bridge, a bridged (sub thing of a bridge) or a @@ -21,7 +20,7 @@ * * @author Arne Seime - Initial contribution */ -public abstract class JRuleAbstractThing implements JRuleThingStatusTrigger { +public abstract class JRuleAbstractThing { private String thingUID; protected JRuleAbstractThing(String thingUID) { diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRuleContactTrigger.java b/src/main/java/org/openhab/automation/jrule/trigger/JRuleContactTrigger.java index 58efe860..2cc74c1d 100644 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRuleContactTrigger.java +++ b/src/main/java/org/openhab/automation/jrule/trigger/JRuleContactTrigger.java @@ -17,13 +17,7 @@ * * @author Joseph (Seaside) Hagberg - Initial contribution */ -public interface JRuleContactTrigger extends JRuleCommonTrigger { - String TRIGGER_RECEIVED_UPDATE_OPEN = "received update OPEN"; - String TRIGGER_RECEIVED_UPDATE_CLOSED = "received update CLOSED"; - String TRIGGER_CHANGED_FROM_OPEN_TO_CLOSED = "Changed from OPEN to CLOSED"; - String TRIGGER_CHANGED_FROM_OPEN = "Changed from OPEN"; - String TRIGGER_CHANGED_FROM_CLOSED = "Changed from CLOSED"; - String TRIGGER_CHANGED_TO_CLOSED = "Changed to CLOSED"; - String TRIGGER_CHANGED_TO_OPEN = "Changed to OPEN"; - String TRIGGER_CHANGED_FROM_CLOSED_TO_OPEN = "Changed from CLOSED to OPEN"; +public interface JRuleContactTrigger { + String OPEN = "OPEN"; + String CLOSED = "CLOSED"; } diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRuleDimmerTrigger.java b/src/main/java/org/openhab/automation/jrule/trigger/JRuleDimmerTrigger.java deleted file mode 100644 index 1b616f82..00000000 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRuleDimmerTrigger.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2010-2022 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.automation.jrule.trigger; - -/** - * The {@link JRuleDimmerTrigger} Items - * - * @author Gerhard Riegler - Initial contribution - */ -public interface JRuleDimmerTrigger extends JRuleCommonTrigger { - String TRIGGER_RECEIVED_UPDATE_ON = "received update ON"; - String TRIGGER_RECEIVED_UPDATE_OFF = "received update OFF"; - String TRIGGER_RECEIVED_COMMAND_ON = "received command ON"; - String TRIGGER_RECEIVED_COMMAND_OFF = "received command OFF"; - String TRIGGER_CHANGED_FROM_ON_TO_OFF = "Changed from ON to OFF"; - String TRIGGER_CHANGED_FROM_OFF_TO_ON = "Changed from OFF to ON"; - String TRIGGER_CHANGED_FROM_ON = "Changed from ON"; - String TRIGGER_CHANGED_FROM_OFF = "Changed from OFF"; - String TRIGGER_CHANGED_TO_OFF = "Changed to OFF"; - String TRIGGER_CHANGED_TO_ON = "Changed to ON"; -} diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRulePlayerTrigger.java b/src/main/java/org/openhab/automation/jrule/trigger/JRulePlayerTrigger.java index 3b926516..87acc00b 100644 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRulePlayerTrigger.java +++ b/src/main/java/org/openhab/automation/jrule/trigger/JRulePlayerTrigger.java @@ -17,15 +17,7 @@ * * @author Gerhard Riegler - Initial contribution */ -public interface JRulePlayerTrigger extends JRuleCommonTrigger { - String TRIGGER_RECEIVED_UPDATE_ON = "received update PLAY"; - String TRIGGER_RECEIVED_UPDATE_OFF = "received update PAUSE"; - String TRIGGER_RECEIVED_COMMAND_ON = "received command PLAY"; - String TRIGGER_RECEIVED_COMMAND_OFF = "received command PAUSE"; - String TRIGGER_CHANGED_FROM_ON_TO_OFF = "Changed from PLAY to PAUSE"; - String TRIGGER_CHANGED_FROM_OFF_TO_ON = "Changed from PAUSE to PLAY"; - String TRIGGER_CHANGED_FROM_ON = "Changed from PLAY"; - String TRIGGER_CHANGED_FROM_OFF = "Changed from PAUSE"; - String TRIGGER_CHANGED_TO_OFF = "Changed to PAUSE"; - String TRIGGER_CHANGED_TO_ON = "Changed to PLAY"; +public interface JRulePlayerTrigger { + String PLAY = "PLAY"; + String PAUSE = "PAUSE"; } diff --git a/src/main/java/org/openhab/automation/jrule/trigger/JRuleSwitchTrigger.java b/src/main/java/org/openhab/automation/jrule/trigger/JRuleSwitchTrigger.java index 37bc7b04..7a89fb13 100644 --- a/src/main/java/org/openhab/automation/jrule/trigger/JRuleSwitchTrigger.java +++ b/src/main/java/org/openhab/automation/jrule/trigger/JRuleSwitchTrigger.java @@ -17,15 +17,7 @@ * * @author Gerhard Riegler - Initial contribution */ -public interface JRuleSwitchTrigger extends JRuleCommonTrigger { - String TRIGGER_RECEIVED_UPDATE_ON = "received update ON"; - String TRIGGER_RECEIVED_UPDATE_OFF = "received update OFF"; - String TRIGGER_RECEIVED_COMMAND_ON = "received command ON"; - String TRIGGER_RECEIVED_COMMAND_OFF = "received command OFF"; - String TRIGGER_CHANGED_FROM_ON_TO_OFF = "Changed from ON to OFF"; - String TRIGGER_CHANGED_FROM_ON = "Changed from ON"; - String TRIGGER_CHANGED_FROM_OFF = "Changed from OFF"; - String TRIGGER_CHANGED_TO_OFF = "Changed to OFF"; - String TRIGGER_CHANGED_TO_ON = "Changed to ON"; - String TRIGGER_CHANGED_FROM_OFF_TO_ON = "Changed from OFF to ON"; +public interface JRuleSwitchTrigger { + String ON = "ON"; + String OFF = "OFF"; } diff --git a/src/test/java/org/openhab/binding/jrule/internal/cron/JRuleCronTest.java b/src/test/java/org/openhab/binding/jrule/internal/cron/JRuleCronTest.java deleted file mode 100644 index 7e440c27..00000000 --- a/src/test/java/org/openhab/binding/jrule/internal/cron/JRuleCronTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2010-2022 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.jrule.internal.cron; - -import java.time.ZonedDateTime; -import java.util.Date; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.openhab.automation.jrule.internal.cron.JRuleCronExpression; -import org.openhab.binding.jrule.internal.JRuleUtilTest; -import org.slf4j.LoggerFactory; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; - -/** - * The {@link JRuleUtilTest} - * - * @author Joseph (Seaside) Hagberg - Initial contribution - */ -public class JRuleCronTest { - - private static final Logger logger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); - - @BeforeEach - public void setUp() { - Logger rootLogger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); - rootLogger.setLevel(Level.ALL); - } - - @Test - public void testParseItemName() { - // sec min hour day month DAY - JRuleCronExpression expr = new JRuleCronExpression("4 10 21 6 8 *"); - ZonedDateTime nextTimeAfter = expr.nextTimeAfter(ZonedDateTime.now()); - Date futureTime = Date.from(nextTimeAfter.toInstant()); - long initialDelay = new Date(futureTime.getTime() - System.currentTimeMillis()).getTime(); - logger.debug("Schedule cron: {} initialDelay: {}", futureTime, initialDelay); - } -}