[mqtt.homeassistant] Use a thing action for updates, instead of a config action (#18524)

* [mqtt.homeassistant] Use a thing action for updates, instead of a config action

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer
2025-05-17 14:10:54 +02:00
committed by GitHub
parent 3607758dea
commit 97ea270399
7 changed files with 120 additions and 81 deletions
@@ -81,8 +81,8 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (supportsThingType(thingTypeUID)) { if (supportsThingType(thingTypeUID)) {
return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry, return new HomeAssistantThingHandler(thing, this, typeProvider, stateDescriptionProvider,
jinjava, unitProvider, 10000, 2000); channelTypeRegistry, jinjava, unitProvider, 10000, 2000);
} }
return null; return null;
} }
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2010-2025 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.binding.mqtt.homeassistant.internal.actions;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the automation engine action handler service for the update action.
*
* @author Cody Cutrer - Initial contribution
*/
@Component(scope = ServiceScope.PROTOTYPE, service = HomeAssistantUpdateThingActions.class)
@ThingActionsScope(name = "mqtt")
@NonNullByDefault
public class HomeAssistantUpdateThingActions implements ThingActions {
private final Logger logger = LoggerFactory.getLogger(HomeAssistantUpdateThingActions.class);
private @Nullable HomeAssistantThingHandler handler;
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
this.handler = (HomeAssistantThingHandler) handler;
}
@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}
@RuleAction(label = "@text/updateActionLabel", description = "@text/updateActionDesc")
public void update() {
HomeAssistantThingHandler handler = this.handler;
if (handler == null) {
logger.warn("Home Assistant Update Action Service ThingHandler is null!");
return;
}
handler.doUpdate();
}
public static void update(ThingActions actions) {
((HomeAssistantUpdateThingActions) actions).update();
}
}
@@ -12,7 +12,6 @@
*/ */
package org.openhab.binding.mqtt.homeassistant.internal.handler; package org.openhab.binding.mqtt.homeassistant.internal.handler;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@@ -40,13 +39,13 @@ import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.Compon
import org.openhab.binding.mqtt.homeassistant.internal.HaID; import org.openhab.binding.mqtt.homeassistant.internal.HaID;
import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration; import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantChannelLinkageChecker; import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantChannelLinkageChecker;
import org.openhab.binding.mqtt.homeassistant.internal.actions.HomeAssistantUpdateThingActions;
import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent; import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory; import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory;
import org.openhab.binding.mqtt.homeassistant.internal.component.Update; import org.openhab.binding.mqtt.homeassistant.internal.component.Update;
import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory; import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException; import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.core.validation.ConfigValidationException;
import org.openhab.core.i18n.UnitProvider; import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
@@ -56,6 +55,7 @@ import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeRegistry; import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -88,10 +88,10 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
private static final Comparator<AbstractComponent<?>> COMPONENT_COMPARATOR = Comparator private static final Comparator<AbstractComponent<?>> COMPONENT_COMPARATOR = Comparator
.comparing((AbstractComponent<?> component) -> component.hasGroup()) .comparing((AbstractComponent<?> component) -> component.hasGroup())
.thenComparing(AbstractComponent::getName); .thenComparing(AbstractComponent::getName);
private static final URI UPDATABLE_CONFIG_DESCRIPTION_URI = URI.create("thing-type:mqtt:homeassistant-updatable");
private final Logger logger = LoggerFactory.getLogger(HomeAssistantThingHandler.class); private final Logger logger = LoggerFactory.getLogger(HomeAssistantThingHandler.class);
protected final BaseThingHandlerFactory thingHandlerFactory;
protected final MqttChannelTypeProvider channelTypeProvider; protected final MqttChannelTypeProvider channelTypeProvider;
protected final MqttChannelStateDescriptionProvider stateDescriptionProvider; protected final MqttChannelStateDescriptionProvider stateDescriptionProvider;
protected final ChannelTypeRegistry channelTypeRegistry; protected final ChannelTypeRegistry channelTypeRegistry;
@@ -122,11 +122,13 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
* @param subscribeTimeout Timeout for the entire tree parsing and subscription. In milliseconds. * @param subscribeTimeout Timeout for the entire tree parsing and subscription. In milliseconds.
* @param attributeReceiveTimeout The timeout per attribute field subscription. In milliseconds. * @param attributeReceiveTimeout The timeout per attribute field subscription. In milliseconds.
*/ */
public HomeAssistantThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider, public HomeAssistantThingHandler(Thing thing, BaseThingHandlerFactory thingHandlerFactory,
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry, MqttChannelTypeProvider channelTypeProvider, MqttChannelStateDescriptionProvider stateDescriptionProvider,
Jinjava jinjava, UnitProvider unitProvider, int subscribeTimeout, int attributeReceiveTimeout) { ChannelTypeRegistry channelTypeRegistry, Jinjava jinjava, UnitProvider unitProvider, int subscribeTimeout,
int attributeReceiveTimeout) {
super(thing, subscribeTimeout); super(thing, subscribeTimeout);
this.gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create(); this.gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
this.thingHandlerFactory = thingHandlerFactory;
this.channelTypeProvider = channelTypeProvider; this.channelTypeProvider = channelTypeProvider;
this.stateDescriptionProvider = stateDescriptionProvider; this.stateDescriptionProvider = stateDescriptionProvider;
this.channelTypeRegistry = channelTypeRegistry; this.channelTypeRegistry = channelTypeRegistry;
@@ -344,11 +346,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
return null; return null;
}); });
} }
if (discovered instanceof Update) {
updateComponent = (Update) discovered;
updateComponent.setReleaseStateUpdateListener(this::releaseStateUpdated);
}
} }
updateThingType(typeID); updateThingType(typeID);
} }
@@ -369,6 +366,10 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
haComponents.remove(known.getComponentId()); haComponents.remove(known.getComponentId());
haComponentsByHaId.remove(removed); haComponentsByHaId.remove(removed);
componentActuallyRemoved = true; componentActuallyRemoved = true;
if (known.equals(updateComponent)) {
updateComponent = null;
}
} }
} }
if (componentActuallyRemoved) { if (componentActuallyRemoved) {
@@ -377,6 +378,17 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
} }
} }
public void doUpdate() {
Update updateComponent = this.updateComponent;
if (updateComponent == null) {
logger.warn(
"Received update command for Home Assistant device {}, but it does not have an update component.",
getThing().getUID());
} else {
updateComponent.doUpdate();
}
}
@Override @Override
protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) { protected void updateThingStatus(boolean messageReceived, Optional<Boolean> availabilityTopicsSeen) {
if (availabilityTopicsSeen.orElse(messageReceived)) { if (availabilityTopicsSeen.orElse(messageReceived)) {
@@ -386,26 +398,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
} }
} }
@Override
public void handleConfigurationUpdate(Map<String, Object> configurationParameters)
throws ConfigValidationException {
if (configurationParameters.containsKey("doUpdate")) {
configurationParameters = new HashMap<>(configurationParameters);
Object value = configurationParameters.remove("doUpdate");
if (value instanceof Boolean doUpdate && doUpdate) {
Update updateComponent = this.updateComponent;
if (updateComponent == null) {
logger.warn(
"Received update command for Home Assistant device {}, but it does not have an update component.",
getThing().getUID());
} else {
updateComponent.doUpdate();
}
}
}
super.handleConfigurationUpdate(configurationParameters);
}
private boolean updateThingType(ThingTypeUID typeID) { private boolean updateThingType(ThingTypeUID typeID) {
// if this is a dynamic type, then we update the type // if this is a dynamic type, then we update the type
if (!MqttBindingConstants.HOMEASSISTANT_MQTT_THING.equals(typeID)) { if (!MqttBindingConstants.HOMEASSISTANT_MQTT_THING.equals(typeID)) {
@@ -434,10 +426,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
var channelDefs = sortedComponents.stream().map(AbstractComponent::getChannelDefinitions) var channelDefs = sortedComponents.stream().map(AbstractComponent::getChannelDefinitions)
.flatMap(List::stream).toList(); .flatMap(List::stream).toList();
thingTypeBuilder.withChannelDefinitions(channelDefs).withChannelGroupDefinitions(groupDefs); thingTypeBuilder.withChannelDefinitions(channelDefs).withChannelGroupDefinitions(groupDefs);
Update updateComponent = this.updateComponent;
if (updateComponent != null && updateComponent.isUpdatable()) {
thingTypeBuilder.withConfigDescriptionURI(UPDATABLE_CONFIG_DESCRIPTION_URI);
}
channelTypeProvider.putThingType(thingTypeBuilder.build()); channelTypeProvider.putThingType(thingTypeBuilder.build());
@@ -511,6 +499,14 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
haComponents.put(component.getComponentId(), component); haComponents.put(component.getComponentId(), component);
haComponentsByUniqueId.put(component.getUniqueId(), component); haComponentsByUniqueId.put(component.getUniqueId(), component);
haComponentsByHaId.put(component.getHaID(), component); haComponentsByHaId.put(component.getHaID(), component);
if (component instanceof Update updateComponent) {
this.updateComponent = updateComponent;
updateComponent.setReleaseStateUpdateListener(this::releaseStateUpdated);
if (updateComponent.isUpdatable()) {
thingHandlerFactory.registerService(this, HomeAssistantUpdateThingActions.class);
}
}
return true; return true;
} }
@@ -16,28 +16,4 @@
<default>homeassistant</default> <default>homeassistant</default>
</parameter> </parameter>
</config-description> </config-description>
<config-description uri="thing-type:mqtt:homeassistant-updatable">
<parameter-group name="actions">
<label>Actions</label>
</parameter-group>
<parameter name="topics" type="text" required="true" multiple="true">
<label>MQTT Config Topic</label>
<description>List of Home Assistant configuration topics (e.g. button/my-device/restart)</description>
</parameter>
<parameter name="basetopic" type="text" required="true">
<label>MQTT Base Prefix</label>
<description>MQTT base prefix</description>
<default>homeassistant</default>
</parameter>
<parameter name="doUpdate" type="boolean" groupName="actions">
<label>Update</label>
<description>Request the device do an OTA update</description>
<advanced>true</advanced>
<default>false</default>
</parameter>
</config-description>
</config-description:config-descriptions> </config-description:config-descriptions>
@@ -45,15 +45,6 @@ channel-type.config.mqtt.ha-channel.nodeid.description = Optional node name of t
channel-type.config.mqtt.ha-channel.objectid.label = Object ID channel-type.config.mqtt.ha-channel.objectid.label = Object ID
channel-type.config.mqtt.ha-channel.objectid.description = Object ID of the component channel-type.config.mqtt.ha-channel.objectid.description = Object ID of the component
# thing types config
thing-type.config.mqtt.homeassistant-updatable.basetopic.label = MQTT Base Prefix
thing-type.config.mqtt.homeassistant-updatable.basetopic.description = MQTT base prefix
thing-type.config.mqtt.homeassistant-updatable.topics.label = MQTT Config Topic
thing-type.config.mqtt.homeassistant-updatable.topics.description = List of Home Assistant configuration topics (e.g. button/my-device/restart)
thing-type.config.mqtt.homeassistant-updatable.doUpdate.label = Update
thing-type.config.mqtt.homeassistant-updatable.doUpdate.description = Request the device do an OTA update
# binding config # binding config
binding.config.mqtt.homeassistant-status.label = Publish Online Status binding.config.mqtt.homeassistant-status.label = Publish Online Status
@@ -129,3 +120,8 @@ state.water-heater.mode.gas = Gas
state.water-heater.mode.heat-pump = Heat Pump state.water-heater.mode.heat-pump = Heat Pump
state.water-heater.mode.high-demand = High Demand state.water-heater.mode.high-demand = High Demand
state.water-heater.mode.performance = Performance state.water-heater.mode.performance = Performance
# thing actions
updateActionLabel = Update
updateActionDesc = Trigger the device to perform an update
@@ -47,6 +47,7 @@ import org.openhab.core.library.types.HSBType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.type.AutoUpdatePolicy; import org.openhab.core.thing.type.AutoUpdatePolicy;
import org.openhab.core.thing.type.ChannelKind; import org.openhab.core.thing.type.ChannelKind;
@@ -67,6 +68,7 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
private static final int ATTRIBUTE_RECEIVE_TIMEOUT = 2000; private static final int ATTRIBUTE_RECEIVE_TIMEOUT = 2000;
private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock; private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
protected @Mock @NonNullByDefault({}) BaseThingHandlerFactory thingHandlerFactory;
private @NonNullByDefault({}) LatchThingHandler thingHandler; private @NonNullByDefault({}) LatchThingHandler thingHandler;
protected @Mock @NonNullByDefault({}) UnitProvider unitProvider; protected @Mock @NonNullByDefault({}) UnitProvider unitProvider;
@@ -85,8 +87,9 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
when(callbackMock.getBridge(eq(BRIDGE_UID))).thenReturn(bridgeThing); when(callbackMock.getBridge(eq(BRIDGE_UID))).thenReturn(bridgeThing);
thingHandler = new LatchThingHandler(haThing, channelTypeProvider, stateDescriptionProvider, thingHandler = new LatchThingHandler(haThing, thingHandlerFactory, channelTypeProvider,
channelTypeRegistry, unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT); stateDescriptionProvider, channelTypeRegistry, unitProvider, SUBSCRIBE_TIMEOUT,
ATTRIBUTE_RECEIVE_TIMEOUT);
thingHandler.setConnection(bridgeConnection); thingHandler.setConnection(bridgeConnection);
thingHandler.setCallback(callbackMock); thingHandler.setCallback(callbackMock);
thingHandler = spy(thingHandler); thingHandler = spy(thingHandler);
@@ -361,11 +364,12 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
private @Nullable CountDownLatch latch; private @Nullable CountDownLatch latch;
private @Nullable AbstractComponent<@NonNull ? extends AbstractChannelConfiguration> discoveredComponent; private @Nullable AbstractComponent<@NonNull ? extends AbstractChannelConfiguration> discoveredComponent;
public LatchThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider, public LatchThingHandler(Thing thing, BaseThingHandlerFactory thingHandlerFactory,
MqttChannelTypeProvider channelTypeProvider,
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry, MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
UnitProvider unitProvider, int subscribeTimeout, int attributeReceiveTimeout) { UnitProvider unitProvider, int subscribeTimeout, int attributeReceiveTimeout) {
super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry, new Jinjava(), super(thing, thingHandlerFactory, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry,
unitProvider, subscribeTimeout, attributeReceiveTimeout); new Jinjava(), unitProvider, subscribeTimeout, attributeReceiveTimeout);
} }
@Override @Override
@@ -42,6 +42,7 @@ import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandlerCallback; import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ChannelBuilder; import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.thing.binding.builder.ThingBuilder;
@@ -70,6 +71,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
private static final List<String> MQTT_TOPICS = CONFIG_TOPICS.stream() private static final List<String> MQTT_TOPICS = CONFIG_TOPICS.stream()
.map(AbstractHomeAssistantTests::configTopicToMqtt).collect(Collectors.toList()); .map(AbstractHomeAssistantTests::configTopicToMqtt).collect(Collectors.toList());
private @Mock @NonNullByDefault({}) BaseThingHandlerFactory thingHandlerFactory;
private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock; private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
private @NonNullByDefault({}) HomeAssistantThingHandler thingHandler; private @NonNullByDefault({}) HomeAssistantThingHandler thingHandler;
private @NonNullByDefault({}) HomeAssistantThingHandler nonSpyThingHandler; private @NonNullByDefault({}) HomeAssistantThingHandler nonSpyThingHandler;
@@ -88,8 +90,9 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
} }
protected void setupThingHandler() { protected void setupThingHandler() {
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider, thingHandler = new HomeAssistantThingHandler(haThing, thingHandlerFactory, channelTypeProvider,
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT); stateDescriptionProvider, channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT,
ATTRIBUTE_RECEIVE_TIMEOUT);
thingHandler.setConnection(bridgeConnection); thingHandler.setConnection(bridgeConnection);
thingHandler.setCallback(callbackMock); thingHandler.setCallback(callbackMock);
nonSpyThingHandler = thingHandler; nonSpyThingHandler = thingHandler;
@@ -357,8 +360,9 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
@Test @Test
public void testDuplicateChannelId() { public void testDuplicateChannelId() {
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider, thingHandler = new HomeAssistantThingHandler(haThing, thingHandlerFactory, channelTypeProvider,
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT); stateDescriptionProvider, channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT,
ATTRIBUTE_RECEIVE_TIMEOUT);
thingHandler.setConnection(bridgeConnection); thingHandler.setConnection(bridgeConnection);
thingHandler.setCallback(callbackMock); thingHandler.setCallback(callbackMock);
nonSpyThingHandler = thingHandler; nonSpyThingHandler = thingHandler;
@@ -413,8 +417,9 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
@Test @Test
public void testDuplicateChannelIdComplex() { public void testDuplicateChannelIdComplex() {
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider, thingHandler = new HomeAssistantThingHandler(haThing, thingHandlerFactory, channelTypeProvider,
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT); stateDescriptionProvider, channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT,
ATTRIBUTE_RECEIVE_TIMEOUT);
thingHandler.setConnection(bridgeConnection); thingHandler.setConnection(bridgeConnection);
thingHandler.setCallback(callbackMock); thingHandler.setCallback(callbackMock);
nonSpyThingHandler = thingHandler; nonSpyThingHandler = thingHandler;