]> git.basschouten.com Git - openhab-addons.git/commitdiff
[miele] Fix configuration validation and optimize performance (#12510)
authorJacob Laursen <jacob-github@vindvejr.dk>
Mon, 28 Mar 2022 05:14:55 +0000 (07:14 +0200)
committerGitHub <noreply@github.com>
Mon, 28 Mar 2022 05:14:55 +0000 (07:14 +0200)
* Update bridge status to UNKNOWN before scheduling polling job
* Reduced error log levels to warning
* Validate configuration and leave initialize() with status UNKNOWN
* Extract JSON-RPC handling to separate class
* Use already stored applianceId
* Keep listeners in Set to eliminate duplicates and improve performance
* Reduce access levels for variables exposed to subclasses unneededly
* Initialize translation provider with default language in constructor to avoid nullness
* Divide listener interface into two: discovery and appliance status
* Optimize communication between bridge and handlers
* Fix thing status transitions and support gone appliances
* Do not check/schedule jobs for each listener registration
* Optimize listener registration by using cached appliances
* Optimize state/property updates and reduce logging level for properties
* Fix concurrency issue during handler initialization
* Simplify online/offline transitions
* Use cached appliances for discovery listener callback
* Avoid unneeded call every 15 seconds receiving response with ~5.5 KB payload
* Fix devices being removed after failed RPC calls
* Fix method parameter descriptions

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
26 files changed:
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleGatewayCommunicationController.java [new file with mode: 0644]
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceStatusListener.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DiscoveryListener.java [new file with mode: 0644]
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishWasherHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishwasherChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerHandler.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineChannelSelector.java
bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineHandler.java
bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties

index 311ac6088e2a96a5dd3e27036bd7f021d2073ea0..b6ff6002b14a9ba7b12521268be105750769ebae 100644 (file)
@@ -89,7 +89,7 @@ public class DeviceUtil {
      * as well as built-in/translated strings.
      */
     public static State getStateTextState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+            MieleTranslationProvider translationProvider) {
         return getTextState(s, dmd, translationProvider, STATES, MISSING_STATE_TEXT_PREFIX, "");
     }
 
@@ -106,7 +106,7 @@ public class DeviceUtil {
      * @return Text string as State
      */
     public static State getTextState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider, Map<String, String> valueMap, String propertyPrefix,
+            MieleTranslationProvider translationProvider, Map<String, String> valueMap, String propertyPrefix,
             String appliancePrefix) {
         if ("0".equals(s)) {
             return UnDefType.UNDEF;
@@ -122,7 +122,7 @@ public class DeviceUtil {
         }
 
         String value = valueMap.get(s);
-        if (value != null && translationProvider != null) {
+        if (value != null) {
             String key = TEXT_PREFIX + propertyPrefix + appliancePrefix + value;
             return new StringType(
                     translationProvider.getText(key, gatewayText != null ? gatewayText : propertyPrefix + s));
diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleGatewayCommunicationController.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleGatewayCommunicationController.java
new file mode 100644 (file)
index 0000000..58f5363
--- /dev/null
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2010-2022 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.miele.internal;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+/**
+ * The {@link MieleGatewayCommunicationController} class is used for communicating with
+ * the XGW 3000 gateway through JSON-RPC.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class MieleGatewayCommunicationController {
+
+    private final URL url;
+    private final Random rand = new Random();
+    private final Gson gson = new Gson();
+    private final Logger logger = LoggerFactory.getLogger(MieleGatewayCommunicationController.class);
+
+    public MieleGatewayCommunicationController(String host) throws MalformedURLException {
+        url = new URL("http://" + host + "/remote/json-rpc");
+    }
+
+    public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
+            String methodName) throws MieleRpcException {
+        Object[] args = new Object[4];
+        args[0] = applianceIdentifier.getUid();
+        args[1] = MieleBindingConstants.MIELE_CLASS + modelID;
+        args[2] = methodName;
+        args[3] = null;
+
+        return invokeRPC("HDAccess/invokeDCOOperation", args);
+    }
+
+    public JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
+        JsonElement result = null;
+        JsonObject req = new JsonObject();
+        int id = rand.nextInt(Integer.MAX_VALUE);
+        req.addProperty("jsonrpc", "2.0");
+        req.addProperty("id", id);
+        req.addProperty("method", methodName);
+
+        JsonArray params = new JsonArray();
+        for (Object o : args) {
+            params.add(gson.toJsonTree(o));
+        }
+        req.add("params", params);
+
+        String requestData = req.toString();
+        String responseData = null;
+        try {
+            responseData = post(url, Collections.emptyMap(), requestData);
+        } catch (IOException e) {
+            throw new MieleRpcException("Exception occurred while posting data", e);
+        }
+
+        logger.trace("The request '{}' yields '{}'", requestData, responseData);
+        JsonObject parsedResponse = null;
+        try {
+            parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
+        } catch (JsonParseException e) {
+            throw new MieleRpcException("Error parsing JSON response", e);
+        }
+
+        JsonElement error = parsedResponse.get("error");
+        if (error != null && !error.isJsonNull()) {
+            if (error.isJsonPrimitive()) {
+                throw new MieleRpcException("Remote exception occurred: '" + error.getAsString() + "'");
+            } else if (error.isJsonObject()) {
+                JsonObject o = error.getAsJsonObject();
+                Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
+                String message = (o.has("message") ? o.get("message").getAsString() : null);
+                String data = (o.has("data")
+                        ? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
+                        : null);
+                throw new MieleRpcException(
+                        "Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
+            } else {
+                throw new MieleRpcException("Unknown remote exception occurred: '" + error.toString() + "'");
+            }
+        }
+
+        result = parsedResponse.get("result");
+        if (result == null) {
+            throw new MieleRpcException("Result is missing in response");
+        }
+
+        return result;
+    }
+
+    private String post(URL url, Map<String, String> headers, String data) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            connection.addRequestProperty(entry.getKey(), entry.getValue());
+        }
+
+        connection.addRequestProperty("Accept-Encoding", "gzip");
+        connection.setRequestMethod("POST");
+        connection.setDoOutput(true);
+        connection.connect();
+
+        OutputStream out = null;
+
+        try {
+            out = connection.getOutputStream();
+
+            out.write(data.getBytes());
+            out.flush();
+
+            int statusCode = connection.getResponseCode();
+            if (statusCode != HttpURLConnection.HTTP_OK) {
+                logger.debug("An unexpected status code was returned: '{}'", statusCode);
+            }
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+
+        String responseEncoding = connection.getHeaderField("Content-Encoding");
+        responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        InputStream in = connection.getInputStream();
+        try {
+            in = connection.getInputStream();
+            if ("gzip".equalsIgnoreCase(responseEncoding)) {
+                in = new GZIPInputStream(in);
+            }
+            in = new BufferedInputStream(in);
+
+            byte[] buff = new byte[1024];
+            int n;
+            while ((n = in.read(buff)) > 0) {
+                bos.write(buff, 0, n);
+            }
+            bos.flush();
+            bos.close();
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+
+        return bos.toString();
+    }
+}
index cf9bd30cfe921ac91a6a87438795a92c5d6337af..2677d36945731eb041e62f03239f8d682839e0f6 100644 (file)
@@ -23,10 +23,8 @@ import java.util.Set;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
-import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
-import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
 import org.openhab.binding.miele.internal.api.dto.HomeDevice;
-import org.openhab.binding.miele.internal.handler.ApplianceStatusListener;
+import org.openhab.binding.miele.internal.handler.DiscoveryListener;
 import org.openhab.binding.miele.internal.handler.MieleApplianceHandler;
 import org.openhab.binding.miele.internal.handler.MieleBridgeHandler;
 import org.openhab.core.config.discovery.AbstractDiscoveryService;
@@ -47,7 +45,7 @@ import org.slf4j.LoggerFactory;
  * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
  */
 @NonNullByDefault
-public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener {
+public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements DiscoveryListener {
 
     private final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class);
 
@@ -61,13 +59,13 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
     }
 
     public void activate() {
-        mieleBridgeHandler.registerApplianceStatusListener(this);
+        mieleBridgeHandler.registerDiscoveryListener(this);
     }
 
     @Override
     public void deactivate() {
         removeOlderResults(new Date().getTime());
-        mieleBridgeHandler.unregisterApplianceStatusListener(this);
+        mieleBridgeHandler.unregisterDiscoveryListener(this);
     }
 
     @Override
@@ -77,7 +75,7 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
 
     @Override
     public void startScan() {
-        List<HomeDevice> appliances = mieleBridgeHandler.getHomeDevices();
+        List<HomeDevice> appliances = mieleBridgeHandler.getHomeDevicesEmptyOnFailure();
         for (HomeDevice l : appliances) {
             onApplianceAddedInternal(l);
         }
@@ -146,16 +144,6 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
         }
     }
 
-    @Override
-    public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco) {
-        // nothing to do
-    }
-
-    @Override
-    public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp) {
-        // nothing to do
-    }
-
     private @Nullable ThingUID getThingUID(HomeDevice appliance) {
         ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID();
         String modelId = appliance.getDeviceClass();
index 0bbed76aabacb173eb97ab546ceaad080844d489..b9d5dd4885af0dc648fdeceec1d779b7a6b1109d 100644 (file)
@@ -64,7 +64,7 @@ public interface ApplianceChannelSelector {
      * @param dmd - the device meta data
      * @param translationProvider {@link MieleTranslationProvider} instance
      */
-    State getState(String s, @Nullable DeviceMetaData dmd, @Nullable MieleTranslationProvider translationProvider);
+    State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider);
 
     /**
      * Returns a State for the given string, taking into
index 7b81fcb38351f85c43365f56f593357b9cec7388..69cf25bf8abbd541e0bd913d970eec1e9912cbef 100644 (file)
 package org.openhab.binding.miele.internal.handler;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
 import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
 import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
 import org.openhab.binding.miele.internal.api.dto.HomeDevice;
 
 /**
- *
- * The {@link ApplianceStatusListener} is notified when an appliance status has changed or
- * an appliance has been removed or added.
+ * The {@link ApplianceStatusListener} is notified when the status for the subscribed
+ * appliance has changed or it has been removed or added.
  *
  * @author Karel Goderis - Initial contribution
  * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
@@ -32,30 +30,26 @@ public interface ApplianceStatusListener {
     /**
      * This method is called whenever the state of the given appliance has changed.
      *
-     * @param applianceIdentifier the fully qualified identifier of the appliance that has changed
      * @param dco the POJO containing the new state (properties and/or operations)
      */
-    void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco);
+    void onApplianceStateChanged(DeviceClassObject dco);
 
     /**
      * This method is called whenever a "property" of the given appliance has changed.
      *
-     * @param applianceIdentifier the fully qualified identifier of the appliance that has changed
      * @param dco the POJO containing the new state of the property
      */
-    void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp);
+    void onAppliancePropertyChanged(DeviceProperty dp);
 
     /**
      * This method is called whenever an appliance is removed.
-     *
-     * @param appliance The XGW homedevice definition of the appliance that was removed
      */
-    void onApplianceRemoved(HomeDevice appliance);
+    void onApplianceRemoved();
 
     /**
      * This method is called whenever an appliance is added.
      *
-     * @param appliance The XGW homedevice definition of the appliance that was removed
+     * @param appliance The XGW homedevice definition of the appliance that was added
      */
     void onApplianceAdded(HomeDevice appliance);
 }
index b65b6a1c3fbc489ee305178a236e5db28681199e..d4679caef0790ee668b6faa2f673766663d7f089 100644 (file)
@@ -45,16 +45,14 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
     STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
     PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
                     MIELE_COFFEE_MACHINE_TEXT_PREFIX);
         }
@@ -63,8 +61,7 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
     PROGRAMTYPE("programType", "type", StringType.class, false),
     PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
                     MIELE_COFFEE_MACHINE_TEXT_PREFIX);
         }
@@ -73,8 +70,7 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
     // lightingStatus signalFailure signalInfo
     DOOR("signalDoor", "door", OpenClosedType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -133,8 +129,7 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -163,7 +158,7 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 4aa51acb588af54d235171dfe0e5efc333631a08..77d14fac347a780d3e8bddaad45ea4cb403273ac 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_COFFEE_SYSTEM;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -52,7 +51,7 @@ public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineCha
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -64,7 +63,7 @@ public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineCha
         try {
             switch (selector) {
                 case SWITCH: {
-                    MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+                    MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
                     if (bridgeHandler == null) {
                         logger.warn("Command '{}' failed, missing bridge handler", command);
                         return;
diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DiscoveryListener.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DiscoveryListener.java
new file mode 100644 (file)
index 0000000..1f6a352
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2022 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.miele.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.api.dto.HomeDevice;
+
+/**
+ * The {@link DiscoveryListener} is notified when any appliance has been removed or added.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public interface DiscoveryListener {
+
+    /**
+     * This method is called whenever any appliance is removed.
+     *
+     * @param appliance The XGW homedevice definition of the appliance that was removed
+     */
+    void onApplianceRemoved(HomeDevice appliance);
+
+    /**
+     * This method is called whenever any appliance is added.
+     *
+     * @param appliance The XGW homedevice definition of the appliance that was added
+     */
+    void onApplianceAdded(HomeDevice appliance);
+}
index c5933f88ff49eb68e2c5e0ffdf6297c95a68dda9..4db8dbac3c4bd18e22c587db4f3a9ea23154890e 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_DISHWASHER;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.POWER_CONSUMPTION_CHANNEL_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID;
@@ -63,7 +62,7 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -75,7 +74,7 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
         try {
             switch (selector) {
                 case SWITCH: {
-                    MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+                    MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
                     if (bridgeHandler == null) {
                         logger.warn("Command '{}' failed, missing bridge handler", command);
                         return;
index 6f1f61b6ebe8d4c6b631a0913d54a99e8c4287c2..7c03a1bb198b3b4d14fdc04311d61475ec46fda3 100644 (file)
@@ -52,16 +52,14 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
     STATE("", STATE_CHANNEL_ID, DecimalType.class, false, false),
     PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
                     MIELE_DISHWASHER_TEXT_PREFIX);
         }
@@ -69,8 +67,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false, false),
     PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
                     MIELE_DISHWASHER_TEXT_PREFIX);
         }
@@ -78,8 +75,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false, false),
     START_TIME("startTime", "start", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -93,8 +89,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     },
     DURATION("duration", "duration", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -108,8 +103,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     },
     ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -123,8 +117,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     },
     FINISH_TIME("finishTime", "finish", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -138,8 +131,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -208,8 +200,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -238,7 +229,7 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 46b46084042372278f22b94ec5f19b434e85e6f9..1e3b7fd9d999879dfc9364f6162319a5d37bfb17 100644 (file)
@@ -45,8 +45,7 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
@@ -54,23 +53,20 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
     SUPERCOOL("", SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
     FRIDGECURRENTTEMP("currentTemperature", "current", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     FRIDGETARGETTEMP("targetTemperature", "target", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false) {
         @Override
 
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -124,8 +120,7 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -154,7 +149,7 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index a08ef29aa8b38729ac9a4f55599473e70deac551..36db42ad4cb10475008d236149fc188d3b69e69c 100644 (file)
@@ -46,8 +46,7 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
@@ -58,36 +57,31 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
     SUPERFREEZE("", SUPERFREEZE_CHANNEL_ID, OnOffType.class, false),
     FREEZERCURRENTTEMP("freezerCurrentTemperature", "freezercurrent", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     FREEZERTARGETTEMP("freezerTargetTemperature", "freezertarget", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     FRIDGECURRENTTEMP("fridgeCurrentTemperature", "fridgecurrent", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     FRIDGETARGETTEMP("fridgeTargetTemperature", "fridgetarget", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -142,8 +136,7 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -172,7 +165,7 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 3bfcd07974575eb92330fe5ede78b09006e9e98a..1e2c56e25e3d65fde694b84318ad3a74aec9cacd 100644 (file)
@@ -52,7 +52,7 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -62,7 +62,7 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
         JsonElement result = null;
 
         try {
-            MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+            MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
             if (bridgeHandler == null) {
                 logger.warn("Command '{}' failed, missing bridge handler", command);
                 return;
@@ -108,7 +108,7 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
     }
 
     @Override
-    protected void onAppliancePropertyChanged(DeviceProperty dp) {
+    public void onAppliancePropertyChanged(DeviceProperty dp) {
         super.onAppliancePropertyChanged(dp);
 
         if (!STATE_PROPERTY_NAME.equals(dp.Name)) {
index da8dea107a46b5e504507f9b72425fba425b0580..840e89143eebfc65e84df3baa003f50fd1cbf74c 100644 (file)
@@ -52,7 +52,7 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -62,7 +62,7 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
         JsonElement result = null;
 
         try {
-            MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+            MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
             if (bridgeHandler == null) {
                 logger.warn("Command '{}' failed, missing bridge handler", command);
                 return;
@@ -109,7 +109,7 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
     }
 
     @Override
-    protected void onAppliancePropertyChanged(DeviceProperty dp) {
+    public void onAppliancePropertyChanged(DeviceProperty dp) {
         super.onAppliancePropertyChanged(dp);
 
         if (!STATE_PROPERTY_NAME.equals(dp.Name)) {
index 4059931fc0d437a9a05c2717f7527b9942abb487..47b94affaaa3f3fa68fbaa971a4321df8293f0f7 100644 (file)
@@ -42,8 +42,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
@@ -52,8 +51,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE1_POWER("plate1PowerStep", "plate1power", DecimalType.class, false),
     PLATE1_HEAT("plate1RemainingHeat", "plate1heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             // If there is remaining heat, the device metadata contains some informative string which can not be
             // converted into a DecimalType. We therefore ignore the metadata and return the device property value as a
             // State
@@ -64,8 +62,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE2_POWER("plate2PowerStep", "plate2power", DecimalType.class, false),
     PLATE2_HEAT("plate2RemainingHeat", "plate2heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
@@ -73,8 +70,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE3_POWER("plate3PowerStep", "plate3power", DecimalType.class, false),
     PLATE3_HEAT("plate3RemainingHeat", "plate3heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
@@ -82,8 +78,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE4_POWER("plate4PowerStep", "plate4power", DecimalType.class, false),
     PLATE4_HEAT("plate4RemainingHeat", "plate4heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
@@ -91,8 +86,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE5_POWER("plate5PowerStep", "plate5power", DecimalType.class, false),
     PLATE5_HEAT("plate5RemainingHeat", "plate5heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
@@ -100,8 +94,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     PLATE6_POWER("plate6PowerStep", "plate6power", DecimalType.class, false),
     PLATE6_HEAT("plate6RemainingHeat", "plate6heat", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
@@ -147,8 +140,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -177,7 +169,7 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index f84eebc91941db4f227c280b1943d5c7067fe505..491f627fe38ef6d6359dc37048aa38ad8bbdba7d 100644 (file)
@@ -43,8 +43,7 @@ public enum HoodChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
@@ -53,8 +52,7 @@ public enum HoodChannelSelector implements ApplianceChannelSelector {
     LIGHT("lightingStatus", "light", OnOffType.class, false) {
         @Override
 
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("ON");
             }
@@ -108,8 +106,7 @@ public enum HoodChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -138,7 +135,7 @@ public enum HoodChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 83cb84ec7fafc292fe6545c3a8f29fbe7f43fc19..35491260ea583575b2d3501f72f77924ce89f530 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_HOOD;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -51,7 +50,7 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -61,7 +60,7 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
         JsonElement result = null;
 
         try {
-            MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+            MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
             if (bridgeHandler == null) {
                 logger.warn("Command '{}' failed, missing bridge handler", command);
                 return;
index 85aac472c97701089646c3951d42544e5b3a3fdf..929c7339a57cce9f39e5ddf7151d9285f797a254 100644 (file)
@@ -23,7 +23,6 @@ import java.util.Set;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.miele.internal.DeviceUtil;
-import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
 import org.openhab.binding.miele.internal.MieleTranslationProvider;
 import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
 import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
@@ -35,8 +34,9 @@ import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusInfo;
+import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
 import org.openhab.core.thing.binding.BaseThingHandler;
 import org.openhab.core.thing.binding.ThingHandler;
 import org.openhab.core.types.Command;
@@ -74,10 +74,10 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     protected Gson gson = new Gson();
 
     protected @Nullable String applianceId;
-    protected @Nullable MieleBridgeHandler bridgeHandler;
+    private @Nullable MieleBridgeHandler bridgeHandler;
     protected TranslationProvider i18nProvider;
     protected LocaleProvider localeProvider;
-    protected @Nullable MieleTranslationProvider translationProvider;
+    protected MieleTranslationProvider translationProvider;
     private Class<E> selectorType;
     protected String modelID;
 
@@ -90,6 +90,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
         this.localeProvider = localeProvider;
         this.selectorType = selectorType;
         this.modelID = modelID;
+        this.translationProvider = new MieleTranslationProvider(i18nProvider, localeProvider);
     }
 
     public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelectorText)
@@ -128,18 +129,24 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     public void initialize() {
         logger.debug("Initializing Miele appliance handler.");
         final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
-        if (applianceId != null) {
-            this.applianceId = applianceId;
-            this.onBridgeConnectionResumed();
+        if (applianceId == null || applianceId.isBlank()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
+                    "@text/offline.configuration-error.uid-not-set");
+            return;
         }
-    }
-
-    public void onBridgeConnectionResumed() {
+        this.applianceId = applianceId;
         Bridge bridge = getBridge();
-        if (bridge != null && getMieleBridgeHandler() != null) {
-            ThingStatusInfo statusInfo = bridge.getStatusInfo();
-            updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
-            initializeTranslationProvider(bridge);
+        if (bridge == null) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
+                    "@text/offline.configuration-error.bridge-missing");
+            return;
+        }
+        initializeTranslationProvider(bridge);
+        updateStatus(ThingStatus.UNKNOWN);
+
+        MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
+        if (bridgeHandler != null) {
+            bridgeHandler.registerApplianceStatusListener(applianceId, this);
         }
     }
 
@@ -150,7 +157,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
             try {
                 locale = new Locale.Builder().setLanguageTag(language).build();
             } catch (IllformedLocaleException e) {
-                logger.error("Invalid language configured: {}", e.getMessage());
+                logger.warn("Invalid language configured: {}", e.getMessage());
             }
         }
         if (locale == null) {
@@ -164,10 +171,11 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     @Override
     public void dispose() {
         logger.debug("Handler disposes. Unregistering listener.");
+        String applianceId = this.applianceId;
         if (applianceId != null) {
             MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
             if (bridgeHandler != null) {
-                bridgeHandler.unregisterApplianceStatusListener(this);
+                bridgeHandler.unregisterApplianceStatusListener(applianceId, this);
             }
             applianceId = null;
         }
@@ -183,13 +191,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     }
 
     @Override
-    public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier,
-            DeviceClassObject dco) {
-        String applianceId = this.applianceId;
-        if (applianceId == null || !applianceId.equals(applicationIdentifier.getApplianceId())) {
-            return;
-        }
-
+    public void onApplianceStateChanged(DeviceClassObject dco) {
         JsonArray properties = dco.Properties;
         if (properties == null) {
             return;
@@ -205,7 +207,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
                     dp.Value = dp.Value.trim();
                     dp.Value = dp.Value.strip();
                 }
-                onAppliancePropertyChanged(applicationIdentifier, dp);
+                onAppliancePropertyChanged(dp);
             } catch (Exception p) {
                 // Ignore - this is due to an unrecognized and not yet reverse-engineered array property
             }
@@ -213,17 +215,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     }
 
     @Override
-    public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) {
-        String applianceId = this.applianceId;
-
-        if (applianceId == null || !applianceId.equals(applicationIdentifier.getApplianceId())) {
-            return;
-        }
-
-        this.onAppliancePropertyChanged(dp);
-    }
-
-    protected void onAppliancePropertyChanged(DeviceProperty dp) {
+    public void onAppliancePropertyChanged(DeviceProperty dp) {
         try {
             DeviceMetaData dmd = null;
             if (dp.Metadata == null) {
@@ -271,24 +263,22 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
             String dpValue = dp.Value.strip().trim();
 
             if (selector != null) {
-                if (!selector.isProperty()) {
-                    ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID());
-
-                    State state = selector.getState(dpValue, dmd, this.translationProvider);
+                String channelId = selector.getChannelID();
+                ThingUID thingUid = getThing().getUID();
+                State state = selector.getState(dpValue, dmd, this.translationProvider);
+                if (selector.isProperty()) {
+                    String value = state.toString();
+                    logger.trace("Updating the property '{}' of '{}' to '{}'", channelId, thingUid, value);
+                    updateProperty(channelId, value);
+                } else {
+                    ChannelUID theChannelUID = new ChannelUID(thingUid, channelId);
                     logger.trace("Update state of {} with getState '{}'", theChannelUID, state);
                     updateState(theChannelUID, state);
                     updateRawChannel(dp.Name, dpValue);
-                } else {
-                    logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(),
-                            getThing().getUID(), selector.getState(dpValue, dmd, this.translationProvider).toString());
-                    Map<String, String> properties = editProperties();
-                    properties.put(selector.getChannelID(),
-                            selector.getState(dpValue, dmd, this.translationProvider).toString());
-                    updateProperties(properties);
                 }
             }
         } catch (IllegalArgumentException e) {
-            logger.error("An exception occurred while processing a changed device property :'{}'", e.getMessage());
+            logger.warn("An exception occurred while processing a changed device property: '{}'", e.getMessage());
         }
     }
 
@@ -329,65 +319,41 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
     }
 
     @Override
-    public void onApplianceRemoved(HomeDevice appliance) {
-        String applianceId = this.applianceId;
-        if (applianceId == null) {
-            return;
-        }
-
-        FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
-        if (applianceIdentifier == null) {
-            return;
-        }
-
-        if (applianceId.equals(applianceIdentifier.getApplianceId())) {
-            updateStatus(ThingStatus.OFFLINE);
-        }
+    public void onApplianceRemoved() {
+        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.GONE);
     }
 
     @Override
     public void onApplianceAdded(HomeDevice appliance) {
-        String applianceId = this.applianceId;
-        if (applianceId == null) {
-            return;
+        Map<String, String> properties = editProperties();
+        String vendor = appliance.Vendor;
+        if (vendor != null) {
+            properties.put(Thing.PROPERTY_VENDOR, vendor);
         }
-
-        FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
-        if (applianceIdentifier == null) {
-            return;
+        properties.put(Thing.PROPERTY_MODEL_ID, appliance.getApplianceModel());
+        properties.put(Thing.PROPERTY_SERIAL_NUMBER, appliance.getSerialNumber());
+        properties.put(Thing.PROPERTY_FIRMWARE_VERSION, appliance.getFirmwareVersion());
+        String protocolAdapterName = appliance.ProtocolAdapterName;
+        if (protocolAdapterName != null) {
+            properties.put(PROPERTY_PROTOCOL_ADAPTER, protocolAdapterName);
         }
-
-        if (applianceId.equals(applianceIdentifier.getApplianceId())) {
-            Map<String, String> properties = editProperties();
-            String vendor = appliance.Vendor;
-            if (vendor != null) {
-                properties.put(Thing.PROPERTY_VENDOR, vendor);
-            }
-            properties.put(Thing.PROPERTY_MODEL_ID, appliance.getApplianceModel());
-            properties.put(Thing.PROPERTY_SERIAL_NUMBER, appliance.getSerialNumber());
-            properties.put(Thing.PROPERTY_FIRMWARE_VERSION, appliance.getFirmwareVersion());
-            String protocolAdapterName = appliance.ProtocolAdapterName;
-            if (protocolAdapterName != null) {
-                properties.put(PROPERTY_PROTOCOL_ADAPTER, protocolAdapterName);
-            }
-            String deviceClass = appliance.getDeviceClass();
-            if (deviceClass != null) {
-                properties.put(PROPERTY_DEVICE_CLASS, deviceClass);
-            }
-            String connectionType = appliance.getConnectionType();
-            if (connectionType != null) {
-                properties.put(PROPERTY_CONNECTION_TYPE, connectionType);
-            }
-            String connectionBaudRate = appliance.getConnectionBaudRate();
-            if (connectionBaudRate != null) {
-                properties.put(PROPERTY_CONNECTION_BAUD_RATE, connectionBaudRate);
-            }
-            updateProperties(properties);
-            updateStatus(ThingStatus.ONLINE);
+        String deviceClass = appliance.getDeviceClass();
+        if (deviceClass != null) {
+            properties.put(PROPERTY_DEVICE_CLASS, deviceClass);
+        }
+        String connectionType = appliance.getConnectionType();
+        if (connectionType != null) {
+            properties.put(PROPERTY_CONNECTION_TYPE, connectionType);
         }
+        String connectionBaudRate = appliance.getConnectionBaudRate();
+        if (connectionBaudRate != null) {
+            properties.put(PROPERTY_CONNECTION_BAUD_RATE, connectionBaudRate);
+        }
+        updateProperties(properties);
+        updateStatus(ThingStatus.ONLINE);
     }
 
-    private synchronized @Nullable MieleBridgeHandler getMieleBridgeHandler() {
+    protected synchronized @Nullable MieleBridgeHandler getMieleBridgeHandler() {
         if (this.bridgeHandler == null) {
             Bridge bridge = getBridge();
             if (bridge == null) {
@@ -395,11 +361,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
             }
             ThingHandler handler = bridge.getHandler();
             if (handler instanceof MieleBridgeHandler) {
-                var bridgeHandler = (MieleBridgeHandler) handler;
-                this.bridgeHandler = bridgeHandler;
-                bridgeHandler.registerApplianceStatusListener(this);
-            } else {
-                return null;
+                this.bridgeHandler = (MieleBridgeHandler) handler;
             }
         }
         return this.bridgeHandler;
index e8a460bbfca7ce5f061fb81ecb889b7b1a1182a1..cf385ed7b0d7567eee5b1837f385e8550f6d74a7 100644 (file)
@@ -14,43 +14,33 @@ package org.openhab.binding.miele.internal.handler;
 
 import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
 
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StringReader;
 import java.net.DatagramPacket;
-import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.MulticastSocket;
 import java.net.SocketTimeoutException;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.IllformedLocaleException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
-import java.util.zip.GZIPInputStream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
+import org.openhab.binding.miele.internal.MieleGatewayCommunicationController;
 import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
 import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
 import org.openhab.binding.miele.internal.api.dto.HomeDevice;
@@ -59,7 +49,6 @@ import org.openhab.core.common.NamedThreadFactory;
 import org.openhab.core.config.core.Configuration;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.ThingTypeUID;
@@ -70,11 +59,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
-import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
 
 /**
  * The {@link MieleBridgeHandler} is responsible for handling commands, which are
@@ -93,26 +78,26 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
     private static final Pattern IP_PATTERN = Pattern
             .compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
 
-    protected static final int POLLING_PERIOD = 15; // in seconds
-    protected static final int JSON_RPC_PORT = 2810;
-    protected static final String JSON_RPC_MULTICAST_IP1 = "239.255.68.139";
-    protected static final String JSON_RPC_MULTICAST_IP2 = "224.255.68.139";
-    private boolean lastBridgeConnectionState = false;
-    private boolean currentBridgeConnectionState = false;
+    private static final int POLLING_PERIOD = 15; // in seconds
+    private static final int JSON_RPC_PORT = 2810;
+    private static final String JSON_RPC_MULTICAST_IP1 = "239.255.68.139";
+    private static final String JSON_RPC_MULTICAST_IP2 = "224.255.68.139";
 
-    protected Random rand = new Random();
-    protected Gson gson = new Gson();
     private final Logger logger = LoggerFactory.getLogger(MieleBridgeHandler.class);
 
-    protected List<ApplianceStatusListener> applianceStatusListeners = new CopyOnWriteArrayList<>();
-    protected @Nullable ScheduledFuture<?> pollingJob;
-    protected @Nullable ExecutorService executor;
-    protected @Nullable Future<?> eventListenerJob;
+    private boolean lastBridgeConnectionState = false;
+
+    private Gson gson = new Gson();
+    private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication;
 
-    protected Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<String, HomeDevice>();
-    protected Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<String, HomeDevice>();
+    private Set<DiscoveryListener> discoveryListeners = ConcurrentHashMap.newKeySet();
+    private Map<String, ApplianceStatusListener> applianceStatusListeners = new ConcurrentHashMap<>();
+    private @Nullable ScheduledFuture<?> pollingJob;
+    private @Nullable ExecutorService executor;
+    private @Nullable Future<?> eventListenerJob;
 
-    protected @Nullable URL url;
+    private Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<>();
+    private Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<>();
 
     public MieleBridgeHandler(Bridge bridge) {
         super(bridge);
@@ -127,16 +112,15 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
         }
 
         try {
-            url = new URL("http://" + (String) getConfig().get(HOST) + "/remote/json-rpc");
+            gatewayCommunication = new MieleGatewayCommunicationController((String) getConfig().get(HOST));
         } catch (MalformedURLException e) {
-            logger.debug("An exception occurred while defining an URL :'{}'", e.getMessage());
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
             return;
         }
 
-        onUpdate();
-        lastBridgeConnectionState = false;
         updateStatus(ThingStatus.UNKNOWN);
+        lastBridgeConnectionState = false;
+        schedulePollingAndEventListener();
     }
 
     private boolean validateConfig(Configuration config) {
@@ -177,94 +161,47 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
     private Runnable pollingRunnable = new Runnable() {
         @Override
         public void run() {
-            if (!IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) {
-                logger.debug("Invalid IP address for the Miele@Home gateway : '{}'", getConfig().get(HOST));
-                return;
-            }
-
+            String host = (String) getConfig().get(HOST);
             try {
-                if (isReachable((String) getConfig().get(HOST))) {
-                    currentBridgeConnectionState = true;
-                } else {
-                    currentBridgeConnectionState = false;
-                    lastBridgeConnectionState = false;
-                    onConnectionLost();
-                }
+                List<HomeDevice> homeDevices = getHomeDevices();
 
-                if (!lastBridgeConnectionState && currentBridgeConnectionState) {
-                    logger.debug("Connection to Miele Gateway {} established.", getConfig().get(HOST));
+                if (!lastBridgeConnectionState) {
+                    logger.debug("Connection to Miele Gateway {} established.", host);
                     lastBridgeConnectionState = true;
-                    onConnectionResumed();
-                }
-
-                if (!currentBridgeConnectionState || getThing().getStatus() != ThingStatus.ONLINE) {
-                    return;
                 }
-
-                List<HomeDevice> homeDevices = getHomeDevices();
-                for (HomeDevice hd : homeDevices) {
-                    String key = hd.getApplianceIdentifier().getApplianceId();
-                    if (!cachedHomeDevicesByApplianceId.containsKey(key)) {
-                        logger.debug("A new appliance with ID '{}' has been added", hd.UID);
-                        for (ApplianceStatusListener listener : applianceStatusListeners) {
-                            listener.onApplianceAdded(hd);
-                        }
+                updateStatus(ThingStatus.ONLINE);
+
+                refreshHomeDevices(homeDevices);
+
+                for (Entry<String, ApplianceStatusListener> entry : applianceStatusListeners.entrySet()) {
+                    String applianceId = entry.getKey();
+                    ApplianceStatusListener listener = entry.getValue();
+                    FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId(
+                            applianceId);
+                    if (applianceIdentifier == null) {
+                        logger.debug("The appliance with ID '{}' was not found in appliance list from bridge.",
+                                applianceId);
+                        listener.onApplianceRemoved();
+                        continue;
                     }
-                    cachedHomeDevicesByApplianceId.put(key, hd);
-                    cachedHomeDevicesByRemoteUid.put(hd.getRemoteUid(), hd);
-                }
 
-                Set<Entry<String, HomeDevice>> cachedEntries = cachedHomeDevicesByApplianceId.entrySet();
-                Iterator<Entry<String, HomeDevice>> iterator = cachedEntries.iterator();
+                    Object[] args = new Object[2];
+                    args[0] = applianceIdentifier.getUid();
+                    args[1] = true;
+                    JsonElement result = gatewayCommunication.invokeRPC("HDAccess/getDeviceClassObjects", args);
 
-                while (iterator.hasNext()) {
-                    Entry<String, HomeDevice> cachedEntry = iterator.next();
-                    HomeDevice cachedHomeDevice = cachedEntry.getValue();
-                    if (!homeDevices.stream().anyMatch(d -> d.UID.equals(cachedHomeDevice.UID))) {
-                        logger.debug("The appliance with ID '{}' has been removed", cachedHomeDevice.UID);
-                        for (ApplianceStatusListener listener : applianceStatusListeners) {
-                            listener.onApplianceRemoved(cachedHomeDevice);
-                        }
-                        cachedHomeDevicesByRemoteUid.remove(cachedHomeDevice.getRemoteUid());
-                        iterator.remove();
-                    }
-                }
-
-                for (Thing appliance : getThing().getThings()) {
-                    if (appliance.getStatus() == ThingStatus.ONLINE) {
-                        String applianceId = (String) appliance.getConfiguration().getProperties().get(APPLIANCE_ID);
-                        FullyQualifiedApplianceIdentifier applianceIdentifier = null;
-                        if (applianceId != null) {
-                            applianceIdentifier = getApplianceIdentifierFromApplianceId(applianceId);
-                        }
-
-                        if (applianceIdentifier == null) {
-                            logger.warn("The appliance with ID '{}' was not found in appliance list from bridge.",
-                                    applianceId);
-                            continue;
-                        }
-
-                        Object[] args = new Object[2];
-                        args[0] = applianceIdentifier.getUid();
-                        args[1] = true;
-                        JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args);
-
-                        for (JsonElement obj : result.getAsJsonArray()) {
-                            try {
-                                DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
-
-                                // Skip com.prosyst.mbs.services.zigbee.hdm.deviceclasses.ReportingControl
-                                if (dco == null || !dco.DeviceClass.startsWith(MIELE_CLASS)) {
-                                    continue;
-                                }
+                    for (JsonElement obj : result.getAsJsonArray()) {
+                        try {
+                            DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
 
-                                for (ApplianceStatusListener listener : applianceStatusListeners) {
-                                    listener.onApplianceStateChanged(applianceIdentifier, dco);
-                                }
-                            } catch (Exception e) {
-                                logger.debug("An exception occurred while querying an appliance : '{}'",
-                                        e.getMessage());
+                            // Skip com.prosyst.mbs.services.zigbee.hdm.deviceclasses.ReportingControl
+                            if (dco == null || !dco.DeviceClass.startsWith(MIELE_CLASS)) {
+                                continue;
                             }
+
+                            listener.onApplianceStateChanged(dco);
+                        } catch (Exception e) {
+                            logger.debug("An exception occurred while querying an appliance : '{}'", e.getMessage());
                         }
                     }
                 }
@@ -276,50 +213,85 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                     logger.debug("An exception occurred while polling an appliance: '{}' -> '{}'", e.getMessage(),
                             cause.getMessage());
                 }
+                if (lastBridgeConnectionState) {
+                    logger.debug("Connection to Miele Gateway {} lost.", host);
+                    lastBridgeConnectionState = false;
+                }
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR);
             }
         }
+    };
 
-        private boolean isReachable(String ipAddress) {
-            try {
-                // note that InetAddress.isReachable is unreliable, see
-                // http://stackoverflow.com/questions/9922543/why-does-inetaddress-isreachable-return-false-when-i-can-ping-the-ip-address
-                // That's why we do an HTTP access instead
+    private synchronized void refreshHomeDevices(List<HomeDevice> homeDevices) {
+        for (HomeDevice hd : homeDevices) {
+            String key = hd.getApplianceIdentifier().getApplianceId();
+            if (!cachedHomeDevicesByApplianceId.containsKey(key)) {
+                logger.debug("A new appliance with ID '{}' has been added", hd.UID);
+                for (DiscoveryListener listener : discoveryListeners) {
+                    listener.onApplianceAdded(hd);
+                }
+                ApplianceStatusListener listener = applianceStatusListeners
+                        .get(hd.getApplianceIdentifier().getApplianceId());
+                if (listener != null) {
+                    listener.onApplianceAdded(hd);
+                }
+            }
+            cachedHomeDevicesByApplianceId.put(key, hd);
+            cachedHomeDevicesByRemoteUid.put(hd.getRemoteUid(), hd);
+        }
 
-                // If there is no connection, this line will fail
-                invokeRPC("system.listMethods", new Object[0]);
-            } catch (MieleRpcException e) {
-                logger.debug("{} is not reachable", ipAddress);
-                return false;
+        Set<Entry<String, HomeDevice>> cachedEntries = cachedHomeDevicesByApplianceId.entrySet();
+        Iterator<Entry<String, HomeDevice>> iterator = cachedEntries.iterator();
+
+        while (iterator.hasNext()) {
+            Entry<String, HomeDevice> cachedEntry = iterator.next();
+            HomeDevice cachedHomeDevice = cachedEntry.getValue();
+            if (!homeDevices.stream().anyMatch(d -> d.UID.equals(cachedHomeDevice.UID))) {
+                logger.debug("The appliance with ID '{}' has been removed", cachedHomeDevice.UID);
+                for (DiscoveryListener listener : discoveryListeners) {
+                    listener.onApplianceRemoved(cachedHomeDevice);
+                }
+                ApplianceStatusListener listener = applianceStatusListeners
+                        .get(cachedHomeDevice.getApplianceIdentifier().getApplianceId());
+                if (listener != null) {
+                    listener.onApplianceRemoved();
+                }
+                cachedHomeDevicesByRemoteUid.remove(cachedHomeDevice.getRemoteUid());
+                iterator.remove();
             }
+        }
+    }
 
-            logger.debug("{} is reachable", ipAddress);
-            return true;
+    public List<HomeDevice> getHomeDevicesEmptyOnFailure() {
+        try {
+            return getHomeDevices();
+        } catch (MieleRpcException e) {
+            Throwable cause = e.getCause();
+            if (cause == null) {
+                logger.debug("An exception occurred while getting the home devices: '{}'", e.getMessage());
+            } else {
+                logger.debug("An exception occurred while getting the home devices: '{}' -> '{}", e.getMessage(),
+                        cause.getMessage());
+            }
+            return new ArrayList<>();
         }
-    };
+    }
 
-    public List<HomeDevice> getHomeDevices() {
+    private List<HomeDevice> getHomeDevices() throws MieleRpcException {
         List<HomeDevice> devices = new ArrayList<>();
 
-        if (getThing().getStatus() == ThingStatus.ONLINE) {
-            try {
-                String[] args = new String[1];
-                args[0] = "(type=SuperVision)";
-                JsonElement result = invokeRPC("HDAccess/getHomeDevices", args);
-
-                for (JsonElement obj : result.getAsJsonArray()) {
-                    HomeDevice hd = gson.fromJson(obj, HomeDevice.class);
-                    if (hd != null) {
-                        devices.add(hd);
-                    }
-                }
-            } catch (MieleRpcException e) {
-                Throwable cause = e.getCause();
-                if (cause == null) {
-                    logger.debug("An exception occurred while getting the home devices: '{}'", e.getMessage());
-                } else {
-                    logger.debug("An exception occurred while getting the home devices: '{}' -> '{}", e.getMessage(),
-                            cause.getMessage());
-                }
+        if (!isInitialized()) {
+            return devices;
+        }
+
+        String[] args = new String[1];
+        args[0] = "(type=SuperVision)";
+        JsonElement result = gatewayCommunication.invokeRPC("HDAccess/getHomeDevices", args);
+
+        for (JsonElement obj : result.getAsJsonArray()) {
+            HomeDevice hd = gson.fromJson(obj, HomeDevice.class);
+            if (hd != null) {
+                devices.add(hd);
             }
         }
         return devices;
@@ -344,8 +316,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                     address1 = InetAddress.getByName(JSON_RPC_MULTICAST_IP1);
                     address2 = InetAddress.getByName(JSON_RPC_MULTICAST_IP2);
                 } catch (UnknownHostException e) {
-                    logger.debug("An exception occurred while setting up the multicast receiver : '{}'",
-                            e.getMessage());
+                    logger.debug("An exception occurred while setting up the multicast receiver: '{}'", e.getMessage());
                 }
 
                 byte[] buf = new byte[256];
@@ -410,20 +381,22 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                                 var deviceProperty = new DeviceProperty();
                                 deviceProperty.Name = name;
                                 deviceProperty.Value = value;
-                                for (ApplianceStatusListener listener : applianceStatusListeners) {
-                                    listener.onAppliancePropertyChanged(applianceIdentifier, deviceProperty);
+                                ApplianceStatusListener listener = applianceStatusListeners
+                                        .get(applianceIdentifier.getApplianceId());
+                                if (listener != null) {
+                                    listener.onAppliancePropertyChanged(deviceProperty);
                                 }
                             } catch (SocketTimeoutException e) {
                                 try {
                                     Thread.sleep(500);
                                 } catch (InterruptedException ex) {
-                                    logger.debug("Eventlistener has been interrupted.");
+                                    logger.debug("Event listener has been interrupted.");
                                     break;
                                 }
                             }
                         }
                     } catch (Exception ex) {
-                        logger.debug("An exception occurred while receiving multicast packets : '{}'", ex.getMessage());
+                        logger.debug("An exception occurred while receiving multicast packets: '{}'", ex.getMessage());
                     }
 
                     // restart the cycle with a clean slate
@@ -433,7 +406,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                             clientSocket.leaveGroup(address2);
                         }
                     } catch (IOException e) {
-                        logger.debug("An exception occurred while leaving multicast group : '{}'", e.getMessage());
+                        logger.debug("An exception occurred while leaving multicast group: '{}'", e.getMessage());
                     }
                     if (clientSocket != null) {
                         clientSocket.close();
@@ -441,7 +414,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                 }
             }
         } else {
-            logger.debug("Invalid IP address for the multicast interface : '{}'", getConfig().get(INTERFACE));
+            logger.debug("Invalid IP address for the multicast interface: '{}'", getConfig().get(INTERFACE));
         }
     };
 
@@ -456,137 +429,10 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
                     + " was not found in appliance list from gateway - operations can not be invoked");
         }
 
-        Object[] args = new Object[4];
-        args[0] = applianceIdentifier.getUid();
-        args[1] = MIELE_CLASS + modelID;
-        args[2] = methodName;
-        args[3] = null;
-
-        return invokeRPC("HDAccess/invokeDCOOperation", args);
+        return gatewayCommunication.invokeOperation(applianceIdentifier, modelID, methodName);
     }
 
-    protected JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
-        JsonElement result = null;
-        URL url = this.url;
-        if (url == null) {
-            throw new MieleRpcException("URL is not set");
-        }
-
-        JsonObject req = new JsonObject();
-        int id = rand.nextInt(Integer.MAX_VALUE);
-        req.addProperty("jsonrpc", "2.0");
-        req.addProperty("id", id);
-        req.addProperty("method", methodName);
-
-        JsonArray params = new JsonArray();
-        for (Object o : args) {
-            params.add(gson.toJsonTree(o));
-        }
-        req.add("params", params);
-
-        String requestData = req.toString();
-        String responseData = null;
-        try {
-            responseData = post(url, Collections.emptyMap(), requestData);
-        } catch (IOException e) {
-            throw new MieleRpcException("Exception occurred while posting data", e);
-        }
-
-        logger.trace("The request '{}' yields '{}'", requestData, responseData);
-        JsonObject parsedResponse = null;
-        try {
-            parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
-        } catch (JsonParseException e) {
-            throw new MieleRpcException("Error parsing JSON response", e);
-        }
-
-        JsonElement error = parsedResponse.get("error");
-        if (error != null && !error.isJsonNull()) {
-            if (error.isJsonPrimitive()) {
-                throw new MieleRpcException("Remote exception occurred: '" + error.getAsString() + "'");
-            } else if (error.isJsonObject()) {
-                JsonObject o = error.getAsJsonObject();
-                Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
-                String message = (o.has("message") ? o.get("message").getAsString() : null);
-                String data = (o.has("data")
-                        ? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
-                        : null);
-                throw new MieleRpcException(
-                        "Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
-            } else {
-                throw new MieleRpcException("Unknown remote exception occurred: '" + error.toString() + "'");
-            }
-        }
-
-        result = parsedResponse.get("result");
-        if (result == null) {
-            throw new MieleRpcException("Result is missing in response");
-        }
-
-        return result;
-    }
-
-    protected String post(URL url, Map<String, String> headers, String data) throws IOException {
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-
-        for (Map.Entry<String, String> entry : headers.entrySet()) {
-            connection.addRequestProperty(entry.getKey(), entry.getValue());
-        }
-
-        connection.addRequestProperty("Accept-Encoding", "gzip");
-
-        connection.setRequestMethod("POST");
-        connection.setDoOutput(true);
-        connection.connect();
-
-        OutputStream out = null;
-
-        try {
-            out = connection.getOutputStream();
-
-            out.write(data.getBytes());
-            out.flush();
-
-            int statusCode = connection.getResponseCode();
-            if (statusCode != HttpURLConnection.HTTP_OK) {
-                logger.debug("An unexpected status code was returned: '{}'", statusCode);
-            }
-        } finally {
-            if (out != null) {
-                out.close();
-            }
-        }
-
-        String responseEncoding = connection.getHeaderField("Content-Encoding");
-        responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
-
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
-        InputStream in = connection.getInputStream();
-        try {
-            in = connection.getInputStream();
-            if ("gzip".equalsIgnoreCase(responseEncoding)) {
-                in = new GZIPInputStream(in);
-            }
-            in = new BufferedInputStream(in);
-
-            byte[] buff = new byte[1024];
-            int n;
-            while ((n = in.read(buff)) > 0) {
-                bos.write(buff, 0, n);
-            }
-            bos.flush();
-            bos.close();
-        } finally {
-            if (in != null) {
-                in.close();
-            }
-        }
-
-        return bos.toString();
-    }
-
-    private synchronized void onUpdate() {
+    private synchronized void schedulePollingAndEventListener() {
         logger.debug("Scheduling the Miele polling job");
         ScheduledFuture<?> pollingJob = this.pollingJob;
         if (pollingJob == null || pollingJob.isCancelled()) {
@@ -595,8 +441,8 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
             this.pollingJob = pollingJob;
             logger.trace("Scheduling the Miele polling job Job is done ?{}", pollingJob.isDone());
         }
-        logger.debug("Scheduling the Miele event listener job");
 
+        logger.debug("Scheduling the Miele event listener job");
         Future<?> eventListenerJob = this.eventListenerJob;
         if (eventListenerJob == null || eventListenerJob.isCancelled()) {
             ExecutorService executor = Executors
@@ -606,47 +452,71 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
         }
     }
 
-    /**
-     * This method is called whenever the connection to the given {@link MieleBridge} is lost.
-     *
-     */
-    public void onConnectionLost() {
-        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR);
-    }
+    public boolean registerApplianceStatusListener(String applianceId,
+            ApplianceStatusListener applianceStatusListener) {
+        ApplianceStatusListener existingListener = applianceStatusListeners.get(applianceId);
+        if (existingListener != null) {
+            if (!existingListener.equals(applianceStatusListener)) {
+                logger.warn("Unsupported configuration: appliance with ID '{}' referenced by multiple things",
+                        applianceId);
+            } else {
+                logger.debug("Duplicate listener registration attempted for '{}'", applianceId);
+            }
+            return false;
+        }
+        applianceStatusListeners.put(applianceId, applianceStatusListener);
 
-    /**
-     * This method is called whenever the connection to the given {@link MieleBridge} is resumed.
-     *
-     * @param bridge the Miele bridge the connection is resumed to
-     */
-    public void onConnectionResumed() {
-        updateStatus(ThingStatus.ONLINE);
-        for (Thing thing : getThing().getThings()) {
-            MieleApplianceHandler<?> handler = (MieleApplianceHandler<?>) thing.getHandler();
-            if (handler != null) {
-                handler.onBridgeConnectionResumed();
+        HomeDevice cachedHomeDevice = cachedHomeDevicesByApplianceId.get(applianceId);
+        if (cachedHomeDevice != null) {
+            applianceStatusListener.onApplianceAdded(cachedHomeDevice);
+        } else {
+            try {
+                refreshHomeDevices(getHomeDevices());
+            } catch (MieleRpcException e) {
+                Throwable cause = e.getCause();
+                if (cause == null) {
+                    logger.debug("An exception occurred while getting the home devices: '{}'", e.getMessage());
+                } else {
+                    logger.debug("An exception occurred while getting the home devices: '{}' -> '{}", e.getMessage(),
+                            cause.getMessage());
+                }
             }
         }
+
+        return true;
     }
 
-    public boolean registerApplianceStatusListener(ApplianceStatusListener applianceStatusListener) {
-        boolean result = applianceStatusListeners.add(applianceStatusListener);
-        if (result && isInitialized()) {
-            onUpdate();
+    public boolean unregisterApplianceStatusListener(String applianceId,
+            ApplianceStatusListener applianceStatusListener) {
+        return applianceStatusListeners.remove(applianceId) != null;
+    }
 
-            for (HomeDevice hd : getHomeDevices()) {
-                applianceStatusListener.onApplianceAdded(hd);
+    public boolean registerDiscoveryListener(DiscoveryListener discoveryListener) {
+        if (!discoveryListeners.add(discoveryListener)) {
+            return false;
+        }
+        if (cachedHomeDevicesByApplianceId.isEmpty()) {
+            try {
+                refreshHomeDevices(getHomeDevices());
+            } catch (MieleRpcException e) {
+                Throwable cause = e.getCause();
+                if (cause == null) {
+                    logger.debug("An exception occurred while getting the home devices: '{}'", e.getMessage());
+                } else {
+                    logger.debug("An exception occurred while getting the home devices: '{}' -> '{}", e.getMessage(),
+                            cause.getMessage());
+                }
+            }
+        } else {
+            for (Entry<String, HomeDevice> entry : cachedHomeDevicesByApplianceId.entrySet()) {
+                discoveryListener.onApplianceAdded(entry.getValue());
             }
         }
-        return result;
+        return true;
     }
 
-    public boolean unregisterApplianceStatusListener(ApplianceStatusListener applianceStatusListener) {
-        boolean result = applianceStatusListeners.remove(applianceStatusListener);
-        if (result && isInitialized()) {
-            onUpdate();
-        }
-        return result;
+    public boolean unregisterDiscoveryListener(DiscoveryListener discoveryListener) {
+        return discoveryListeners.remove(discoveryListener);
     }
 
     @Override
index d1d64bfde3c501b094d224d8fd6b999daac0d0ec..01a068b2b5ded3fb39afaaef4d717938c183877b 100644 (file)
@@ -52,8 +52,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
@@ -63,8 +62,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     PROGRAMTYPE("programType", "type", StringType.class, false),
     PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
                     MIELE_OVEN_TEXT_PREFIX);
         }
@@ -72,8 +70,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false),
     START_TIME("startTime", "start", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -87,8 +84,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     },
     DURATION("duration", "duration", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -102,8 +98,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     },
     ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -117,8 +112,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     },
     FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -132,37 +126,32 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     },
     TARGET_TEMP("targetTemperature", "target", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     MEASURED_TEMP("measuredTemperature", "measured", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     DEVICE_TEMP_ONE("deviceTemperature1", "temp1", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     DEVICE_TEMP_TWO("deviceTemperature2", "temp2", QuantityType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false) {
         @Override
 
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -221,8 +210,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -251,7 +239,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index a1681033a8b37e6eec6ee9a375eb381d8f7c391a..4dcb7dcbd7cd83ec0f3d47d293727b461751af96 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_OVEN;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -52,7 +51,7 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -62,7 +61,7 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
         JsonElement result = null;
 
         try {
-            MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+            MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
             if (bridgeHandler == null) {
                 logger.warn("Command '{}' failed, missing bridge handler", command);
                 return;
index ebb355f30eac4b69d9a2df6308c6757fd654829a..bfb8d6b67f90514c58c308d31a716797fecc04ff 100644 (file)
@@ -51,16 +51,14 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
     STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
     PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
                     MIELE_TUMBLE_DRYER_TEXT_PREFIX);
         }
@@ -69,8 +67,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     PROGRAMTYPE("programType", "type", StringType.class, false),
     PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
                     MIELE_TUMBLE_DRYER_TEXT_PREFIX);
         }
@@ -78,8 +75,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false),
     START_TIME("startTime", "start", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -93,8 +89,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     },
     DURATION("duration", "duration", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -108,8 +103,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     },
     ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -123,8 +117,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     },
     FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -138,16 +131,14 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     },
     DRYING_STEP("dryingStep", "step", DecimalType.class, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getState(s);
         }
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false) {
         @Override
 
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -217,8 +208,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -247,7 +237,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 0c459414328215c32bf1f9c7eaa3b8ffd46f6771..6115c66b4a72360d5e888b3a0048ffdfed1055b6 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_TUMBLE_DRYER;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -52,7 +51,7 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -64,7 +63,7 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
         try {
             switch (selector) {
                 case SWITCH: {
-                    MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+                    MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
                     if (bridgeHandler == null) {
                         logger.warn("Command '{}' failed, missing bridge handler", command);
                         return;
index bd22781dcbfe066ea1a6c2c4effa9e20982cfe59..65f24cddbf3e560b72ab6f19404a79cefc745433 100644 (file)
@@ -52,16 +52,14 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
     STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getStateTextState(s, dmd, translationProvider);
         }
     },
     STATE("", STATE_CHANNEL_ID, DecimalType.class, false, false),
     PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
                     MIELE_WASHING_MACHINE_TEXT_PREFIX);
         }
@@ -70,8 +68,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     PROGRAMTYPE("programType", "type", StringType.class, false, false),
     PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
                     MIELE_WASHING_MACHINE_TEXT_PREFIX);
         }
@@ -79,8 +76,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false, false),
     START_TIME("startTime", "start", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -94,8 +90,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     },
     DURATION("duration", "duration", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -109,8 +104,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     },
     ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -124,8 +118,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     },
     FINISH_TIME("finishTime", "finish", DateTimeType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             Date date = new Date();
             SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
             dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
@@ -139,15 +132,13 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     },
     TARGET_TEMP("targetTemperature", "target", QuantityType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             return getTemperatureState(s);
         }
     },
     SPINNING_SPEED("spinningSpeed", "spinningspeed", StringType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("0".equals(s)) {
                 return getState("Without spinning");
             }
@@ -159,8 +150,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     },
     DOOR("signalDoor", "door", OpenClosedType.class, false, false) {
         @Override
-        public State getState(String s, @Nullable DeviceMetaData dmd,
-                @Nullable MieleTranslationProvider translationProvider) {
+        public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
             if ("true".equals(s)) {
                 return getState("OPEN");
             }
@@ -234,8 +224,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
     }
 
     @Override
-    public State getState(String s, @Nullable DeviceMetaData dmd,
-            @Nullable MieleTranslationProvider translationProvider) {
+    public State getState(String s, @Nullable DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
         return this.getState(s, dmd);
     }
 
@@ -264,7 +253,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
                 return state;
             }
         } catch (Exception e) {
-            logger.error("An exception occurred while converting '{}' into a State", s);
+            logger.warn("An exception occurred while converting '{}' into a State", s);
         }
 
         return UnDefType.UNDEF;
index 736a1b6b791004e3d304f7817ff435221a4bdfd1..05139b22a073b435c2a4d00894eb0d6fcf3b4d5c 100644 (file)
@@ -12,7 +12,6 @@
  */
 package org.openhab.binding.miele.internal.handler;
 
-import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_WASHING_MACHINE;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.POWER_CONSUMPTION_CHANNEL_ID;
 import static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID;
@@ -64,7 +63,7 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
         super.handleCommand(channelUID, command);
 
         String channelID = channelUID.getId();
-        String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+        String applianceId = this.applianceId;
         if (applianceId == null) {
             logger.warn("Command '{}' failed, appliance id is unknown", command);
             return;
@@ -77,7 +76,7 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
         try {
             switch (selector) {
                 case SWITCH: {
-                    MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+                    MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
                     if (bridgeHandler == null) {
                         logger.warn("Command '{}' failed, missing bridge handler", command);
                         return;
index 748758e9cec4c8eb73f3c9708bcf87d751f416b8..1a04ee1c89001c111e5b1f1a9af2d38cf78cfdce 100644 (file)
@@ -125,11 +125,13 @@ channel-type.miele.waterConsumption.description = Water consumption by the curre
 
 # thing status descriptions
 
+offline.configuration-error.bridge-missing = Bridge is missing
 offline.configuration-error.ip-address-not-set = Cannot connect to the Miele gateway: host IP address is not set.
 offline.configuration-error.ip-multicast-interface-not-set = Cannot connect to the Miele gateway: multicast interface is not set.
 offline.configuration-error.invalid-ip-gateway = Invalid IP address for the Miele@Home gateway: {0}
 offline.configuration-error.invalid-ip-multicast-interface = Invalid IP address for the multicast interface: {0}
 offline.configuration-error.invalid-language = Invalid language: {0}
+offline.configuration-error.uid-not-set = Appliance ID is not set
 
 # Discovery result