[mqtt.homeassistant] handle null component name (#15427)

* [mqtt.homeassistant] handle null component name

channels from such components will not have a group. this is
now done by zigbee2mqtt for the "default" component of a device,
such as the light. HASS encourages this as of release 2023.8

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer 2023-11-18 14:46:57 -07:00 committed by GitHub
parent a535caa13c
commit 7fc351c9c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 111 additions and 70 deletions

View File

@ -224,7 +224,7 @@ public class ComponentChannel {
ChannelType type;
ChannelTypeUID channelTypeUID;
channelUID = new ChannelUID(component.getGroupUID(), channelID);
channelUID = component.buildChannelUID(channelID);
channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID,
channelUID.getGroupId() + "_" + channelID);
channelState = new HomeAssistantChannelState(

View File

@ -14,6 +14,7 @@ package org.openhab.binding.mqtt.homeassistant.internal.component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
@ -31,8 +32,10 @@ import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory.ComponentConfiguration;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.thing.ChannelGroupUID;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.type.ChannelDefinition;
import org.openhab.core.thing.type.ChannelGroupDefinition;
import org.openhab.core.thing.type.ChannelGroupType;
@ -54,8 +57,8 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
// Component location fields
private final ComponentConfiguration componentConfiguration;
protected final ChannelGroupTypeUID channelGroupTypeUID;
protected final ChannelGroupUID channelGroupUID;
protected final @Nullable ChannelGroupTypeUID channelGroupTypeUID;
protected final @Nullable ChannelGroupUID channelGroupUID;
protected final HaID haID;
// Channels and configuration
@ -83,10 +86,15 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
this.haID = componentConfiguration.getHaID();
String groupId = this.haID.getGroupId(channelConfiguration.getUniqueId());
if (channelConfiguration.getName() != null) {
String groupId = this.haID.getGroupId(channelConfiguration.getUniqueId());
this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
} else {
this.channelGroupTypeUID = null;
this.channelGroupUID = null;
}
this.configSeen = false;
@ -142,7 +150,10 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
* @param channelTypeProvider The channel type provider
*/
public void addChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
channelTypeProvider.setChannelGroupType(getGroupTypeUID(), getType());
ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
if (groupTypeUID != null) {
channelTypeProvider.setChannelGroupType(groupTypeUID, Objects.requireNonNull(getType()));
}
channels.values().forEach(v -> v.addChannelTypes(channelTypeProvider));
}
@ -154,20 +165,31 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
*/
public void removeChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
channels.values().forEach(v -> v.removeChannelTypes(channelTypeProvider));
channelTypeProvider.removeChannelGroupType(getGroupTypeUID());
ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
if (groupTypeUID != null) {
channelTypeProvider.removeChannelGroupType(groupTypeUID);
}
}
public ChannelUID buildChannelUID(String channelID) {
final ChannelGroupUID groupUID = channelGroupUID;
if (groupUID != null) {
return new ChannelUID(groupUID, channelID);
}
return new ChannelUID(componentConfiguration.getThingUID(), channelID);
}
/**
* Each HomeAssistant component corresponds to a Channel Group Type.
*/
public ChannelGroupTypeUID getGroupTypeUID() {
public @Nullable ChannelGroupTypeUID getGroupTypeUID() {
return channelGroupTypeUID;
}
/**
* The unique id of this component.
*/
public ChannelGroupUID getGroupUID() {
public @Nullable ChannelGroupUID getGroupUID() {
return channelGroupUID;
}
@ -175,7 +197,16 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
* Component (Channel Group) name.
*/
public String getName() {
return channelConfiguration.getName();
String result = channelConfiguration.getName();
Device device = channelConfiguration.getDevice();
if (result == null && device != null) {
result = device.getName();
}
if (result == null) {
result = haID.objectID;
}
return result;
}
/**
@ -207,11 +238,19 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
/**
* Return the channel group type.
*/
public ChannelGroupType getType() {
public @Nullable ChannelGroupType getType() {
ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
if (groupTypeUID == null) {
return null;
}
final List<ChannelDefinition> channelDefinitions = channels.values().stream().map(ComponentChannel::type)
.collect(Collectors.toList());
return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, getName())
.withChannelDefinitions(channelDefinitions).build();
return ChannelGroupTypeBuilder.instance(groupTypeUID, getName()).withChannelDefinitions(channelDefinitions)
.build();
}
public List<ChannelDefinition> getChannels() {
return channels.values().stream().map(ComponentChannel::type).collect(Collectors.toList());
}
/**
@ -225,8 +264,12 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
/**
* Return the channel group definition for this component.
*/
public ChannelGroupDefinition getGroupDefinition() {
return new ChannelGroupDefinition(channelGroupUID.getId(), getGroupTypeUID(), getName(), null);
public @Nullable ChannelGroupDefinition getGroupDefinition() {
ChannelGroupTypeUID groupTypeUID = channelGroupTypeUID;
if (groupTypeUID == null) {
return null;
}
return new ChannelGroupDefinition(channelGroupUID.getId(), groupTypeUID, getName(), null);
}
public HaID getHaID() {

View File

@ -74,24 +74,23 @@ public class AlarmControlPanel extends AbstractComponent<AlarmControlPanel.Chann
final String[] stateEnum = { channelConfiguration.stateDisarmed, channelConfiguration.stateArmedHome,
channelConfiguration.stateArmedAway, channelConfiguration.statePending,
channelConfiguration.stateTriggered };
buildChannel(STATE_CHANNEL_ID, new TextValue(stateEnum), channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(STATE_CHANNEL_ID, new TextValue(stateEnum), getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.build();
String commandTopic = channelConfiguration.commandTopic;
if (commandTopic != null) {
buildChannel(SWITCH_DISARM_CHANNEL_ID, new TextValue(new String[] { channelConfiguration.payloadDisarm }),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
getName(), componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
buildChannel(SWITCH_ARM_HOME_CHANNEL_ID,
new TextValue(new String[] { channelConfiguration.payloadArmHome }), channelConfiguration.getName(),
new TextValue(new String[] { channelConfiguration.payloadArmHome }), getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
buildChannel(SWITCH_ARM_AWAY_CHANNEL_ID,
new TextValue(new String[] { channelConfiguration.payloadArmAway }), channelConfiguration.getName(),
new TextValue(new String[] { channelConfiguration.payloadArmAway }), getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos()).build();
}

View File

@ -43,7 +43,7 @@ public class Camera extends AbstractComponent<Camera.ChannelConfiguration> {
ImageValue value = new ImageValue();
buildChannel(CAMERA_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener()).stateTopic(channelConfiguration.topic).build();
buildChannel(CAMERA_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.topic).build();
}
}

View File

@ -286,7 +286,7 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
@Nullable String commandTopic, @Nullable String stateTemplate, @Nullable String stateTopic,
@Nullable Predicate<Command> commandFilter) {
if ((commandTopic != null && !commandTopic.isBlank()) || (stateTopic != null && !stateTopic.isBlank())) {
return buildChannel(channelId, valueState, channelConfiguration.getName(), channelStateUpdateListener)
return buildChannel(channelId, valueState, getName(), channelStateUpdateListener)
.stateTopic(stateTopic, stateTemplate, channelConfiguration.getValueTemplate())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos(),
commandTemplate)

View File

@ -56,8 +56,7 @@ public class Cover extends AbstractComponent<Cover.ChannelConfiguration> {
RollershutterValue value = new RollershutterValue(channelConfiguration.payloadOpen,
channelConfiguration.payloadClose, channelConfiguration.payloadStop);
buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(SWITCH_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())

View File

@ -281,7 +281,7 @@ public class DefaultSchemaLight extends Light {
colorValue.update(newOnState);
}
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID),
state.equals(OnOffType.ON) ? newOnState : HSBType.BLACK);
} else if (brightnessChannel != null) {
listener.updateChannelState(new ChannelUID(channel.getThingUID(), BRIGHTNESS_CHANNEL_ID),
@ -301,8 +301,7 @@ public class DefaultSchemaLight extends Light {
colorValue.update(new HSBType(DecimalType.ZERO, PercentType.ZERO,
(PercentType) brightnessValue.getChannelState()));
}
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
colorValue.getChannelState());
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
} else {
listener.updateChannelState(channel, state);
}
@ -330,13 +329,11 @@ public class DefaultSchemaLight extends Light {
HSBType xyColor = HSBType.fromXY(x, y);
colorValue.update(new HSBType(xyColor.getHue(), xyColor.getSaturation(), brightness));
}
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
colorValue.getChannelState());
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
return;
case RGB_CHANNEL_ID:
colorValue.update((HSBType) state);
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID),
colorValue.getChannelState());
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
break;
case RGBW_CHANNEL_ID:
case RGBWW_CHANNEL_ID:

View File

@ -65,8 +65,7 @@ public class DeviceTrigger extends AbstractComponent<DeviceTrigger.ChannelConfig
value = new TextValue();
}
buildChannel(channelConfiguration.type, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(channelConfiguration.type, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.topic, channelConfiguration.getValueTemplate()).trigger(true).build();
}
}

View File

@ -54,8 +54,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> {
super(componentConfiguration, ChannelConfiguration.class);
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(SWITCH_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)

View File

@ -286,12 +286,11 @@ public class JSONSchemaLight extends AbstractRawSchemaLight {
colorModeValue.getChannelState());
if (hasColorChannel) {
listener.updateChannelState(new ChannelUID(getGroupUID(), COLOR_CHANNEL_ID), colorValue.getChannelState());
listener.updateChannelState(buildChannelUID(COLOR_CHANNEL_ID), colorValue.getChannelState());
} else if (brightnessChannel != null) {
listener.updateChannelState(new ChannelUID(getGroupUID(), BRIGHTNESS_CHANNEL_ID),
brightnessValue.getChannelState());
listener.updateChannelState(buildChannelUID(BRIGHTNESS_CHANNEL_ID), brightnessValue.getChannelState());
} else {
listener.updateChannelState(new ChannelUID(getGroupUID(), ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
listener.updateChannelState(buildChannelUID(ON_OFF_CHANNEL_ID), onOffValue.getChannelState());
}
}
}

View File

@ -58,8 +58,8 @@ public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
}
buildChannel(SWITCH_CHANNEL_ID,
new OnOffValue(channelConfiguration.payloadLock, channelConfiguration.payloadUnlock),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
new OnOffValue(channelConfiguration.payloadLock, channelConfiguration.payloadUnlock), getName(),
componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())

View File

@ -82,8 +82,7 @@ public class Number extends AbstractComponent<Number.ChannelConfiguration> {
NumberValue value = new NumberValue(channelConfiguration.min, channelConfiguration.max,
channelConfiguration.step, UnitUtils.parseUnit(channelConfiguration.unitOfMeasurement));
buildChannel(NUMBER_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(NUMBER_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)

View File

@ -66,8 +66,7 @@ public class Select extends AbstractComponent<Select.ChannelConfiguration> {
TextValue value = new TextValue(channelConfiguration.options);
buildChannel(SELECT_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
buildChannel(SELECT_CHANNEL_ID, value, getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos(), channelConfiguration.commandTemplate)

View File

@ -88,8 +88,7 @@ public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
boolean trigger = TRIGGER_ICONS.matcher(icon).matches();
buildChannel(SENSOR_CHANNEL_ID, value, channelConfiguration.getName(),
getListener(componentConfiguration, value))
buildChannel(SENSOR_CHANNEL_ID, value, getName(), getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.trigger(trigger).build();
}

View File

@ -294,7 +294,7 @@ public class Vacuum extends AbstractComponent<Vacuum.ChannelConfiguration> {
ChannelStateUpdateListener channelStateUpdateListener, @Nullable String commandTemplate,
@Nullable String commandTopic, @Nullable String stateTemplate, @Nullable String stateTopic) {
if ((commandTopic != null && !commandTopic.isBlank()) || (stateTopic != null && !stateTopic.isBlank())) {
return buildChannel(channelId, valueState, channelConfiguration.getName(), channelStateUpdateListener)
return buildChannel(channelId, valueState, getName(), channelStateUpdateListener)
.stateTopic(stateTopic, stateTemplate, channelConfiguration.getValueTemplate())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos(),
commandTemplate)

View File

@ -33,8 +33,9 @@ import com.google.gson.annotations.SerializedName;
@NonNullByDefault
public abstract class AbstractChannelConfiguration {
public static final char PARENT_TOPIC_PLACEHOLDER = '~';
private static final String DEFAULT_THING_NAME = "Home Assistant Device";
protected String name;
protected @Nullable String name;
protected String icon = "";
protected int qos; // defaults to 0 according to HA specification
@ -93,6 +94,9 @@ public abstract class AbstractChannelConfiguration {
if (result == null) {
result = name;
}
if (result == null) {
result = DEFAULT_THING_NAME;
}
return result;
}
@ -127,7 +131,7 @@ public abstract class AbstractChannelConfiguration {
return properties;
}
public String getName() {
public @Nullable String getName() {
return name;
}

View File

@ -18,6 +18,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@ -44,6 +45,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurati
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
@ -53,7 +55,6 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelDefinition;
import org.openhab.core.thing.type.ChannelGroupDefinition;
import org.openhab.core.thing.type.ChannelGroupType;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.util.ThingHelper;
import org.slf4j.Logger;
@ -93,7 +94,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
protected final DiscoverComponents discoverComponents;
private final Gson gson;
protected final Map<String, AbstractComponent<?>> haComponents = new HashMap<>();
protected final Map<@Nullable String, AbstractComponent<?>> haComponents = new HashMap<>();
protected HandlerConfiguration config = new HandlerConfiguration();
private Set<HaID> discoveryHomeAssistantIDs = new HashSet<>();
@ -137,10 +138,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
for (Channel channel : thing.getChannels()) {
final String groupID = channel.getUID().getGroupId();
if (groupID == null) {
logger.warn("Channel {} has no groupd ID", channel.getLabel());
continue;
}
// Already restored component?
@Nullable
AbstractComponent<?> component = haComponents.get(groupID);
@ -160,7 +157,12 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
try {
component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
scheduler, gson, transformationServiceProvider);
haComponents.put(component.getGroupUID().getId(), component);
final ChannelGroupUID groupUID = component.getGroupUID();
String id = null;
if (groupUID != null) {
id = groupUID.getId();
}
haComponents.put(id, component);
component.addChannelTypes(channelTypeProvider);
} catch (ConfigurationException e) {
logger.error("Cannot not restore component {}: {}", thing, e.getMessage());
@ -228,9 +230,6 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
@Override
public @Nullable ChannelState getChannelState(ChannelUID channelUID) {
String groupID = channelUID.getGroupId();
if (groupID == null) {
return null;
}
AbstractComponent<?> component;
synchronized (haComponents) { // sync whenever discoverComponents is started
component = haComponents.get(groupID);
@ -266,7 +265,12 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
synchronized (haComponents) { // sync whenever discoverComponents is started
for (AbstractComponent<?> discovered : discoveredComponentsList) {
AbstractComponent<?> known = haComponents.get(discovered.getGroupUID().getId());
final ChannelGroupUID groupUID = discovered.getGroupUID();
String id = null;
if (groupUID != null) {
id = groupUID.getId();
}
AbstractComponent<?> known = haComponents.get(id);
// Is component already known?
if (known != null) {
if (discovered.getConfigHash() != known.getConfigHash()) {
@ -282,10 +286,10 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
// Add channel and group types to the types registry
discovered.addChannelTypes(channelTypeProvider);
// Add component to the component map
haComponents.put(discovered.getGroupUID().getId(), discovered);
haComponents.put(id, discovered);
// Start component / Subscribe to channel topics
discovered.start(connection, scheduler, 0).exceptionally(e -> {
logger.warn("Failed to start component {}", discovered.getGroupUID(), e);
logger.warn("Failed to start component {}", discovered.getHaID(), e);
return null;
});
@ -296,7 +300,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
// We remove all conflicting old channels, they will be re-added below based on the new discovery
logger.debug(
"Received component {} with slightly different config. Making sure we re-create conflicting channels...",
discovered.getGroupUID());
discovered.getHaID());
removeJustRediscoveredChannels(discoveredChannels);
}
@ -346,9 +350,8 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
List<ChannelDefinition> channelDefs;
synchronized (haComponents) { // sync whenever discoverComponents is started
groupDefs = haComponents.values().stream().map(AbstractComponent::getGroupDefinition)
.collect(Collectors.toList());
channelDefs = haComponents.values().stream().map(AbstractComponent::getType)
.map(ChannelGroupType::getChannelDefinitions).flatMap(List::stream)
.filter(Objects::nonNull).map(Objects::requireNonNull).collect(Collectors.toList());
channelDefs = haComponents.values().stream().map(AbstractComponent::getChannels).flatMap(List::stream)
.collect(Collectors.toList());
}
ThingType thingType = channelTypeProvider.derive(typeID, MqttBindingConstants.HOMEASSISTANT_MQTT_THING)

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
@ -160,7 +161,8 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
ComponentDiscovered cd = (haID, c) -> {
haComponents.put(c.getGroupUID().getId(), c);
c.addChannelTypes(channelTypeProvider);
channelTypeProvider.setChannelGroupType(c.getGroupTypeUID(), c.getType());
channelTypeProvider.setChannelGroupType(Objects.requireNonNull(c.getGroupTypeUID()),
Objects.requireNonNull(c.getType()));
latch.countDown();
};