]> git.basschouten.com Git - openhab-addons.git/commitdiff
[mqtt.homeassistant] handle null component name (#15427)
authorCody Cutrer <cody@cutrer.us>
Sat, 18 Nov 2023 21:46:57 +0000 (14:46 -0700)
committerGitHub <noreply@github.com>
Sat, 18 Nov 2023 21:46:57 +0000 (22:46 +0100)
* [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>
18 files changed:
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentChannel.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponent.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AlarmControlPanel.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Camera.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Cover.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DefaultSchemaLight.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/DeviceTrigger.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Fan.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/JSONSchemaLight.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Lock.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Number.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Select.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Sensor.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Vacuum.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/config/dto/AbstractChannelConfiguration.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java
itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/HomeAssistantMQTTImplementationTest.java

index b016a6960e0bdbb470c2ac7f29fdb0661260c47e..d63a304d5efc7ca4fabee9420034e4abcb394586 100644 (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(
index 203218baa75aeed676c39b3431294a5b275c8384..87625305d300b84a1977ba1f758305761587f55b 100644 (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() {
index 8d213e7075cd87e3479c7e6b0f65612977844ca2..5f60eec5fad5364a339b7e96ed91210d786ec3db 100644 (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();
         }
index c66119ff53bdf802836c8da712bb07c1eb91665d..bcf12f36087f716197b57363d74e9a34d8a4c683 100644 (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();
     }
 }
index a2ad15e81b64ae51ff8088e97d650ae1745153bc..5e68b56689ade951c5e00195295bf72f61bde23d 100644 (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)
index ecfa3a48c69193c3fcd43510d32e7353781185cc..8191f16c2ec6f9f597be6f987053dee11bcf03bd 100644 (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())
index 092b898a695f6c4e1810526155f5510f86a3be07..8b90631d3a93a984002f8ae26e03c86d05c52051 100644 (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:
index 657483fc538a7ec70f752b4fd3dd5096931f297f..e3c914bef2d9ea741af653d9f2254320009c261f 100644 (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();
     }
 }
index 49be4ee9238c6603f9a24b9501e2a84a41f7530a..7d056ed10ca38630d894f50c31dc135595a869d0 100644 (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)
index 8650018426da4777bd4b8511abf17cb559aea7db..5c89fc6e4dd4ce885fa3afd7de51592cd34748c5 100644 (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());
         }
     }
 }
index b50974a412aa57390136c5c82fad1f0fb2eec46d..ccfab925c9b4894e6317aeecf1f36e9e5b5a3f66 100644 (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())
index 947f46cc41a536a51c1f0b9d31edef950049b2d0..10519eb24312c272af44f3573e45c6c4ad12c91a 100644 (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)
index 9ead86ef8af4bb2c6fbfaea2b481ee435d831fe4..d49dfbf1a717252eb8ef31a3954cc0464aafa7db 100644 (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)
index f9f8706b5b108044ff6cac56548f7f0c88ad28c9..148a5b5e989abe3c7284eecef0392a9b5baabb5f 100644 (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();
     }
index 7fd9d6b901fb9cb19c6c850a0d78fa14fb4bf39d..75de1517cf03d6b7430e43ae710937bb84e02555 100644 (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)
index ae64e5d2445da4d7cc7fdd34bc70776089965c33..dec22608edb28499130c1b610e06cd65ba9a81a5 100644 (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;
     }
 
index 14612c5f67924c2d9efbc3bfa1aaa88f2f6127f5..99a14d4abd9c4fe3b55d73e121d152f1d1c72dd8 100644 (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)
index 4bf6be8374ce37fda99ec9d5b75d644588389f30..c514b3159e4eeb0f4cd5d33502de11051bae649e 100644 (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();
         };