Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.events.TopicEventFilter;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.events.ChannelTriggeredEvent;
import org.osgi.framework.BundleContext;
Expand All @@ -35,6 +36,7 @@
* This is a ModuleHandler implementation for trigger channels with specific events
*
* @author Stefan Triller - Initial contribution
* @author Jimmy Tanagra - Add support for wildcard channel matching
*/
@NonNullByDefault
public class ChannelEventTriggerHandler extends BaseTriggerModuleHandler implements EventSubscriber, EventFilter {
Expand All @@ -48,16 +50,33 @@ public class ChannelEventTriggerHandler extends BaseTriggerModuleHandler impleme
private final Logger logger = LoggerFactory.getLogger(ChannelEventTriggerHandler.class);

private @Nullable final String eventOnChannel;
private final ChannelUID channelUID;
private final @Nullable ChannelUID channelUID;
private final @Nullable TopicEventFilter eventTopicFilter;
private final Set<String> types;
private final BundleContext bundleContext;
private final ServiceRegistration<?> eventSubscriberRegistration;

public ChannelEventTriggerHandler(Trigger module, BundleContext bundleContext) {
super(module);

String cfgChannel = (String) module.getConfiguration().get(CFG_CHANNEL);

if (cfgChannel == null || cfgChannel.isBlank()) {
throw new IllegalArgumentException("Configuration must contain a non-empty channelUID");
}

this.eventOnChannel = (String) module.getConfiguration().get(CFG_CHANNEL_EVENT);
this.channelUID = new ChannelUID((String) module.getConfiguration().get(CFG_CHANNEL));
TopicEventFilter topicFilter = null;
ChannelUID parsedChannel = null;
if (cfgChannel.contains("?") || cfgChannel.contains("*")) {
String topicRegex = "^openhab/channels/" + cfgChannel.replace("?", ".?").replace("*", ".*?")
+ "/triggered$";
topicFilter = new TopicEventFilter(topicRegex);
} else {
parsedChannel = new ChannelUID(cfgChannel);
}
Comment thread
jimtng marked this conversation as resolved.
this.channelUID = parsedChannel;
this.eventTopicFilter = topicFilter;
this.types = Set.of("ChannelTriggeredEvent");
this.bundleContext = bundleContext;

Expand All @@ -78,12 +97,15 @@ public void receive(Event event) {
public boolean apply(Event event) {
boolean eventMatches = false;
if (event instanceof ChannelTriggeredEvent cte) {
if (channelUID.equals(cte.getChannel())) {
String eventOnChannel = this.eventOnChannel;
logger.trace("->FILTER: {}:{}", cte.getEvent(), eventOnChannel);
eventMatches = eventOnChannel == null || eventOnChannel.isBlank()
|| eventOnChannel.equals(cte.getEvent());
if (channelUID != null && !channelUID.equals(cte.getChannel())) {
return false;
}
if (eventTopicFilter != null && !eventTopicFilter.apply(event)) {
return false;
}
String eventOnChannel = this.eventOnChannel;
logger.trace("->FILTER: {}:{}", cte.getEvent(), eventOnChannel);
eventMatches = eventOnChannel == null || eventOnChannel.isBlank() || eventOnChannel.equals(cte.getEvent());
}
return eventMatches;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"type": "TEXT",
"context": "channel",
"label": "Channel",
"description": "the id of the channel which should be observed for triggers",
"description": "the id of the channel which should be observed for triggers. '*' and '?' can be used as wildcards to match multiple channels.",
"required": true,
"filterCriteria": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module-type.core.ChannelEventTrigger.label = a trigger channel fires
module-type.core.ChannelEventTrigger.description = React on events from a trigger channel of a thing.
module-type.core.ChannelEventTrigger.config.channelUID.label = Channel
module-type.core.ChannelEventTrigger.config.channelUID.description = the id of the channel which should be observed for triggers
module-type.core.ChannelEventTrigger.config.channelUID.description = the id of the channel which should be observed for triggers. '*' and '?' can be used as wildcards to match multiple channels.
module-type.core.ChannelEventTrigger.config.event.label = Event
module-type.core.ChannelEventTrigger.config.event.description = the event on the channel to react on
module-type.core.ChannelEventTrigger.output.event.label = Event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* Basic test cases for {@link ChannelEventTriggerHandler}
*
* @author Thomas Weißschuh - Initial contribution
* @author Jimmy Tanagra - Add test cases for wildcard channel matching and channel event matching
*/
@NonNullByDefault
class ChannelEventTriggerHandlerTest {
Expand Down Expand Up @@ -61,4 +62,97 @@ public void testSubstringMatchingChannelIsNotApplied() {

assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testWildcardAsteriskMatchingChannelIsApplied() {
when(moduleMock.getConfiguration())
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:*")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testWildcardAsteriskNonMatchingChannelIsNotApplied() {
when(moduleMock.getConfiguration())
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:*")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baa:quux"))));
}

@Test
public void testWildcardQuestionMarkMatchingChannelIsApplied() {
when(moduleMock.getConfiguration())
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quu?")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}
Comment thread
jimtng marked this conversation as resolved.

@Test
public void testWildcardQuestionMarkNonMatchingChannelIsNotApplied() {
when(moduleMock.getConfiguration())
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quu?")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertFalse(
handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quuxx"))));
}

@Test
public void testMatchingChannelEventIsApplied() {
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "PRESSED")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testNonMatchingChannelEventIsNotApplied() {
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "RELEASED")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testBlankChannelEventMatchesAllEvents() {
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("RELEASED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testOmittedChannelEventMatchesAllEvents() {
when(moduleMock.getConfiguration())
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quux")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("RELEASED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testWildcardChannelWithMatchingChannelEventIsApplied() {
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
"foo:bar:baz:*", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "PRESSED")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}

@Test
public void testWildcardChannelWithNonMatchingChannelEventIsNotApplied() {
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
"foo:bar:baz:*", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "RELEASED")));
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);

assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
}
}
Loading