Skip to content

Commit ba06ad8

Browse files
authored
Add wildcard support for ChannelEventTriggerHandler (openhab#5512)
* Add wildcard support for ChannelEventTriggerHandler * Fix event matching on wildcard channel and add relevant tests * Add negative match test for wildcard filter * Check against null and blank channelUID configuration Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
1 parent bd23e47 commit ba06ad8

4 files changed

Lines changed: 125 additions & 9 deletions

File tree

bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/ChannelEventTriggerHandler.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.openhab.core.events.Event;
2525
import org.openhab.core.events.EventFilter;
2626
import org.openhab.core.events.EventSubscriber;
27+
import org.openhab.core.events.TopicEventFilter;
2728
import org.openhab.core.thing.ChannelUID;
2829
import org.openhab.core.thing.events.ChannelTriggeredEvent;
2930
import org.osgi.framework.BundleContext;
@@ -35,6 +36,7 @@
3536
* This is a ModuleHandler implementation for trigger channels with specific events
3637
*
3738
* @author Stefan Triller - Initial contribution
39+
* @author Jimmy Tanagra - Add support for wildcard channel matching
3840
*/
3941
@NonNullByDefault
4042
public class ChannelEventTriggerHandler extends BaseTriggerModuleHandler implements EventSubscriber, EventFilter {
@@ -48,16 +50,33 @@ public class ChannelEventTriggerHandler extends BaseTriggerModuleHandler impleme
4850
private final Logger logger = LoggerFactory.getLogger(ChannelEventTriggerHandler.class);
4951

5052
private @Nullable final String eventOnChannel;
51-
private final ChannelUID channelUID;
53+
private final @Nullable ChannelUID channelUID;
54+
private final @Nullable TopicEventFilter eventTopicFilter;
5255
private final Set<String> types;
5356
private final BundleContext bundleContext;
5457
private final ServiceRegistration<?> eventSubscriberRegistration;
5558

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

62+
String cfgChannel = (String) module.getConfiguration().get(CFG_CHANNEL);
63+
64+
if (cfgChannel == null || cfgChannel.isBlank()) {
65+
throw new IllegalArgumentException("Configuration must contain a non-empty channelUID");
66+
}
67+
5968
this.eventOnChannel = (String) module.getConfiguration().get(CFG_CHANNEL_EVENT);
60-
this.channelUID = new ChannelUID((String) module.getConfiguration().get(CFG_CHANNEL));
69+
TopicEventFilter topicFilter = null;
70+
ChannelUID parsedChannel = null;
71+
if (cfgChannel.contains("?") || cfgChannel.contains("*")) {
72+
String topicRegex = "^openhab/channels/" + cfgChannel.replace("?", ".?").replace("*", ".*?")
73+
+ "/triggered$";
74+
topicFilter = new TopicEventFilter(topicRegex);
75+
} else {
76+
parsedChannel = new ChannelUID(cfgChannel);
77+
}
78+
this.channelUID = parsedChannel;
79+
this.eventTopicFilter = topicFilter;
6180
this.types = Set.of("ChannelTriggeredEvent");
6281
this.bundleContext = bundleContext;
6382

@@ -78,12 +97,15 @@ public void receive(Event event) {
7897
public boolean apply(Event event) {
7998
boolean eventMatches = false;
8099
if (event instanceof ChannelTriggeredEvent cte) {
81-
if (channelUID.equals(cte.getChannel())) {
82-
String eventOnChannel = this.eventOnChannel;
83-
logger.trace("->FILTER: {}:{}", cte.getEvent(), eventOnChannel);
84-
eventMatches = eventOnChannel == null || eventOnChannel.isBlank()
85-
|| eventOnChannel.equals(cte.getEvent());
100+
if (channelUID != null && !channelUID.equals(cte.getChannel())) {
101+
return false;
102+
}
103+
if (eventTopicFilter != null && !eventTopicFilter.apply(event)) {
104+
return false;
86105
}
106+
String eventOnChannel = this.eventOnChannel;
107+
logger.trace("->FILTER: {}:{}", cte.getEvent(), eventOnChannel);
108+
eventMatches = eventOnChannel == null || eventOnChannel.isBlank() || eventOnChannel.equals(cte.getEvent());
87109
}
88110
return eventMatches;
89111
}

bundles/org.openhab.core.automation/src/main/resources/OH-INF/automation/moduletypes/ChannelTrigger.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"type": "TEXT",
1111
"context": "channel",
1212
"label": "Channel",
13-
"description": "the id of the channel which should be observed for triggers",
13+
"description": "the id of the channel which should be observed for triggers. '*' and '?' can be used as wildcards to match multiple channels.",
1414
"required": true,
1515
"filterCriteria": [
1616
{

bundles/org.openhab.core.automation/src/main/resources/OH-INF/i18n/automation.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module-type.core.ChannelEventTrigger.label = a trigger channel fires
44
module-type.core.ChannelEventTrigger.description = React on events from a trigger channel of a thing.
55
module-type.core.ChannelEventTrigger.config.channelUID.label = Channel
6-
module-type.core.ChannelEventTrigger.config.channelUID.description = the id of the channel which should be observed for triggers
6+
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.
77
module-type.core.ChannelEventTrigger.config.event.label = Event
88
module-type.core.ChannelEventTrigger.config.event.description = the event on the channel to react on
99
module-type.core.ChannelEventTrigger.output.event.label = Event

bundles/org.openhab.core.automation/src/test/java/org/openhab/core/automation/internal/module/handler/ChannelEventTriggerHandlerTest.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* Basic test cases for {@link ChannelEventTriggerHandler}
3232
*
3333
* @author Thomas Weißschuh - Initial contribution
34+
* @author Jimmy Tanagra - Add test cases for wildcard channel matching and channel event matching
3435
*/
3536
@NonNullByDefault
3637
class ChannelEventTriggerHandlerTest {
@@ -61,4 +62,97 @@ public void testSubstringMatchingChannelIsNotApplied() {
6162

6263
assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
6364
}
65+
66+
@Test
67+
public void testWildcardAsteriskMatchingChannelIsApplied() {
68+
when(moduleMock.getConfiguration())
69+
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:*")));
70+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
71+
72+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
73+
}
74+
75+
@Test
76+
public void testWildcardAsteriskNonMatchingChannelIsNotApplied() {
77+
when(moduleMock.getConfiguration())
78+
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:*")));
79+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
80+
81+
assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baa:quux"))));
82+
}
83+
84+
@Test
85+
public void testWildcardQuestionMarkMatchingChannelIsApplied() {
86+
when(moduleMock.getConfiguration())
87+
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quu?")));
88+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
89+
90+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
91+
}
92+
93+
@Test
94+
public void testWildcardQuestionMarkNonMatchingChannelIsNotApplied() {
95+
when(moduleMock.getConfiguration())
96+
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quu?")));
97+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
98+
99+
assertFalse(
100+
handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quuxx"))));
101+
}
102+
103+
@Test
104+
public void testMatchingChannelEventIsApplied() {
105+
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
106+
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "PRESSED")));
107+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
108+
109+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
110+
}
111+
112+
@Test
113+
public void testNonMatchingChannelEventIsNotApplied() {
114+
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
115+
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "RELEASED")));
116+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
117+
118+
assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
119+
}
120+
121+
@Test
122+
public void testBlankChannelEventMatchesAllEvents() {
123+
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
124+
"foo:bar:baz:quux", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "")));
125+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
126+
127+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
128+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("RELEASED", new ChannelUID("foo:bar:baz:quux"))));
129+
}
130+
131+
@Test
132+
public void testOmittedChannelEventMatchesAllEvents() {
133+
when(moduleMock.getConfiguration())
134+
.thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL, "foo:bar:baz:quux")));
135+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
136+
137+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
138+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("RELEASED", new ChannelUID("foo:bar:baz:quux"))));
139+
}
140+
141+
@Test
142+
public void testWildcardChannelWithMatchingChannelEventIsApplied() {
143+
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
144+
"foo:bar:baz:*", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "PRESSED")));
145+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
146+
147+
assertTrue(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
148+
}
149+
150+
@Test
151+
public void testWildcardChannelWithNonMatchingChannelEventIsNotApplied() {
152+
when(moduleMock.getConfiguration()).thenReturn(new Configuration(Map.of(ChannelEventTriggerHandler.CFG_CHANNEL,
153+
"foo:bar:baz:*", ChannelEventTriggerHandler.CFG_CHANNEL_EVENT, "RELEASED")));
154+
handler = new ChannelEventTriggerHandler(moduleMock, contextMock);
155+
156+
assertFalse(handler.apply(ThingEventFactory.createTriggerEvent("PRESSED", new ChannelUID("foo:bar:baz:quux"))));
157+
}
64158
}

0 commit comments

Comments
 (0)