diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java index 311ac6088e2..b6ff6002b14 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java @@ -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 valueMap, String propertyPrefix, + MieleTranslationProvider translationProvider, Map 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 index 00000000000..58f5363b3d1 --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleGatewayCommunicationController.java @@ -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 headers, String data) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + for (Map.Entry 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(); + } +} diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java index cf9bd30cfe9..2677d369457 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleApplianceDiscoveryService.java @@ -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 appliances = mieleBridgeHandler.getHomeDevices(); + List 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(); diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceChannelSelector.java index 0bbed76aaba..b9d5dd4885a 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceChannelSelector.java @@ -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 diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceStatusListener.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceStatusListener.java index 7b81fcb3835..69cf25bf8ab 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceStatusListener.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/ApplianceStatusListener.java @@ -13,15 +13,13 @@ 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); } diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineChannelSelector.java index b65b6a1c3fb..d4679caef07 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineChannelSelector.java @@ -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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineHandler.java index 4aa51acb588..77d14fac347 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/CoffeeMachineHandler.java @@ -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 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 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 } @Override - protected void onAppliancePropertyChanged(DeviceProperty dp) { + public void onAppliancePropertyChanged(DeviceProperty dp) { super.onAppliancePropertyChanged(dp); if (!STATE_PROPERTY_NAME.equals(dp.Name)) { diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobChannelSelector.java index 4059931fc0d..47b94affaaa 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobChannelSelector.java @@ -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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodChannelSelector.java index f84eebc9194..491f627fe38 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodChannelSelector.java @@ -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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodHandler.java index 83cb84ec7fa..35491260ea5 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HoodHandler.java @@ -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 { 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 { JsonElement result = null; try { - 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/MieleApplianceHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java index 85aac472c97..929c7339a57 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleApplianceHandler.java @@ -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 & 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 selectorType; protected String modelID; @@ -90,6 +90,7 @@ public abstract class MieleApplianceHandler & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 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 & 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 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 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 & 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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java index e8a460bbfca..cf385ed7b0d 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/MieleBridgeHandler.java @@ -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 applianceStatusListeners = new CopyOnWriteArrayList<>(); - protected @Nullable ScheduledFuture pollingJob; - protected @Nullable ExecutorService executor; - protected @Nullable Future eventListenerJob; + private boolean lastBridgeConnectionState = false; - protected Map cachedHomeDevicesByApplianceId = new ConcurrentHashMap(); - protected Map cachedHomeDevicesByRemoteUid = new ConcurrentHashMap(); + private Gson gson = new Gson(); + private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication; - protected @Nullable URL url; + private Set discoveryListeners = ConcurrentHashMap.newKeySet(); + private Map applianceStatusListeners = new ConcurrentHashMap<>(); + private @Nullable ScheduledFuture pollingJob; + private @Nullable ExecutorService executor; + private @Nullable Future eventListenerJob; + + private Map cachedHomeDevicesByApplianceId = new ConcurrentHashMap<>(); + private Map 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(); - } - - if (!lastBridgeConnectionState && currentBridgeConnectionState) { - logger.debug("Connection to Miele Gateway {} established.", getConfig().get(HOST)); - lastBridgeConnectionState = true; - onConnectionResumed(); - } - - if (!currentBridgeConnectionState || getThing().getStatus() != ThingStatus.ONLINE) { - return; - } - List 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); - } - } - cachedHomeDevicesByApplianceId.put(key, hd); - cachedHomeDevicesByRemoteUid.put(hd.getRemoteUid(), hd); + + if (!lastBridgeConnectionState) { + logger.debug("Connection to Miele Gateway {} established.", host); + lastBridgeConnectionState = true; } + updateStatus(ThingStatus.ONLINE); - Set> cachedEntries = cachedHomeDevicesByApplianceId.entrySet(); - Iterator> iterator = cachedEntries.iterator(); + refreshHomeDevices(homeDevices); - while (iterator.hasNext()) { - Entry 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 (Entry 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; } - } - 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); - } + Object[] args = new Object[2]; + args[0] = applianceIdentifier.getUid(); + args[1] = true; + JsonElement result = gatewayCommunication.invokeRPC("HDAccess/getDeviceClassObjects", args); - if (applianceIdentifier == null) { - logger.warn("The appliance with ID '{}' was not found in appliance list from bridge.", - applianceId); - continue; - } + for (JsonElement obj : result.getAsJsonArray()) { + try { + DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class); - 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 (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 - - // 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; - } - - logger.debug("{} is reachable", ipAddress); - return true; - } }; - public List getHomeDevices() { + private synchronized void refreshHomeDevices(List 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); + } + + Set> cachedEntries = cachedHomeDevicesByApplianceId.entrySet(); + Iterator> iterator = cachedEntries.iterator(); + + while (iterator.hasNext()) { + Entry 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(); + } + } + } + + public List 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<>(); + } + } + + private List getHomeDevices() throws MieleRpcException { List devices = new ArrayList<>(); - if (getThing().getStatus() == ThingStatus.ONLINE) { - try { - String[] args = new String[1]; - args[0] = "(type=SuperVision)"; - JsonElement result = invokeRPC("HDAccess/getHomeDevices", args); + if (!isInitialized()) { + return devices; + } - 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()); - } + 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 headers, String data) throws IOException { - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - - for (Map.Entry 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 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 diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenChannelSelector.java index d1d64bfde3c..01a068b2b5d 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenChannelSelector.java @@ -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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenHandler.java index a1681033a8b..4dcb7dcbd7c 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/OvenHandler.java @@ -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 { 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 { JsonElement result = null; try { - 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/TumbleDryerChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerChannelSelector.java index ebb355f30ea..bfb8d6b67f9 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerChannelSelector.java @@ -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; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerHandler.java index 0c459414328..6115c66b4a7 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/TumbleDryerHandler.java @@ -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