Skip to content

Commit c5c8bcf

Browse files
committed
Fix cofniguration handling
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
1 parent 688be46 commit c5c8bcf

3 files changed

Lines changed: 78 additions & 1 deletion

File tree

bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ public void initialize() {
151151
}
152152
final ChannelConfig channelConfig = channel.getConfiguration().as(ChannelConfig.class);
153153

154+
boolean triggerChannel = channelConfig.trigger
155+
|| MqttBindingConstants.TRIGGER.equals(channelTypeUID.getId());
156+
if (channelConfig.stateTopic.isBlank() && (triggerChannel || channelConfig.commandTopic.isBlank())) {
157+
String requiredTopics = triggerChannel ? "stateTopic" : "stateTopic or commandTopic";
158+
logger.warn("Channel '{}' must define {}", channel.getUID(), requiredTopics);
159+
configErrors.add(channel.getUID());
160+
continue;
161+
}
162+
154163
if (channelTypeUID
155164
.equals(new ChannelTypeUID(MqttBindingConstants.BINDING_ID, MqttBindingConstants.NUMBER))) {
156165
Unit<?> unit = UnitUtils.parseUnit(channelConfig.unit);
@@ -197,7 +206,7 @@ public void initialize() {
197206
// If some channels could not start up, put the entire thing offline and display the channels
198207
// in question to the user.
199208
if (!configErrors.isEmpty()) {
200-
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Remove and recreate: "
209+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid channel configuration: "
201210
+ configErrors.stream().map(ChannelUID::getAsString).collect(Collectors.joining(",")));
202211
return;
203212
}

bundles/org.openhab.binding.mqtt/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/GenericThingHandlerTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import static org.mockito.Mockito.*;
2020
import static org.openhab.binding.mqtt.generic.internal.handler.ThingChannelConstants.*;
2121

22+
import java.util.List;
23+
import java.util.Map;
2224
import java.util.concurrent.CompletableFuture;
2325

2426
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -42,12 +44,15 @@
4244
import org.openhab.core.config.core.Configuration;
4345
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
4446
import org.openhab.core.library.types.StringType;
47+
import org.openhab.core.thing.Channel;
4548
import org.openhab.core.thing.ChannelUID;
4649
import org.openhab.core.thing.Thing;
4750
import org.openhab.core.thing.ThingStatus;
4851
import org.openhab.core.thing.ThingStatusDetail;
4952
import org.openhab.core.thing.ThingStatusInfo;
5053
import org.openhab.core.thing.binding.ThingHandlerCallback;
54+
import org.openhab.core.thing.binding.builder.ChannelBuilder;
55+
import org.openhab.core.thing.type.ChannelKind;
5156
import org.openhab.core.types.RefreshType;
5257
import org.openhab.core.types.UnDefType;
5358

@@ -125,6 +130,55 @@ public void initialize() {
125130
&& ThingStatusDetail.NONE.equals(arg.getStatusDetail())));
126131
}
127132

133+
@Test
134+
public void initializeWithStateOnlyChannel() {
135+
Channel channel = cb("stateOnly", "String", new Configuration(Map.of("stateTopic", "test/state")),
136+
TEXT_CHANNEL);
137+
when(thingMock.getChannels()).thenReturn(List.of(channel));
138+
139+
thingHandler.initialize();
140+
141+
assertThat(thingHandler.channelStateByChannelUID.containsKey(channel.getUID()), is(true));
142+
verify(connectionMock).subscribe(eq("test/state"), any());
143+
}
144+
145+
@Test
146+
public void initializeWithCommandOnlyChannel() {
147+
Channel channel = cb("commandOnly", "String", new Configuration(Map.of("commandTopic", "test/command")),
148+
TEXT_CHANNEL);
149+
when(thingMock.getChannels()).thenReturn(List.of(channel));
150+
151+
thingHandler.initialize();
152+
153+
assertThat(thingHandler.channelStateByChannelUID.containsKey(channel.getUID()), is(true));
154+
verify(connectionMock, never()).subscribe(any(), any());
155+
}
156+
157+
@Test
158+
public void initializeRejectsChannelWithUnknownPropertiesInsteadOfTopics() {
159+
Channel channel = cb("invalid", "String",
160+
new Configuration(Map.of("status", "test/state", "trans", "JSONPATH:$.value")), TEXT_CHANNEL);
161+
162+
assertConfigurationError(channel);
163+
}
164+
165+
@Test
166+
public void initializeRejectsTypedTriggerWithoutStateTopic() {
167+
Channel channel = cb("invalidTypedTrigger", "String",
168+
new Configuration(Map.of("commandTopic", "test/command", "trigger", true)), TEXT_CHANNEL);
169+
170+
assertConfigurationError(channel);
171+
}
172+
173+
@Test
174+
public void initializeRejectsTriggerWithoutStateTopic() {
175+
ChannelUID channelUID = new ChannelUID(TEST_GENERIC_THING, "invalidTrigger");
176+
Channel channel = ChannelBuilder.create(channelUID).withType(TRIGGER_CHANNEL).withKind(ChannelKind.TRIGGER)
177+
.withConfiguration(new Configuration(Map.of("commandTopic", "test/command"))).build();
178+
179+
assertConfigurationError(channel);
180+
}
181+
128182
@Test
129183
public void handleCommandRefresh() {
130184
TextValue value = spy(new TextValue());
@@ -206,4 +260,17 @@ public void handleBridgeStatusChange() {
206260
thingHandler.bridgeStatusChanged(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null));
207261
verify(connectionMock, times(2)).subscribe(eq("test/LWT"), any());
208262
}
263+
264+
private void assertConfigurationError(Channel channel) {
265+
when(thingMock.getChannels()).thenReturn(List.of(channel));
266+
267+
thingHandler.initialize();
268+
269+
assertThat(thingHandler.channelStateByChannelUID.containsKey(channel.getUID()), is(false));
270+
verify(thingHandler, never()).start(any());
271+
verify(callbackMock).statusUpdated(eq(thingMock),
272+
argThat(arg -> ThingStatus.OFFLINE.equals(arg.getStatus())
273+
&& ThingStatusDetail.CONFIGURATION_ERROR.equals(arg.getStatusDetail())
274+
&& ("Invalid channel configuration: " + channel.getUID()).equals(arg.getDescription())));
275+
}
209276
}

bundles/org.openhab.binding.mqtt/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/ThingChannelConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class ThingChannelConstants {
4545
public static final ChannelTypeUID ON_OFF_CHANNEL = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.SWITCH);
4646
public static final ChannelTypeUID NUMBER_CHANNEL = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.NUMBER);
4747
public static final ChannelTypeUID PERCENTAGE_CHANNEL = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.DIMMER);
48+
public static final ChannelTypeUID TRIGGER_CHANNEL = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.TRIGGER);
4849
public static final ChannelTypeUID UNKNOWN_CHANNEL = new ChannelTypeUID(BINDING_ID, "unknown");
4950

5051
public static final ChannelUID TEXT_CHANNEL_UID = new ChannelUID(TEST_GENERIC_THING, "mytext");

0 commit comments

Comments
 (0)