]> git.basschouten.com Git - openhab-addons.git/commitdiff
[MQTT.Homeassistant] process errors in MQTT message handlers during components discov...
authorantroids <36043354+antroids@users.noreply.github.com>
Sun, 24 Oct 2021 09:51:48 +0000 (11:51 +0200)
committerGitHub <noreply@github.com>
Sun, 24 Oct 2021 09:51:48 +0000 (11:51 +0200)
Signed-off-by: Anton Kharuzhy <publicantroids@gmail.com>
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.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/Switch.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/config/ConnectionDeserializer.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/discovery/HomeAssistantDiscovery.java
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/ConfigurationException.java [new file with mode: 0644]
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/UnsupportedComponentException.java [new file with mode: 0644]
bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java
bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandlerTests.java

index a24c5cabb418a206f1160ed4ded7653f07e96111..03c8b03387de4dc9605458a9b4898d49975052da 100644 (file)
@@ -29,6 +29,8 @@ import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
 import org.openhab.binding.mqtt.generic.utils.FutureCollector;
 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.exception.ConfigurationException;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
 import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
 import org.openhab.core.thing.ThingUID;
@@ -97,18 +99,27 @@ public class DiscoverComponents implements MqttMessageSubscriber {
         AbstractComponent<?> component = null;
 
         if (config.length() > 0) {
-            component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
-                    gson, transformationServiceProvider);
-        }
-        if (component != null) {
-            component.setConfigSeen();
-
-            logger.trace("Found HomeAssistant thing {} component {}", haID.objectID, haID.component);
-            if (discoveredListener != null) {
-                discoveredListener.componentDiscovered(haID, component);
+            try {
+                component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
+                        gson, transformationServiceProvider);
+                component.setConfigSeen();
+
+                logger.trace("Found HomeAssistant thing {} component {}", haID.objectID, haID.component);
+
+                if (discoveredListener != null) {
+                    discoveredListener.componentDiscovered(haID, component);
+                }
+            } catch (UnsupportedComponentException e) {
+                logger.warn("HomeAssistant discover error: thing {} component type is unsupported: {}", haID.objectID,
+                        haID.component);
+            } catch (ConfigurationException e) {
+                logger.warn("HomeAssistant discover error: invalid configuration of thing {} component {}: {}",
+                        haID.objectID, haID.component, e.getMessage());
+            } catch (Exception e) {
+                logger.warn("HomeAssistant discover error: {}", e.getMessage());
             }
         } else {
-            logger.debug("Configuration of HomeAssistant thing {} invalid: {}", haID.objectID, config);
+            logger.warn("Configuration of HomeAssistant thing {} is empty", haID.objectID);
         }
     }
 
index bed894391b3c2df2619b317612fdf5e81080d4a2..5be1b36e3bae1dc012166bcd9e131a2bca14f740 100644 (file)
@@ -21,6 +21,8 @@ import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
 import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
 import org.openhab.binding.mqtt.homeassistant.internal.HaID;
 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
 import org.openhab.core.thing.ThingUID;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,40 +50,36 @@ public class ComponentFactory {
      * @param updateListener A channel state update listener
      * @return A HA MQTT Component
      */
-    public static @Nullable AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID,
-            String channelConfigurationJSON, ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
-            ScheduledExecutorService scheduler, Gson gson,
-            TransformationServiceProvider transformationServiceProvider) {
+    public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID, String channelConfigurationJSON,
+            ChannelStateUpdateListener updateListener, AvailabilityTracker tracker, ScheduledExecutorService scheduler,
+            Gson gson, TransformationServiceProvider transformationServiceProvider) throws ConfigurationException {
         ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
                 channelConfigurationJSON, gson, updateListener, tracker, scheduler)
                         .transformationProvider(transformationServiceProvider);
-        try {
-            switch (haID.component) {
-                case "alarm_control_panel":
-                    return new AlarmControlPanel(componentConfiguration);
-                case "binary_sensor":
-                    return new BinarySensor(componentConfiguration);
-                case "camera":
-                    return new Camera(componentConfiguration);
-                case "cover":
-                    return new Cover(componentConfiguration);
-                case "fan":
-                    return new Fan(componentConfiguration);
-                case "climate":
-                    return new Climate(componentConfiguration);
-                case "light":
-                    return new Light(componentConfiguration);
-                case "lock":
-                    return new Lock(componentConfiguration);
-                case "sensor":
-                    return new Sensor(componentConfiguration);
-                case "switch":
-                    return new Switch(componentConfiguration);
-            }
-        } catch (UnsupportedOperationException e) {
-            LOGGER.warn("Not supported", e);
+        switch (haID.component) {
+            case "alarm_control_panel":
+                return new AlarmControlPanel(componentConfiguration);
+            case "binary_sensor":
+                return new BinarySensor(componentConfiguration);
+            case "camera":
+                return new Camera(componentConfiguration);
+            case "cover":
+                return new Cover(componentConfiguration);
+            case "fan":
+                return new Fan(componentConfiguration);
+            case "climate":
+                return new Climate(componentConfiguration);
+            case "light":
+                return new Light(componentConfiguration);
+            case "lock":
+                return new Lock(componentConfiguration);
+            case "sensor":
+                return new Sensor(componentConfiguration);
+            case "switch":
+                return new Switch(componentConfiguration);
+            default:
+                throw new UnsupportedComponentException("Component '" + haID + "' is unsupported!");
         }
-        return null;
     }
 
     protected static class ComponentConfiguration {
index 41148fc0178f6cff4210f5529ed2b6c1fac35dc2..b9078a3e404b2b5927e878561cc64ba246e94845 100644 (file)
@@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.mqtt.generic.values.OnOffValue;
 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -53,7 +54,7 @@ public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
 
         // We do not support all HomeAssistant quirks
         if (channelConfiguration.optimistic && !channelConfiguration.stateTopic.isBlank()) {
-            throw new UnsupportedOperationException("Component:Lock does not support forced optimistic mode");
+            throw new ConfigurationException("Component:Lock does not support forced optimistic mode");
         }
 
         buildChannel(SWITCH_CHANNEL_ID,
index be28b1fbc589e5fa390735cf7b9b1339533ed7ba..87faa217fc2f19a98b62ad6a6078e54bd002e6d1 100644 (file)
@@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.mqtt.generic.values.OnOffValue;
 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -65,7 +66,7 @@ public class Switch extends AbstractComponent<Switch.ChannelConfiguration> {
                 : channelConfiguration.stateTopic.isBlank();
 
         if (optimistic && !channelConfiguration.stateTopic.isBlank()) {
-            throw new UnsupportedOperationException("Component:Switch does not support forced optimistic mode");
+            throw new ConfigurationException("Component:Switch does not support forced optimistic mode");
         }
 
         String stateOn = channelConfiguration.stateOn != null ? channelConfiguration.stateOn
index fcb8a24aeb49fd5ab15ae087a8f25545fd4a25c5..994a95b36bc89e7dd5ce1d780c651319b5d6cf88 100644 (file)
@@ -37,16 +37,18 @@ public class ConnectionDeserializer implements JsonDeserializer<Connection> {
             throws JsonParseException {
         JsonArray list;
         if (json == null) {
-            throw new JsonParseException("JSON element is null");
+            throw new JsonParseException("JSON element is null, but must be connection definition.");
         }
         try {
             list = json.getAsJsonArray();
         } catch (IllegalStateException e) {
-            throw new JsonParseException("Cannot parse JSON array", e);
+            throw new JsonParseException("Cannot parse JSON array. Each connection must be defined as array with two "
+                    + "elements: connection_type, connection identifier. For example: \"connections\": [[\"mac\", "
+                    + "\"02:5b:26:a8:dc:12\"]]", e);
         }
         if (list.size() != 2) {
-            throw new JsonParseException(
-                    "Connection information must be a tuple, but has " + list.size() + " elements!");
+            throw new JsonParseException("Connection information must be a tuple, but has " + list.size()
+                    + " elements! For example: " + "\"connections\": [[\"mac\", \"02:5b:26:a8:dc:12\"]]");
         }
         return new Connection(list.get(0).getAsString(), list.get(1).getAsString());
     }
index b195bb7d65d172dda87e8825bc993fc8564b7996..a34d303ff6b5305c1d3b618aa9239fd80a8caa6a 100644 (file)
@@ -14,14 +14,15 @@ package org.openhab.binding.mqtt.homeassistant.internal.config.dto;
 
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.util.UIDUtils;
 
 import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
 import com.google.gson.annotations.SerializedName;
 
 /**
@@ -199,6 +200,15 @@ public abstract class AbstractChannelConfiguration {
      */
     public static <C extends AbstractChannelConfiguration> C fromString(final String configJSON, final Gson gson,
             final Class<C> clazz) {
-        return Objects.requireNonNull(gson.fromJson(configJSON, clazz));
+        try {
+            @Nullable
+            final C config = gson.fromJson(configJSON, clazz);
+            if (config == null) {
+                throw new ConfigurationException("Channel configuration is empty");
+            }
+            return config;
+        } catch (JsonSyntaxException e) {
+            throw new ConfigurationException("Cannot parse channel configuration JSON", e);
+        }
     }
 }
index 401ee3b1814667a503f2c459cad31a9dd96f7cfc..40dff2642854c26d4394343a90d6aba93e95d703 100644 (file)
@@ -36,6 +36,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.HaID;
 import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
 import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
+import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
 import org.openhab.core.config.discovery.DiscoveryResult;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
 import org.openhab.core.config.discovery.DiscoveryService;
@@ -146,43 +147,50 @@ public class HomeAssistantDiscovery extends AbstractMQTTDiscovery {
         }
         this.future = scheduler.schedule(this::publishResults, 2, TimeUnit.SECONDS);
 
-        AbstractChannelConfiguration config = AbstractChannelConfiguration
-                .fromString(new String(payload, StandardCharsets.UTF_8), gson);
-
         // We will of course find multiple of the same unique Thing IDs, for each different component another one.
         // Therefore the components are assembled into a list and given to the DiscoveryResult label for the user to
         // easily recognize object capabilities.
-
         HaID haID = new HaID(topic);
-        final String thingID = config.getThingId(haID.objectID);
 
-        final ThingTypeUID typeID = new ThingTypeUID(MqttBindingConstants.BINDING_ID,
-                MqttBindingConstants.HOMEASSISTANT_MQTT_THING.getId() + "_" + thingID);
+        try {
+            AbstractChannelConfiguration config = AbstractChannelConfiguration
+                    .fromString(new String(payload, StandardCharsets.UTF_8), gson);
+
+            final String thingID = config.getThingId(haID.objectID);
 
-        final ThingUID thingUID = new ThingUID(typeID, connectionBridge, thingID);
+            final ThingTypeUID typeID = new ThingTypeUID(MqttBindingConstants.BINDING_ID,
+                    MqttBindingConstants.HOMEASSISTANT_MQTT_THING.getId() + "_" + thingID);
 
-        thingIDPerTopic.put(topic, thingUID);
+            final ThingUID thingUID = new ThingUID(typeID, connectionBridge, thingID);
 
-        // We need to keep track of already found component topics for a specific thing
-        Set<HaID> components = componentsPerThingID.computeIfAbsent(thingID, key -> ConcurrentHashMap.newKeySet());
-        components.add(haID);
+            thingIDPerTopic.put(topic, thingUID);
 
-        final String componentNames = components.stream().map(id -> id.component)
-                .map(c -> HA_COMP_TO_NAME.getOrDefault(c, c)).collect(Collectors.joining(", "));
+            // We need to keep track of already found component topics for a specific thing
+            Set<HaID> components = componentsPerThingID.computeIfAbsent(thingID, key -> ConcurrentHashMap.newKeySet());
+            components.add(haID);
 
-        final List<String> topics = components.stream().map(HaID::toShortTopic).collect(Collectors.toList());
+            final String componentNames = components.stream().map(id -> id.component)
+                    .map(c -> HA_COMP_TO_NAME.getOrDefault(c, c)).collect(Collectors.joining(", "));
 
-        Map<String, Object> properties = new HashMap<>();
-        HandlerConfiguration handlerConfig = new HandlerConfiguration(haID.baseTopic, topics);
-        properties = handlerConfig.appendToProperties(properties);
-        properties = config.appendToProperties(properties);
-        properties.put("deviceId", thingID);
+            final List<String> topics = components.stream().map(HaID::toShortTopic).collect(Collectors.toList());
 
-        // Because we need the new properties map with the updated "components" list
-        results.put(thingUID.getAsString(),
-                DiscoveryResultBuilder.create(thingUID).withProperties(properties)
-                        .withRepresentationProperty("deviceId").withBridge(connectionBridge)
-                        .withLabel(config.getThingName() + " (" + componentNames + ")").build());
+            Map<String, Object> properties = new HashMap<>();
+            HandlerConfiguration handlerConfig = new HandlerConfiguration(haID.baseTopic, topics);
+            properties = handlerConfig.appendToProperties(properties);
+            properties = config.appendToProperties(properties);
+            properties.put("deviceId", thingID);
+
+            // Because we need the new properties map with the updated "components" list
+            results.put(thingUID.getAsString(),
+                    DiscoveryResultBuilder.create(thingUID).withProperties(properties)
+                            .withRepresentationProperty("deviceId").withBridge(connectionBridge)
+                            .withLabel(config.getThingName() + " (" + componentNames + ")").build());
+        } catch (ConfigurationException e) {
+            logger.warn("HomeAssistant discover error: invalid configuration of thing {} component {}: {}",
+                    haID.objectID, haID.component, e.getMessage());
+        } catch (Exception e) {
+            logger.warn("HomeAssistant discover error: {}", e.getMessage());
+        }
     }
 
     protected void publishResults() {
diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/ConfigurationException.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/ConfigurationException.java
new file mode 100644 (file)
index 0000000..d6b902c
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.exception;
+
+/**
+ * Exception class for errors in HomeAssistant components configurations
+ *
+ * @author Anton Kharuzhy - Initial contribution
+ */
+public class ConfigurationException extends RuntimeException {
+    public ConfigurationException(String message) {
+        super(message);
+    }
+
+    public ConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/UnsupportedComponentException.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/exception/UnsupportedComponentException.java
new file mode 100644 (file)
index 0000000..84d2413
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.exception;
+
+/**
+ * Exception class for unsupported components
+ *
+ * @author Anton Kharuzhy - Initial contribution
+ */
+public class UnsupportedComponentException extends ConfigurationException {
+    public UnsupportedComponentException(String message) {
+        super(message);
+    }
+
+    public UnsupportedComponentException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
index bc66b1ca9789fb944d6f494bbd37ed462cd99318..2d40882aba3a9af5eb4f5144781d84f991942106 100644 (file)
@@ -40,6 +40,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
 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.config.ChannelConfigurationTypeAdapterFactory;
+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.ChannelUID;
@@ -153,15 +154,14 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
             if (channelConfigurationJSON == null) {
                 logger.warn("Provided channel does not have a 'config' configuration key!");
             } else {
-                component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
-                        scheduler, gson, transformationServiceProvider);
-            }
-
-            if (component != null) {
-                haComponents.put(component.getGroupUID().getId(), component);
-                component.addChannelTypes(channelTypeProvider);
-            } else {
-                logger.warn("Could not restore component {}", thing);
+                try {
+                    component = ComponentFactory.createComponent(thingUID, haID, channelConfigurationJSON, this, this,
+                            scheduler, gson, transformationServiceProvider);
+                    haComponents.put(component.getGroupUID().getId(), component);
+                    component.addChannelTypes(channelTypeProvider);
+                } catch (ConfigurationException e) {
+                    logger.error("Cannot not restore component {}: {}", thing, e.getMessage());
+                }
             }
         }
         updateThingType();
index c1e0bd813114c4bd188ffeb26af096e3e37c812f..a72ab74517be60eb39be33384f0055fd640df231 100644 (file)
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -152,4 +153,34 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
         // Expect channel group types removed, 1 for each component
         verify(channelTypeProvider, times(2)).removeChannelGroupType(any());
     }
+
+    @Test
+    public void testProcessMessageFromUnsupportedComponent() {
+        thingHandler.initialize();
+        thingHandler.discoverComponents.processMessage("homeassistant/unsupportedType/id_zigbee2mqtt/config",
+                "{}".getBytes(StandardCharsets.UTF_8));
+        // Ignore unsupported component
+        thingHandler.delayedProcessing.forceProcessNow();
+        assertThat(haThing.getChannels().size(), CoreMatchers.is(0));
+    }
+
+    @Test
+    public void testProcessMessageWithEmptyConfig() {
+        thingHandler.initialize();
+        thingHandler.discoverComponents.processMessage("homeassistant/sensor/id_zigbee2mqtt/config",
+                "".getBytes(StandardCharsets.UTF_8));
+        // Ignore component with empty config
+        thingHandler.delayedProcessing.forceProcessNow();
+        assertThat(haThing.getChannels().size(), CoreMatchers.is(0));
+    }
+
+    @Test
+    public void testProcessMessageWithBadFormatConfig() {
+        thingHandler.initialize();
+        thingHandler.discoverComponents.processMessage("homeassistant/sensor/id_zigbee2mqtt/config",
+                "{bad format}}".getBytes(StandardCharsets.UTF_8));
+        // Ignore component with bad format config
+        thingHandler.delayedProcessing.forceProcessNow();
+        assertThat(haThing.getChannels().size(), CoreMatchers.is(0));
+    }
 }