diff --git a/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventConditionHandler.java b/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventConditionHandler.java index 956afab4cb..bb7c35c3ae 100644 --- a/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventConditionHandler.java +++ b/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventConditionHandler.java @@ -13,12 +13,15 @@ package org.openhab.core.automation.internal.module.handler; import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.automation.Condition; import org.openhab.core.automation.handler.BaseConditionModuleHandler; import org.openhab.core.events.Event; +import org.openhab.core.events.TopicGlobEventFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,50 +30,69 @@ import org.slf4j.LoggerFactory; * * @author Benedikt Niehues - Initial contribution * @author Kai Kreuzer - refactored and simplified customized module handling + * @author Cody Cutrer - refactored to match configuration and semantics of GenericEventTriggerHandler */ @NonNullByDefault public class GenericEventConditionHandler extends BaseConditionModuleHandler { public static final String MODULETYPE_ID = "core.GenericEventCondition"; - private static final String TOPIC = "topic"; - private static final String EVENTTYPE = "eventType"; - private static final String SOURCE = "source"; - private static final String PAYLOAD = "payload"; + public static final String CFG_TOPIC = "topic"; + public static final String CFG_TYPES = "types"; + public static final String CFG_SOURCE = "source"; + public static final String CFG_PAYLOAD = "payload"; public final Logger logger = LoggerFactory.getLogger(GenericEventConditionHandler.class); + private final String source; + private final @Nullable TopicGlobEventFilter topicFilter; + private final Set types; + private final @Nullable Pattern payloadPattern; + public GenericEventConditionHandler(Condition module) { super(module); - } - private boolean isConfiguredAndMatches(String keyParam, @Nullable String value) { - Object mo = module.getConfiguration().get(keyParam); - String configValue = mo != null && mo instanceof String ? (String) mo : null; - if (configValue != null) { - if (PAYLOAD.equals(keyParam)) { - // automatically adding wildcards only for payload matching - configValue = configValue.startsWith("*") ? configValue : ".*" + configValue; - configValue = configValue.endsWith("*") ? configValue : configValue + ".*"; - } - if (value != null) { - return value.matches(configValue); - } else { - return false; - } + this.source = (String) module.getConfiguration().get(CFG_SOURCE); + String topic = (String) module.getConfiguration().get(CFG_TOPIC); + if (!topic.isBlank()) { + topicFilter = new TopicGlobEventFilter(topic); } else { - return true; + topicFilter = null; + } + if (module.getConfiguration().get(CFG_TYPES) != null) { + this.types = Set.of(((String) module.getConfiguration().get(CFG_TYPES)).split(",")); + } else { + this.types = Set.of(); + } + String payload = (String) module.getConfiguration().get(CFG_PAYLOAD); + if (!payload.isBlank()) { + payloadPattern = Pattern.compile(payload); + } else { + payloadPattern = null; } } @Override public boolean isSatisfied(Map inputs) { - Event event = inputs.get("event") != null ? (Event) inputs.get("event") : null; - if (event != null) { - return isConfiguredAndMatches(TOPIC, event.getTopic()) && isConfiguredAndMatches(SOURCE, event.getSource()) - && isConfiguredAndMatches(PAYLOAD, event.getPayload()) - && isConfiguredAndMatches(EVENTTYPE, event.getType()); + Event event = inputs.get("event") instanceof Event ? (Event) inputs.get("event") : null; + if (event == null) { + return false; } - return false; + if (!types.isEmpty() && !types.contains(event.getType())) { + return false; + } + TopicGlobEventFilter localTopicFilter = topicFilter; + if (localTopicFilter != null && !localTopicFilter.apply(event)) { + return false; + } + if (!source.isEmpty() && !source.equals(event.getSource())) { + return false; + } + Pattern localPayloadPattern = payloadPattern; + if (localPayloadPattern != null && !localPayloadPattern.matcher(event.getPayload()).find()) { + return false; + } + + return true; } } diff --git a/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandler.java b/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandler.java index 4d9517c73e..6577817aee 100644 --- a/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandler.java +++ b/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandler.java @@ -15,6 +15,7 @@ package org.openhab.core.automation.internal.module.handler; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -25,6 +26,7 @@ import org.openhab.core.automation.handler.TriggerHandlerCallback; import org.openhab.core.events.Event; import org.openhab.core.events.EventFilter; import org.openhab.core.events.EventSubscriber; +import org.openhab.core.events.TopicGlobEventFilter; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; @@ -41,37 +43,51 @@ import org.slf4j.LoggerFactory; * * @author Benedikt Niehues - Initial contribution * @author Kai Kreuzer - refactored and simplified customized module handling + * @author Cody Cutrer - refactored to match configuration and semantics of GenericConditionTriggerHandler */ @NonNullByDefault public class GenericEventTriggerHandler extends BaseTriggerModuleHandler implements EventSubscriber, EventFilter { public static final String MODULE_TYPE_ID = "core.GenericEventTrigger"; - private static final String CFG_EVENT_TOPIC = "eventTopic"; - private static final String CFG_EVENT_SOURCE = "eventSource"; - private static final String CFG_EVENT_TYPES = "eventTypes"; + public static final String CFG_TOPIC = "topic"; + public static final String CFG_SOURCE = "source"; + public static final String CFG_TYPES = "types"; + public static final String CFG_PAYLOAD = "payload"; private final Logger logger = LoggerFactory.getLogger(GenericEventTriggerHandler.class); private final String source; - private String topic; + private final @Nullable TopicGlobEventFilter topicFilter; private final Set types; - private final BundleContext bundleContext; + private final @Nullable Pattern payloadPattern; private @Nullable ServiceRegistration eventSubscriberRegistration; public GenericEventTriggerHandler(Trigger module, BundleContext bundleContext) { super(module); - this.source = (String) module.getConfiguration().get(CFG_EVENT_SOURCE); - this.topic = (String) module.getConfiguration().get(CFG_EVENT_TOPIC); - if (module.getConfiguration().get(CFG_EVENT_TYPES) != null) { - this.types = Set.of(((String) module.getConfiguration().get(CFG_EVENT_TYPES)).split(",")); + this.source = (String) module.getConfiguration().get(CFG_SOURCE); + String topic = (String) module.getConfiguration().get(CFG_TOPIC); + if (!topic.isBlank()) { + topicFilter = new TopicGlobEventFilter(topic); + } else { + topicFilter = null; + } + if (module.getConfiguration().get(CFG_TYPES) != null) { + this.types = Set.of(((String) module.getConfiguration().get(CFG_TYPES)).split(",")); } else { this.types = Set.of(); } - this.bundleContext = bundleContext; - eventSubscriberRegistration = this.bundleContext.registerService(EventSubscriber.class.getName(), this, null); - logger.trace("Registered EventSubscriber: Topic: {} Type: {} Source: {}", topic, types, source); + String payload = (String) module.getConfiguration().get(CFG_PAYLOAD); + if (!payload.isBlank()) { + payloadPattern = Pattern.compile(payload); + } else { + payloadPattern = null; + } + + eventSubscriberRegistration = bundleContext.registerService(EventSubscriber.class.getName(), this, null); + logger.trace("Registered EventSubscriber: Topic: {} Type: {} Source: {} Payload: {}", topic, types, source, + payload); } @Override @@ -100,21 +116,6 @@ public class GenericEventTriggerHandler extends BaseTriggerModuleHandler impleme } } - /** - * @return the topic - */ - public String getTopic() { - return topic; - } - - /** - * @param topic - * the topic to set - */ - public void setTopic(String topic) { - this.topic = topic; - } - /** * do the cleanup: unregistering eventSubscriber... */ @@ -129,7 +130,20 @@ public class GenericEventTriggerHandler extends BaseTriggerModuleHandler impleme @Override public boolean apply(Event event) { - logger.trace("->FILTER: {}:{}", event.getTopic(), source); - return event.getTopic().contains(source); + logger.trace("->FILTER: {}: {}", event.getTopic(), source); + + TopicGlobEventFilter localTopicFilter = topicFilter; + if (localTopicFilter != null && !topicFilter.apply(event)) { + return false; + } + if (!source.isEmpty() && !source.equals(event.getSource())) { + return false; + } + Pattern localPayloadPattern = payloadPattern; + if (localPayloadPattern != null && !localPayloadPattern.matcher(event.getPayload()).find()) { + return false; + } + + return true; } } diff --git a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericConditions.json b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericConditions.json index c607a87855..ed9684a1eb 100644 --- a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericConditions.json +++ b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericConditions.json @@ -9,26 +9,34 @@ { "name": "topic", "type": "TEXT", - "description": "topic should match", - "required": false - }, - { - "name": "eventType", - "type": "TEXT", - "description": "eventType should match", - "required": false + "label": "Topic", + "description": "the topic, as a file-system style glob (*, ** and {} operators). will match all events if empty", + "required": true, + "default": "" }, { "name": "source", "type": "TEXT", - "description": "source should match", - "required": false + "label": "Source", + "description": "the source of the event (e.g. org.openhab.core.expire, etc.). will match all events if empty", + "required": true, + "default": "" + }, + { + "name": "types", + "type": "TEXT", + "label": "Event Type", + "description": "the event type the trigger should listen to. multiple types can be specified comma-separated. will match all events if empty", + "required": true, + "default": "" }, { "name": "payload", "type": "TEXT", - "description": "payload should match", - "required": false + "label": "Event Payload", + "description": "A regex to match the event's serialized payload. will match all events if empty", + "required": true, + "default": "" } ], "inputs": [ @@ -36,7 +44,7 @@ "name": "event", "type": "org.openhab.core.events.Event", "label": "Event", - "description": "The events which was sent.", + "description": "The event which was sent.", "required": true } ] diff --git a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericTriggers.json b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericTriggers.json index 57cc345c97..23a3754a1a 100644 --- a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericTriggers.json +++ b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/GenericTriggers.json @@ -7,26 +7,34 @@ "visibility": "HIDDEN", "configDescriptions": [ { - "name": "eventTopic", + "name": "topic", "type": "TEXT", "label": "Topic", - "description": "This is the topic, the trigger will listen to: >>openhab/*<<", - "required": true, - "default": "openhab/*" - }, - { - "name": "eventSource", - "type": "TEXT", - "label": "Source", - "description": "This is the source of the event (eg. item name)", + "description": "the topic, as a file-system style glob (*, ** and {} operators). will match all events if empty", "required": true, "default": "" }, { - "name": "eventTypes", + "name": "source", + "type": "TEXT", + "label": "Source", + "description": "the source of the event (e.g. org.openhab.core.expire, etc.). will match all events if empty", + "required": true, + "default": "" + }, + { + "name": "types", "type": "TEXT", "label": "Event Type", - "description": "the event type, the trigger should listen to. Multiple types can be specified comma-separated", + "description": "the event type the trigger should listen to. multiple types can be specified comma-separated. will match all events if empty", + "required": true, + "default": "" + }, + { + "name": "payload", + "type": "TEXT", + "label": "Event Payload", + "description": "A regex to match the event's serialized payload. will match all events if empty", "required": true, "default": "" } diff --git a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/i18n/automation.properties b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/i18n/automation.properties index 080028dbc3..74412d8527 100644 --- a/bundles/org.openhab.core.automation/src/main/resources/OH-INF/i18n/automation.properties +++ b/bundles/org.openhab.core.automation/src/main/resources/OH-INF/i18n/automation.properties @@ -26,23 +26,29 @@ module-type.core.GenericCompareCondition.input.input.description = The input whi module-type.core.GenericEventCondition.label = Event Condition module-type.core.GenericEventCondition.description = Condition for events -module-type.core.GenericEventCondition.config.topic.description = topic should match -module-type.core.GenericEventCondition.config.eventType.description = eventType should match -module-type.core.GenericEventCondition.config.source.description = source should match -module-type.core.GenericEventCondition.config.payload.description = payload should match +module-type.core.GenericEventCondition.config.topic.label = Topic +module-type.core.GenericEventCondition.config.topic.description = the topic, as a file-system style glob (*, ** and {} operators). will match all events if empty +module-type.core.GenericEventCondition.config.source.label = Source +module-type.core.GenericEventCondition.config.source.description = the source of the event (e.g. org.openhab.core.expire, etc.). will match all events if empty +module-type.core.GenericEventCondition.config.types.label = Event Type +module-type.core.GenericEventCondition.config.types.description = the event type the trigger should listen to. multiple types can be specified comma-separated. will match all events if empty +module-type.core.GenericEventCondition.config.payload.label = Event Payload +module-type.core.GenericEventCondition.config.payload.description = A regex to match the event's serialized payload. will match all events if empty module-type.core.GenericEventCondition.input.event.label = Event -module-type.core.GenericEventCondition.input.event.description = The events which was sent. +module-type.core.GenericEventCondition.input.event.description = The event which was sent. # core.GenericEventTrigger module-type.core.GenericEventTrigger.label = Basic Event Trigger module-type.core.GenericEventTrigger.description = Triggers Rules on Events -module-type.core.GenericEventTrigger.config.eventTopic.label = Topic -module-type.core.GenericEventTrigger.config.eventTopic.description = This is the topic, the trigger will listen to: >>openhab/*<< -module-type.core.GenericEventTrigger.config.eventSource.label = Source -module-type.core.GenericEventTrigger.config.eventSource.description = This is the source of the event (eg. item name) -module-type.core.GenericEventTrigger.config.eventTypes.label = Event Type -module-type.core.GenericEventTrigger.config.eventTypes.description = the event type, the trigger should listen to. Multiple types can be specified comma-separated +module-type.core.GenericEventTrigger.config.topic.label = Topic +module-type.core.GenericEventTrigger.config.topic.description = the topic, as a file-system style glob (*, ** and {} operators). will match all events if empty +module-type.core.GenericEventTrigger.config.source.label = Source +module-type.core.GenericEventTrigger.config.source.description = the source of the event (e.g. org.openhab.core.expire, etc.). will match all events if empty +module-type.core.GenericEventTrigger.config.types.label = Event Type +module-type.core.GenericEventTrigger.config.types.description = the event type the trigger should listen to. multiple types can be specified comma-separated. will match all events if empty +module-type.core.GenericEventTrigger.config.payload.label = Event Payload +module-type.core.GenericEventTrigger.config.payload.description = A regex to match the event's serialized payload. will match all events if empty module-type.core.GenericEventTrigger.output.event.label = Event module-type.core.GenericEventTrigger.output.event.description = The events which was sent. @@ -382,6 +388,10 @@ module-type.timer.DateTimeTrigger.label = it is a date and time specified in an module-type.timer.DateTimeTrigger.description = Triggers at a time specified in an item module-type.timer.DateTimeTrigger.config.itemName.label = Item module-type.timer.DateTimeTrigger.config.itemName.description = the name of the item +module-type.timer.DateTimeTrigger.config.timeOnly.label = Time only +module-type.timer.DateTimeTrigger.config.timeOnly.description = Specifies whether only the time of the item should be compared or the date and time. +module-type.timer.DateTimeTrigger.config.timeOnly.option.true = Yes +module-type.timer.DateTimeTrigger.config.timeOnly.option.false = No # timer.DayOfWeekCondition diff --git a/bundles/org.openhab.core.automation/src/test/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandlerTest.java b/bundles/org.openhab.core.automation/src/test/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandlerTest.java new file mode 100644 index 0000000000..8c86e80338 --- /dev/null +++ b/bundles/org.openhab.core.automation/src/test/java/org/openhab/core/automation/internal/module/handler/GenericEventTriggerHandlerTest.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2010-2023 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.core.automation.internal.module.handler; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openhab.core.automation.Trigger; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.events.Event; +import org.osgi.framework.BundleContext; + +/** + * Basic test cases for {@link GenericEventTriggerHandler} + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +class GenericEventTriggerHandlerTest { + private @NonNullByDefault({}) GenericEventTriggerHandler handler; + private @NonNullByDefault({}) Trigger moduleMock; + private @NonNullByDefault({}) BundleContext contextMock; + + public Event createEvent(String topic, String source) { + Event event = mock(Event.class); + when(event.getTopic()).thenReturn(topic); + when(event.getSource()).thenReturn(source); + return event; + } + + @BeforeEach + public void setUp() { + moduleMock = mock(Trigger.class); + contextMock = mock(BundleContext.class); + } + + @Test + public void testTopicFilterIsGlobbed() { + when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(GenericEventTriggerHandler.CFG_TOPIC, + "openhab/items/*/command", GenericEventTriggerHandler.CFG_SOURCE, "", + GenericEventTriggerHandler.CFG_TYPES, "", GenericEventTriggerHandler.CFG_PAYLOAD, ""))); + handler = new GenericEventTriggerHandler(moduleMock, contextMock); + + assertTrue(handler.apply(createEvent("openhab/items/myMotion1/command", "Source"))); + } + + @Test + public void testsSourceFilterIsExactMatch() { + when(moduleMock.getConfiguration()).thenReturn(new Configuration( + Map.of(GenericEventTriggerHandler.CFG_TOPIC, "", GenericEventTriggerHandler.CFG_SOURCE, "ExactSource", + GenericEventTriggerHandler.CFG_TYPES, "", GenericEventTriggerHandler.CFG_PAYLOAD, ""))); + handler = new GenericEventTriggerHandler(moduleMock, contextMock); + + assertTrue(handler.apply(createEvent("openhab/items/myMotion1/command", "ExactSource"))); + } + + @Test + public void testsSourceFilterDoesntMatchSubstring() { + when(moduleMock.getConfiguration()).thenReturn(new Configuration( + Map.of(GenericEventTriggerHandler.CFG_TOPIC, "", GenericEventTriggerHandler.CFG_SOURCE, "Source", + GenericEventTriggerHandler.CFG_TYPES, "", GenericEventTriggerHandler.CFG_PAYLOAD, ""))); + handler = new GenericEventTriggerHandler(moduleMock, contextMock); + + assertFalse(handler.apply(createEvent("openhab/items/myMotion1/command", "ExactSource"))); + } +} diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/events/TopicGlobEventFilter.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/events/TopicGlobEventFilter.java new file mode 100644 index 0000000000..cce421ff13 --- /dev/null +++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/events/TopicGlobEventFilter.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2023 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.core.events; + +import java.nio.file.FileSystems; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link TopicGlobalEventFilter} is a default openHAB {@link EventFilter} implementation that ensures filtering + * of events based on an event topic. + * + * The syntax for the filter is the glob syntax documented at + * https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String) + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class TopicGlobEventFilter implements EventFilter { + + private final PathMatcher topicMatcher; + + /** + * Constructs a new topic event filter. + * + * @param topicGlob the glob + * @see Java + * Glob + */ + public TopicGlobEventFilter(String topicGlob) { + this.topicMatcher = FileSystems.getDefault().getPathMatcher("glob:" + topicGlob); + } + + @Override + public boolean apply(Event event) { + return topicMatcher.matches(Paths.get(event.getTopic())); + } +} diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/events/TopicGlobEventFilterTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/events/TopicGlobEventFilterTest.java new file mode 100644 index 0000000000..2ddc98c50d --- /dev/null +++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/events/TopicGlobEventFilterTest.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2023 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.core.events; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * {@link TopicGlobEventFilterTest} tests the {@link org.openhab.core.events.TopicGlobEventFilter}. + * + * @author Cody Cutrer - Initial contribution + */ +@NonNullByDefault +public class TopicGlobEventFilterTest { + public Event createEvent(String topic) { + Event event = mock(Event.class); + when(event.getTopic()).thenReturn(topic); + return event; + } + + @Test + public void testBasic() throws Exception { + var filter = new TopicGlobEventFilter("openhab/**"); + assertTrue(filter.apply(createEvent("openhab/items/a"))); + assertFalse(filter.apply(createEvent("somewhereElse"))); + assertFalse(filter.apply(createEvent("preopenhab/items/apost"))); + + // * does not match multiple sub-directories + filter = new TopicGlobEventFilter("openhab/*"); + assertFalse(filter.apply(createEvent("openhab/items/a"))); + + // * can be used in the middle of a path component + filter = new TopicGlobEventFilter("openhab/it*s/*"); + assertTrue(filter.apply(createEvent("openhab/items/a"))); + assertFalse(filter.apply(createEvent("openhab/things/a"))); + } +} diff --git a/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationJsonTest.java b/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationJsonTest.java index a33972b430..33362852a6 100644 --- a/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationJsonTest.java +++ b/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationJsonTest.java @@ -247,9 +247,10 @@ public class AutomationIntegrationJsonTest extends JavaOSGiTest { .filter(t -> "ItemStateChangeTriggerID".equals(t.getId())).findFirst(); assertThat(trigger.isPresent(), is(true)); assertThat(trigger.get().getTypeUID(), is("core.GenericEventTrigger")); - assertThat(trigger.get().getConfiguration().get("eventSource"), is("myMotionItem")); - assertThat(trigger.get().getConfiguration().get("eventTopic"), is("openhab/items/*")); - assertThat(trigger.get().getConfiguration().get("eventTypes"), is("ItemStateEvent")); + assertThat(trigger.get().getConfiguration().get("source"), is("")); + assertThat(trigger.get().getConfiguration().get("topic"), is("openhab/items/myMotionItem/*")); + assertThat(trigger.get().getConfiguration().get("types"), is("ItemStateEvent")); + assertThat(trigger.get().getConfiguration().get("payload"), is("")); Optional action = rule.getActions().stream() .filter(a -> "ItemPostCommandActionID".equals(a.getId())).findFirst(); assertThat(action.isPresent(), is(true)); @@ -287,8 +288,10 @@ public class AutomationIntegrationJsonTest extends JavaOSGiTest { .filter(t -> "ItemStateChangeTriggerID".equals(t.getId())).findFirst(); assertThat(trigger.isPresent(), is(true)); assertThat(trigger.get().getTypeUID(), is("core.GenericEventTrigger")); - assertThat(trigger.get().getConfiguration().get("eventTopic"), is("openhab/items/*")); - assertThat(trigger.get().getConfiguration().get("eventTypes"), is("ItemStateEvent")); + assertThat(trigger.get().getConfiguration().get("source"), is("")); + assertThat(trigger.get().getConfiguration().get("topic"), is("openhab/items/myMotionItem/*")); + assertThat(trigger.get().getConfiguration().get("types"), is("ItemStateEvent")); + assertThat(trigger.get().getConfiguration().get("payload"), is("")); Optional action = rule.getActions().stream() .filter(a -> "ItemPostCommandActionID".equals(a.getId())).findFirst(); assertThat(action.isPresent(), is(true)); diff --git a/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationTest.java b/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationTest.java index 730eb8ded5..d8bc15abec 100644 --- a/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationTest.java +++ b/itests/org.openhab.core.automation.integration.tests/src/main/java/org/openhab/core/automation/integration/test/AutomationIntegrationTest.java @@ -254,12 +254,11 @@ public class AutomationIntegrationTest extends JavaOSGiTest { public void assertThatARuleWithConnectionsIsExecuted() { logger.info("assert that a rule with connections is executed"); Map params = new HashMap<>(); - params.put("eventSource", "myMotionItem3"); - params.put("eventTopic", "openhab/*"); - params.put("eventTypes", "ItemStateEvent"); + params.put("topic", "openhab/items/myMotionItem3/*"); + params.put("types", "ItemStateEvent"); Configuration triggerConfig = new Configuration(params); params = new HashMap<>(); - params.put("eventTopic", "openhab/*"); + params.put("topic", "openhab/**"); Configuration condition1Config = new Configuration(params); params = new HashMap<>(); params.put("itemName", "myLampItem3"); @@ -321,7 +320,7 @@ public class AutomationIntegrationTest extends JavaOSGiTest { params.put("eventTypes", "ItemStateEvent"); Configuration triggerConfig = new Configuration(params); params = new HashMap<>(); - params.put("topic", "openhab/*"); + params.put("topic", "openhab/**"); Configuration condition1Config = new Configuration(params); params = new HashMap<>(); params.put("itemName", "myLampItem3"); @@ -404,7 +403,7 @@ public class AutomationIntegrationTest extends JavaOSGiTest { @Test public void assertThatRuleNowMethodExecutesActionsOfTheRule() throws ItemNotFoundException { - Configuration triggerConfig = new Configuration(Map.of("eventTopic", "runNowEventTopic/*")); + Configuration triggerConfig = new Configuration(Map.of("topic", "runNowEventTopic/*")); Map params = new HashMap<>(); params.put("itemName", "myLampItem3"); params.put("command", "TOGGLE"); @@ -467,7 +466,7 @@ public class AutomationIntegrationTest extends JavaOSGiTest { @Test public void assertThatRuleCanBeUpdated() throws ItemNotFoundException { - Configuration triggerConfig = new Configuration(Map.of("eventTopic", "runNowEventTopic/*")); + Configuration triggerConfig = new Configuration(Map.of("topic", "runNowEventTopic/*")); Map params = new HashMap<>(); params.put("itemName", "myLampItem3"); params.put("command", "ON"); @@ -594,9 +593,8 @@ public class AutomationIntegrationTest extends JavaOSGiTest { logger.info("assert a rule added by api is executed as expected"); // Creation of RULE Map params = new HashMap<>(); - params.put("eventSource", "myMotionItem2"); - params.put("eventTopic", "openhab/*"); - params.put("eventTypes", "ItemStateEvent"); + params.put("topic", "openhab/items/myMotionItem2/*"); + params.put("types", "ItemStateEvent"); Configuration triggerConfig = new Configuration(params); params = new HashMap<>(); params.put("itemName", "myLampItem2"); @@ -854,9 +852,8 @@ public class AutomationIntegrationTest extends JavaOSGiTest { int rand = new Random().nextInt(); Map configs = new HashMap<>(); - configs.put("eventSource", "myMotionItem2"); - configs.put("eventTopic", "openhab/*"); - configs.put("eventTypes", "ItemStateEvent"); + configs.put("topic", "openhab/items/myMotionItem2/*"); + configs.put("types", "ItemStateEvent"); Configuration triggerConfig = new Configuration(configs); configs = new HashMap<>(); configs.put("itemName", "myLampItem2"); @@ -880,9 +877,8 @@ public class AutomationIntegrationTest extends JavaOSGiTest { logger.info("assert a rule with generic condition works"); // Creation of RULE Map configs = new HashMap<>(); - configs.put("eventSource", "myMotionItem5"); - configs.put("eventTopic", "openhab/*"); - configs.put("eventTypes", "ItemStateEvent"); + configs.put("topic", "openhab/items/myMotionItem5/*"); + configs.put("types", "ItemStateEvent"); Configuration triggerConfig = new Configuration(configs); configs = new HashMap<>(); configs.put("operator", "matches"); diff --git a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/rules/ItemRuleDefinition.json b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/rules/ItemRuleDefinition.json index 2980189e94..f19ef5ba7f 100644 --- a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/rules/ItemRuleDefinition.json +++ b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/rules/ItemRuleDefinition.json @@ -17,9 +17,8 @@ "id": "ItemStateChangeTriggerID", "type": "core.GenericEventTrigger", "configuration": { - "eventSource": "myMotionItem", - "eventTopic": "openhab/items/*", - "eventTypes": "ItemStateEvent" + "topic": "openhab/items/myMotionItem/*", + "types": "ItemStateEvent" } } ], diff --git a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/SimpleTestTemplate.json b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/SimpleTestTemplate.json index ff595c0b06..3b4058ef55 100644 --- a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/SimpleTestTemplate.json +++ b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/SimpleTestTemplate.json @@ -39,9 +39,8 @@ "id": "ItemUpdateTrigger_1", "type": "core.GenericEventTrigger", "configuration": { - "eventSource": "{{onItem}}", - "eventTopic": "openhab/items/*", - "eventTypes": "ItemStateEvent" + "topic": "openhab/items/{{onItem}}/*", + "types": "ItemStateEvent" } } ], diff --git a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/TestTemplateRuleWithReferences.json b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/TestTemplateRuleWithReferences.json index 3fc9a904b7..1fd99be383 100644 --- a/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/TestTemplateRuleWithReferences.json +++ b/itests/org.openhab.core.automation.integration.tests/src/main/resources/OH-INF/automation/templates/TestTemplateRuleWithReferences.json @@ -29,9 +29,8 @@ "id": "ItemStateChangeTriggerID", "type": "core.GenericEventTrigger", "configuration": { - "eventSource": "{{triggerItem}}", - "eventTopic": "openhab/items/*", - "eventTypes": "ItemStateEvent" + "topic": "openhab/items/{{triggerItem}}/*", + "types": "ItemStateEvent" } } ], @@ -41,7 +40,7 @@ "type": "core.GenericEventCondition", "configuration": { "topic": "openhab/items/{{triggerItem}}/state", - "payload": ".*ON.*" + "payload": "ON" }, "inputs": { "event": "ItemStateChangeTriggerID.event" diff --git a/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RunRuleModuleTest.java b/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RunRuleModuleTest.java index ec95124c4a..3ba71f090e 100644 --- a/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RunRuleModuleTest.java +++ b/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RunRuleModuleTest.java @@ -113,8 +113,7 @@ public class RunRuleModuleTest extends JavaOSGiTest { private Rule createOuterRule() { final Configuration outerRuleTriggerConfig = new Configuration( - Map.ofEntries(entry("eventSource", "ruleTrigger"), entry("eventTopic", "openhab/*"), - entry("eventTypes", "ItemStateEvent"))); + Map.ofEntries(entry("topic", "openhab/items/ruleTrigger/*"), entry("types", "ItemStateEvent"))); final List ruleUIDs = new ArrayList<>(); ruleUIDs.add("exampleSceneRule"); diff --git a/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RuntimeRuleTest.java b/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RuntimeRuleTest.java index 9755fd409a..c7fddef1b7 100644 --- a/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RuntimeRuleTest.java +++ b/itests/org.openhab.core.automation.module.core.tests/src/main/java/org/openhab/core/automation/internal/module/RuntimeRuleTest.java @@ -142,8 +142,8 @@ public class RuntimeRuleTest extends JavaOSGiTest { @Test public void itemStateUpdatedBySimpleRule() throws ItemNotFoundException, InterruptedException { - final Configuration triggerConfig = new Configuration(Map.ofEntries(entry("eventSource", "myMotionItem2"), - entry("eventTopic", "openhab/*"), entry("eventTypes", "ItemStateEvent"))); + final Configuration triggerConfig = new Configuration( + Map.ofEntries(entry("topic", "openhab/items/myMotionItem2/*"), entry("types", "ItemStateEvent"))); final Configuration actionConfig = new Configuration( Map.ofEntries(entry("itemName", "myLampItem2"), entry("command", "ON"))); final Rule rule = RuleBuilder.create("myRule21" + new Random().nextInt()) diff --git a/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/DemoScriptRule.json b/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/DemoScriptRule.json index 04f11da357..5eda62fa74 100644 --- a/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/DemoScriptRule.json +++ b/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/DemoScriptRule.json @@ -8,9 +8,8 @@ "id": "trigger", "type": "core.GenericEventTrigger", "configuration": { - "eventSource": "MyTrigger", - "eventTopic": "openhab/items/MyTrigger/state", - "eventTypes": "ItemStateEvent" + "topic": "openhab/items/MyTrigger/state", + "types": "ItemStateEvent" } } ], diff --git a/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/ScriptRuleOSGiTest.java b/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/ScriptRuleOSGiTest.java index ac702eae5b..4ec523772c 100644 --- a/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/ScriptRuleOSGiTest.java +++ b/itests/org.openhab.core.automation.module.script.tests/src/main/java/org/openhab/core/automation/module/script/internal/defaultscope/ScriptRuleOSGiTest.java @@ -118,9 +118,8 @@ public class ScriptRuleOSGiTest extends JavaOSGiTest { .findFirst(); assertThat(trigger.isPresent(), is(true)); assertThat(trigger.get().getTypeUID(), is("core.GenericEventTrigger")); - assertThat(trigger.get().getConfiguration().get("eventSource"), is("MyTrigger")); - assertThat(trigger.get().getConfiguration().get("eventTopic"), is("openhab/items/MyTrigger/state")); - assertThat(trigger.get().getConfiguration().get("eventTypes"), is("ItemStateEvent")); + assertThat(trigger.get().getConfiguration().get("topic"), is("openhab/items/MyTrigger/state")); + assertThat(trigger.get().getConfiguration().get("types"), is("ItemStateEvent")); Optional condition1 = rule.getConditions().stream() .filter(c -> "condition".equals(c.getId())).findFirst(); assertThat(condition1.isPresent(), is(true)); diff --git a/itests/org.openhab.core.automation.module.script.tests/src/main/resources/OH-INF/automation/rules/DemoScriptRule.json b/itests/org.openhab.core.automation.module.script.tests/src/main/resources/OH-INF/automation/rules/DemoScriptRule.json index 04f11da357..5eda62fa74 100644 --- a/itests/org.openhab.core.automation.module.script.tests/src/main/resources/OH-INF/automation/rules/DemoScriptRule.json +++ b/itests/org.openhab.core.automation.module.script.tests/src/main/resources/OH-INF/automation/rules/DemoScriptRule.json @@ -8,9 +8,8 @@ "id": "trigger", "type": "core.GenericEventTrigger", "configuration": { - "eventSource": "MyTrigger", - "eventTopic": "openhab/items/MyTrigger/state", - "eventTypes": "ItemStateEvent" + "topic": "openhab/items/MyTrigger/state", + "types": "ItemStateEvent" } } ], diff --git a/itests/org.openhab.core.automation.tests/src/main/java/org/openhab/core/automation/event/RuleEventTest.java b/itests/org.openhab.core.automation.tests/src/main/java/org/openhab/core/automation/event/RuleEventTest.java index a76e37b88e..42d3e4aa8d 100644 --- a/itests/org.openhab.core.automation.tests/src/main/java/org/openhab/core/automation/event/RuleEventTest.java +++ b/itests/org.openhab.core.automation.tests/src/main/java/org/openhab/core/automation/event/RuleEventTest.java @@ -127,8 +127,8 @@ public class RuleEventTest extends JavaOSGiTest { registerService(ruleEventHandler); // Creation of RULE - Configuration triggerConfig = new Configuration(Map.ofEntries(entry("eventSource", "myMotionItem2"), - entry("eventTopic", "openhab/*"), entry("eventTypes", "ItemStateEvent"))); + Configuration triggerConfig = new Configuration( + Map.ofEntries(entry("topic", "openhab/items/myMotionItem2/*"), entry("types", "ItemStateEvent"))); Configuration actionConfig = new Configuration( Map.ofEntries(entry("itemName", "myLampItem2"), entry("command", "ON")));