Refactor GenericEventTrigger and GenericEventCondition (#3299)
* rework GenericEventTrigger and GenericEventCondition fixes #3234 to make their interfaces and semantics match, as well as having a well defined (and useful) way of defining topic filters Signed-off-by: Cody Cutrer <cody@cutrer.us>pull/3369/head
parent
fd6e161d21
commit
474d24c62d
|
@ -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<String> 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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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": ""
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
|
@ -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 <a href=
|
||||
* "https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)">Java
|
||||
* Glob</a>
|
||||
*/
|
||||
public TopicGlobEventFilter(String topicGlob) {
|
||||
this.topicMatcher = FileSystems.getDefault().getPathMatcher("glob:" + topicGlob);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Event event) {
|
||||
return topicMatcher.matches(Paths.get(event.getTopic()));
|
||||
}
|
||||
}
|
|
@ -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")));
|
||||
}
|
||||
}
|
|
@ -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<? extends Action> 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<? extends Action> action = rule.getActions().stream()
|
||||
.filter(a -> "ItemPostCommandActionID".equals(a.getId())).findFirst();
|
||||
assertThat(action.isPresent(), is(true));
|
||||
|
|
|
@ -254,12 +254,11 @@ public class AutomationIntegrationTest extends JavaOSGiTest {
|
|||
public void assertThatARuleWithConnectionsIsExecuted() {
|
||||
logger.info("assert that a rule with connections is executed");
|
||||
Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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");
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
"id": "ItemStateChangeTriggerID",
|
||||
"type": "core.GenericEventTrigger",
|
||||
"configuration": {
|
||||
"eventSource": "myMotionItem",
|
||||
"eventTopic": "openhab/items/*",
|
||||
"eventTypes": "ItemStateEvent"
|
||||
"topic": "openhab/items/myMotionItem/*",
|
||||
"types": "ItemStateEvent"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -39,9 +39,8 @@
|
|||
"id": "ItemUpdateTrigger_1",
|
||||
"type": "core.GenericEventTrigger",
|
||||
"configuration": {
|
||||
"eventSource": "{{onItem}}",
|
||||
"eventTopic": "openhab/items/*",
|
||||
"eventTypes": "ItemStateEvent"
|
||||
"topic": "openhab/items/{{onItem}}/*",
|
||||
"types": "ItemStateEvent"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<String> ruleUIDs = new ArrayList<>();
|
||||
ruleUIDs.add("exampleSceneRule");
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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<? extends Condition> condition1 = rule.getConditions().stream()
|
||||
.filter(c -> "condition".equals(c.getId())).findFirst();
|
||||
assertThat(condition1.isPresent(), is(true));
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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")));
|
||||
|
|
Loading…
Reference in New Issue