From: jlaur Date: Sat, 18 Sep 2021 20:59:18 +0000 (+0200) Subject: [miele] Fix multicast and multi-protocol support (ZigBee/Wi-Fi) (#11244) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=0c021ec795b9c5b53b29e40fba5db36aa177fa6c;p=openhab-addons.git [miele] Fix multicast and multi-protocol support (ZigBee/Wi-Fi) (#11244) * Fix multicast and multi-protocol support (ZigBee/Wi-Fi) * Fix channel description referring to hood but also used for oven and other appliances. * Fix auto-discovery finding already configured things through files. Fixes #11242 Fixes #11243 Signed-off-by: Jacob Laursen --- diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifier.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifier.java new file mode 100644 index 0000000000..2ecfd854f4 --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifier.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.miele.internal; + +/** + * The {@link FullyQualifiedApplianceIdentifier} class represents a fully qualified appliance identifier. + * Example: "hdm:ZigBee:0123456789abcdef#210" + * + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + */ +public class FullyQualifiedApplianceIdentifier { + private String uid; + private String protocol; + private String applianceId; + + public FullyQualifiedApplianceIdentifier(String uid) { + this.uid = uid; + + int separatorPosition = this.uid.lastIndexOf(':') + 1; + this.protocol = uid.substring(0, separatorPosition); + this.applianceId = uid.substring(separatorPosition); + } + + public FullyQualifiedApplianceIdentifier(String applianceId, String protocol) { + this.uid = protocol + applianceId; + this.protocol = protocol; + this.applianceId = applianceId; + } + + /** + * @return UID of appliance (e.g. "hdm:ZigBee:0123456789abcdef#210") + */ + public String getUid() { + return this.uid; + } + + /** + * @return Appliance ID without protocol adapter information (e.g. "0123456789abcdef#210") + */ + public String getApplianceId() { + return this.applianceId; + } + + public String getId() { + return this.getApplianceId().replaceAll("[^a-zA-Z0-9_]", "_"); + } + + /** + * @return Protocol prefix of fully qualified appliance identifier (e.g. "hdmi:ZigBee:"") + */ + public String getProtocol() { + return this.protocol; + } +} diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java index 5cf2da31a7..b435fc7c72 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/MieleBindingConstants.java @@ -21,6 +21,7 @@ import org.openhab.core.thing.ThingTypeUID; * * @author Karel Goderis - Initial contribution * @author Martin Lepsy - added constants for support of WiFi devices & protocol + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ @NonNullByDefault public class MieleBindingConstants { @@ -28,9 +29,8 @@ public class MieleBindingConstants { public static final String BINDING_ID = "miele"; public static final String APPLIANCE_ID = "uid"; public static final String DEVICE_CLASS = "dc"; - public static final String HDM_LAN = "hdm:LAN:"; - public static final String HDM_ZIGBEE = "hdm:ZigBee:"; public static final String PROTOCOL_PROPERTY_NAME = "protocol"; + public static final String SERIAL_NUMBER_PROPERTY_NAME = "serialNumber"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_XGW3000 = new ThingTypeUID(BINDING_ID, "xgw3000"); 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 b85ce8e117..703c5549b5 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 @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.handler.ApplianceStatusListener; import org.openhab.binding.miele.internal.handler.MieleApplianceHandler; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler; @@ -42,6 +43,7 @@ import com.google.gson.JsonElement; * * @author Karel Goderis - Initial contribution * @author Martin Lepsy - Added protocol information in order so support WiFi devices + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener { @@ -101,8 +103,10 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID(); Map properties = new HashMap<>(2); - properties.put(PROTOCOL_PROPERTY_NAME, appliance.getProtocol()); - properties.put(APPLIANCE_ID, appliance.getApplianceId()); + FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); + properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol()); + properties.put(APPLIANCE_ID, applianceIdentifier.getApplianceId()); + properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber()); for (JsonElement dc : appliance.DeviceClasses) { String dcStr = dc.getAsString(); @@ -113,7 +117,8 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp } DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) - .withBridge(bridgeUID).withLabel((String) properties.get(DEVICE_CLASS)).build(); + .withBridge(bridgeUID).withLabel((String) properties.get(DEVICE_CLASS)) + .withRepresentationProperty(APPLIANCE_ID).build(); thingDiscovered(discoveryResult); } else { @@ -132,12 +137,17 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp } @Override - public void onApplianceStateChanged(String uid, DeviceClassObject dco) { + public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco) { // nothing to do } @Override - public void onAppliancePropertyChanged(String uid, DeviceProperty dp) { + public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp) { + // nothing to do + } + + @Override + public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) { // nothing to do } @@ -158,7 +168,7 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp modelID.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase()); if (getSupportedThingTypes().contains(thingTypeUID)) { - ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getId()); + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getApplianceIdentifier().getId()); return thingUID; } else { return null; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java index 7694c88c23..eef48f6b02 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/discovery/MieleMDNSDiscoveryParticipant.java @@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory; * * @author Karel Goderis - Initial contribution * @author Martin Lepsy - Added check for Miele gateway for cleaner discovery - * + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ @Component public class MieleMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant { @@ -83,8 +83,7 @@ public class MieleMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant { } return DiscoveryResultBuilder.create(uid).withProperties(properties) - .withRepresentationProperty(MieleBindingConstants.HOST).withLabel("Miele XGW3000 Gateway") - .build(); + .withRepresentationProperty(MieleBindingConstants.HOST).withLabel("Miele XGW3000").build(); } } return null; 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 153f35956f..769417bf60 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 @@ -12,6 +12,7 @@ */ package org.openhab.binding.miele.internal.handler; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice; @@ -22,24 +23,33 @@ import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice; * an appliance has been removed or added. * * @author Karel Goderis - Initial contribution + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ public interface ApplianceStatusListener { /** * This method is called whenever the state of the given appliance has changed. * - * @param uid the UID of the aplliance that 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(String uid, DeviceClassObject dco); + void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco); /** * This method is called whenever a "property" of the given appliance has changed. * - * @param uid the UID of the aplliance that 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(String uid, DeviceProperty dp); + void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp); + + /** + * This method is called whenever a "property" of the given appliance has changed. + * + * @param serialNumber The serial number of the appliance that has changed + * @param dco the POJO containing the new state of the property + */ + void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp); /** * This method us called whenever an appliance is removed. 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 5b7ed1dd37..0b0cbc34c3 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 @@ -13,7 +13,9 @@ 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.PROTOCOL_PROPERTY_NAME; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -30,6 +32,7 @@ import com.google.gson.JsonElement; * * @author Stephan Esch - Initial contribution * @author Martin Lepsy - fixed handling of empty JSON results + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ public class CoffeeMachineHandler extends MieleApplianceHandler { @@ -44,7 +47,9 @@ public class CoffeeMachineHandler extends MieleApplianceHandler { @@ -45,7 +48,9 @@ public class DishWasherHandler extends MieleApplianceHandler { @@ -44,7 +47,9 @@ public class FridgeFreezerHandler extends MieleApplianceHandler { @@ -44,7 +47,9 @@ public class FridgeHandler extends MieleApplianceHandler super.handleCommand(channelUID, command); String channelID = channelUID.getId(); - String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); + var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol); FridgeChannelSelector selector = (FridgeChannelSelector) getValueSelectorFromChannelID(channelID); JsonElement result = null; @@ -54,15 +59,15 @@ public class FridgeHandler extends MieleApplianceHandler switch (selector) { case SUPERCOOL: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "startSuperCooling"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperCooling"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling"); } break; } case START: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "start"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); } break; } 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 cd2dd89b1d..f89c11d0db 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 @@ -13,7 +13,9 @@ 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.PROTOCOL_PROPERTY_NAME; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -30,7 +32,8 @@ import com.google.gson.JsonElement; * @author Karel Goderis - Initial contribution * @author Kai Kreuzer - fixed handling of REFRESH commands * @author Martin Lepsy - fixed handling of empty JSON results - */ + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + **/ public class HoodHandler extends MieleApplianceHandler { private final Logger logger = LoggerFactory.getLogger(HoodHandler.class); @@ -44,7 +47,9 @@ public class HoodHandler extends MieleApplianceHandler { super.handleCommand(channelUID, command); String channelID = channelUID.getId(); - String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); + var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol); HoodChannelSelector selector = (HoodChannelSelector) getValueSelectorFromChannelID(channelID); JsonElement result = null; @@ -54,15 +59,15 @@ public class HoodHandler extends MieleApplianceHandler { switch (selector) { case LIGHT: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "startLighting"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startLighting"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(uid, modelID, "stopLighting"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopLighting"); } break; } case STOP: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "stop"); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); } break; } 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 d6c000340b..a4f2b9c1f1 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 @@ -21,6 +21,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty; @@ -52,6 +53,7 @@ import com.google.gson.JsonParser; * * @author Karel Goderis - Initial contribution * @author Martin Lepsy - Added check for JsonNull result + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ public abstract class MieleApplianceHandler & ApplianceChannelSelector> extends BaseThingHandler implements ApplianceStatusListener { @@ -65,7 +67,7 @@ public abstract class MieleApplianceHandler & ApplianceChannel protected Gson gson = new Gson(); - protected String uid; + protected String applianceId; protected MieleBridgeHandler bridgeHandler; private Class selectorType; protected String modelID; @@ -103,9 +105,9 @@ public abstract class MieleApplianceHandler & ApplianceChannel @Override public void initialize() { logger.debug("Initializing Miele appliance handler."); - final String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); - if (uid != null) { - this.uid = uid; + final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + if (applianceId != null) { + this.applianceId = applianceId; if (getMieleBridgeHandler() != null) { ThingStatusInfo statusInfo = getBridge().getStatusInfo(); updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription()); @@ -123,12 +125,12 @@ public abstract class MieleApplianceHandler & ApplianceChannel @Override public void dispose() { logger.debug("Handler disposes. Unregistering listener."); - if (uid != null) { + if (applianceId != null) { MieleBridgeHandler bridgeHandler = getMieleBridgeHandler(); if (bridgeHandler != null) { getMieleBridgeHandler().unregisterApplianceStatusListener(this); } - uid = null; + applianceId = null; } } @@ -142,12 +144,13 @@ public abstract class MieleApplianceHandler & ApplianceChannel } @Override - public void onApplianceStateChanged(String UID, DeviceClassObject dco) { - String myUID = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, + DeviceClassObject dco) { + String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); String modelID = StringUtils.right(dco.DeviceClass, dco.DeviceClass.length() - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length()); - if (myUID.equals(UID)) { + if (myApplianceId.equals(applicationIdentifier.getApplianceId())) { if (modelID.equals(this.modelID)) { for (JsonElement prop : dco.Properties.getAsJsonArray()) { try { @@ -155,7 +158,7 @@ public abstract class MieleApplianceHandler & ApplianceChannel dp.Value = StringUtils.trim(dp.Value); dp.Value = StringUtils.strip(dp.Value); - onAppliancePropertyChanged(UID, dp); + onAppliancePropertyChanged(applicationIdentifier, dp); } catch (Exception p) { // Ignore - this is due to an unrecognized and not yet reverse-engineered array property } @@ -165,85 +168,107 @@ public abstract class MieleApplianceHandler & ApplianceChannel } @Override - public void onAppliancePropertyChanged(String UID, DeviceProperty dp) { - String myUID = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) { + String mySerialNumber = getThing().getProperties().get(SERIAL_NUMBER_PROPERTY_NAME); + if (!mySerialNumber.equals(serialNumber)) { + return; + } - if (myUID.equals(UID)) { - try { - DeviceMetaData dmd = null; - if (dp.Metadata == null) { - String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim()); - if (metadata != null) { - JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); - dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); - // only keep the enum, if any - that's all we care for events we receive via multicast - // all other fields are nulled - dmd.LocalizedID = null; - dmd.LocalizedValue = null; - dmd.Filter = null; - dmd.description = null; - } - } - if (dp.Metadata != null) { - String metadata = StringUtils.replace(dp.Metadata.toString(), "enum", "MieleEnum"); + this.onAppliancePropertyChanged(dp); + } + + @Override + public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) { + String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + + if (!myApplianceId.equals(applicationIdentifier.getApplianceId())) { + return; + } + + this.onAppliancePropertyChanged(dp); + } + + private void onAppliancePropertyChanged(DeviceProperty dp) { + try { + DeviceMetaData dmd = null; + if (dp.Metadata == null) { + String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim()); + if (metadata != null) { JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); - metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata); + // only keep the enum, if any - that's all we care for events we receive via multicast + // all other fields are nulled + dmd.LocalizedID = null; + dmd.LocalizedValue = null; + dmd.Filter = null; + dmd.description = null; } + } + if (dp.Metadata != null) { + String metadata = StringUtils.replace(dp.Metadata.toString(), "enum", "MieleEnum"); + JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); + dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); + metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata); + } - ApplianceChannelSelector selector = null; - try { - selector = getValueSelectorFromMieleID(dp.Name); - } catch (Exception h) { - logger.trace("{} is not a valid channel for a {}", dp.Name, modelID); - } + ApplianceChannelSelector selector = null; + try { + selector = getValueSelectorFromMieleID(dp.Name); + } catch (Exception h) { + logger.trace("{} is not a valid channel for a {}", dp.Name, modelID); + } - String dpValue = StringUtils.trim(StringUtils.strip(dp.Value)); - - if (selector != null) { - if (!selector.isProperty()) { - ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID()); - - if (dp.Value != null) { - logger.trace("Update state of {} with getState '{}'", theChannelUID, - selector.getState(dpValue, dmd)); - updateState(theChannelUID, selector.getState(dpValue, dmd)); - } else { - updateState(theChannelUID, UnDefType.UNDEF); - } - } else if (dpValue != null) { - logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(), - getThing().getUID(), selector.getState(dpValue, dmd).toString()); - Map properties = editProperties(); - properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString()); - updateProperties(properties); + String dpValue = StringUtils.trim(StringUtils.strip(dp.Value)); + + if (selector != null) { + if (!selector.isProperty()) { + ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID()); + + if (dp.Value != null) { + logger.trace("Update state of {} with getState '{}'", theChannelUID, + selector.getState(dpValue, dmd)); + updateState(theChannelUID, selector.getState(dpValue, dmd)); + } else { + updateState(theChannelUID, UnDefType.UNDEF); } + } else if (dpValue != null) { + logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(), + getThing().getUID(), selector.getState(dpValue, dmd).toString()); + Map properties = editProperties(); + properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString()); + updateProperties(properties); } - } catch (IllegalArgumentException e) { - logger.error("An exception occurred while processing a changed device property :'{}'", e.getMessage()); } + } catch (IllegalArgumentException e) { + logger.error("An exception occurred while processing a changed device property :'{}'", e.getMessage()); } } @Override public void onApplianceRemoved(HomeDevice appliance) { - if (uid != null) { - if (uid.equals(appliance.getApplianceId())) { - updateStatus(ThingStatus.OFFLINE); - } + if (applianceId == null) { + return; + } + + if (applianceId.equals(appliance.getApplianceIdentifier().getApplianceId())) { + updateStatus(ThingStatus.OFFLINE); } } @Override public void onApplianceAdded(HomeDevice appliance) { - if (uid != null) { - if (uid.equals(appliance.getApplianceId())) { - Map properties = editProperties(); - properties.put(PROTOCOL_PROPERTY_NAME, appliance.getProtocol()); - updateProperties(properties); + if (applianceId == null) { + return; + } - updateStatus(ThingStatus.ONLINE); - } + FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); + + if (applianceId.equals(applianceIdentifier.getApplianceId())) { + Map properties = editProperties(); + properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol()); + properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber()); + updateProperties(properties); + updateStatus(ThingStatus.ONLINE); } } 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 ad2ac3d767..d40c19cf9c 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 @@ -45,6 +45,7 @@ import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import org.apache.commons.lang3.StringUtils; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.common.NamedThreadFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -71,7 +72,8 @@ import com.google.gson.JsonParser; * @author Karel Goderis - Initial contribution * @author Kai Kreuzer - Fixed lifecycle issues * @author Martin Lepsy - Added protocol information to support WiFi devices & some refactoring for HomeDevice - */ + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + **/ public class MieleBridgeHandler extends BaseBridgeHandler { public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000); @@ -103,8 +105,6 @@ public class MieleBridgeHandler extends BaseBridgeHandler { // Data structures to de-JSONify whatever Miele appliances are sending us public class HomeDevice { - private static final String PROTOCOL_LAN = "LAN"; - public String Name; public String Status; public String ParentUID; @@ -121,17 +121,12 @@ public class MieleBridgeHandler extends BaseBridgeHandler { HomeDevice() { } - public String getId() { - return getApplianceId().replaceAll("[^a-zA-Z0-9_]", "_"); - } - - public String getProtocol() { - return ProtocolAdapterName.equals(PROTOCOL_LAN) ? HDM_LAN : HDM_ZIGBEE; + public FullyQualifiedApplianceIdentifier getApplianceIdentifier() { + return new FullyQualifiedApplianceIdentifier(this.UID); } - public String getApplianceId() { - return ProtocolAdapterName.equals(PROTOCOL_LAN) ? StringUtils.right(UID, UID.length() - HDM_LAN.length()) - : StringUtils.right(UID, UID.length() - HDM_ZIGBEE.length()); + public String getSerialNumber() { + return Properties.get("serial.number").getAsString(); } } @@ -269,14 +264,11 @@ public class MieleBridgeHandler extends BaseBridgeHandler { String applianceId = (String) appliance.getConfiguration().getProperties() .get(APPLIANCE_ID); String protocol = appliance.getProperties().get(PROTOCOL_PROPERTY_NAME); - if (protocol == null) { - logger.error("Protocol property is missing for {}", applianceId); - continue; - } - String UID = protocol + applianceId; + var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, + protocol); Object[] args = new Object[2]; - args[0] = UID; + args[0] = applianceIdentifier.getUid(); args[1] = true; JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args); @@ -286,7 +278,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler { DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class); for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onApplianceStateChanged(applianceId, dco); + listener.onApplianceStateChanged(applianceIdentifier, dco); } } catch (Exception e) { logger.debug("An exception occurred while quering an appliance : '{}'", @@ -384,7 +376,7 @@ public class MieleBridgeHandler extends BaseBridgeHandler { packet.getPort()); DeviceProperty dp = new DeviceProperty(); - String uid = null; + String id = null; String[] parts = StringUtils.split(event, "&"); for (String p : parts) { @@ -399,14 +391,27 @@ public class MieleBridgeHandler extends BaseBridgeHandler { break; } case "id": { - uid = subparts[1]; + id = subparts[1]; break; } } } - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onAppliancePropertyChanged(uid, dp); + if (id == null) { + continue; + } + + // In XGW 3000 firmware 2.03 this was changed from UID (hdm:ZigBee:0123456789abcdef#210) + // to serial number (001234567890) + if (id.startsWith("hdm:")) { + for (ApplianceStatusListener listener : applianceStatusListeners) { + listener.onAppliancePropertyChanged(new FullyQualifiedApplianceIdentifier(id), + dp); + } + } else { + for (ApplianceStatusListener listener : applianceStatusListeners) { + listener.onAppliancePropertyChanged(id, dp); + } } } catch (SocketTimeoutException e) { try { @@ -440,14 +445,11 @@ public class MieleBridgeHandler extends BaseBridgeHandler { } }; - public JsonElement invokeOperation(String UID, String modelID, String methodName) { - return invokeOperation(UID, modelID, methodName, HDM_ZIGBEE); - } - - public JsonElement invokeOperation(String UID, String modelID, String methodName, String protocol) { + public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID, + String methodName) { if (getThing().getStatus() == ThingStatus.ONLINE) { Object[] args = new Object[4]; - args[0] = protocol + UID; + args[0] = applianceIdentifier.getUid(); args[1] = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele" + modelID; args[2] = methodName; args[3] = null; 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 de84623d18..20d2fdb0ae 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 @@ -15,6 +15,7 @@ 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.PROTOCOL_PROPERTY_NAME; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -32,6 +33,7 @@ import com.google.gson.JsonElement; * @author Karel Goderis - Initial contribution * @author Kai Kreuzer - fixed handling of REFRESH commands * @author Martin Lepsy - fixed handling of empty JSON results + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) */ public class OvenHandler extends MieleApplianceHandler { @@ -46,8 +48,9 @@ public class OvenHandler extends MieleApplianceHandler { super.handleCommand(channelUID, command); String channelID = channelUID.getId(); - String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); - String protocol = (String) getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); + String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); + String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); + var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol); OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID); JsonElement result = null; @@ -57,15 +60,15 @@ public class OvenHandler extends MieleApplianceHandler { switch (selector) { case SWITCH: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "switchOn", protocol); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(uid, modelID, "switchOff", protocol); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff"); } break; } case STOP: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(uid, modelID, "stop", protocol); + result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); } break; } 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 5c1458034e..3ffe5cc12a 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 @@ -13,7 +13,9 @@ 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.PROTOCOL_PROPERTY_NAME; +import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -31,7 +33,8 @@ import com.google.gson.JsonElement; * @author Karel Goderis - Initial contribution * @author Kai Kreuzer - fixed handling of REFRESH commands * @author Martin Lepsy - fixed handling of empty JSON results - */ + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + **/ public class TumbleDryerHandler extends MieleApplianceHandler { private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class); @@ -45,7 +48,9 @@ public class TumbleDryerHandler extends MieleApplianceHandler { private final Logger logger = LoggerFactory.getLogger(WashingMachineHandler.class); @@ -45,7 +48,9 @@ public class WashingMachineHandler extends MieleApplianceHandler Switch - Stop the hood + Stop the appliance diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml index 8a34bfc992..09d210dcc9 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/coffeemachine.xml @@ -22,6 +22,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml index 902fbaced0..e87446bef6 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/dishwasher.xml @@ -25,6 +25,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml index c68c643376..a50007623b 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridge.xml @@ -22,6 +22,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml index 585ad21d9c..de6aa4371a 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/fridgefreezer.xml @@ -27,6 +27,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml index 71e6ab990a..ab12ed6204 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hob.xml @@ -35,6 +35,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml index aedd3eade7..af075365e3 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/hood.xml @@ -20,6 +20,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml index 4090178d65..ac699106a2 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/oven.xml @@ -31,6 +31,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml index 24035a4211..af71518dff 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/tumbledryer.xml @@ -27,6 +27,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml index 2a41f546dc..deafab98af 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/washingmachine.xml @@ -28,6 +28,8 @@ + uid + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml index 184c3daacc..bdabbde843 100644 --- a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/thing/xgw3000.xml @@ -13,6 +13,8 @@ Miele + ipAddress + network-address diff --git a/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifierTest.java b/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifierTest.java new file mode 100644 index 0000000000..f352b6cff5 --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/FullyQualifiedApplianceIdentifierTest.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.miele.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.openhab.core.test.java.JavaTest; + +/** + * This class provides test cases for {@link + * org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier} + * + * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + */ +public class FullyQualifiedApplianceIdentifierTest extends JavaTest { + + @Test + public void getUidWhenConstructedFromUidReturnsUid() { + var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210"); + assertEquals("hdm:ZigBee:0123456789abcdef#210", identifier.getUid()); + } + + @Test + public void getUidWhenConstructedFromApplianceIdAndProtocolReturnsUid() { + var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:"); + assertEquals("hdm:LAN:0123456789abcdef#210", identifier.getUid()); + } + + @Test + public void getApplianceIdWhenConstructedFromUidReturnsApplianceId() { + var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210"); + assertEquals("0123456789abcdef#210", identifier.getApplianceId()); + } + + @Test + public void getApplianceIdWhenConstructedFromApplianceIdAndProtocolReturnsApplianceId() { + var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:"); + assertEquals("0123456789abcdef#210", identifier.getApplianceId()); + } + + @Test + public void getIdWhenConstructedFromUidReturnsProtocol() { + var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210"); + assertEquals("0123456789abcdef_210", identifier.getId()); + } + + @Test + public void getIdWhenConstructedFromApplianceIdAndProtocolReturnsProtocol() { + var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:"); + assertEquals("0123456789abcdef_210", identifier.getId()); + } + + @Test + public void getProtocolWhenConstructedFromUidReturnsProtocol() { + var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210"); + assertEquals("hdm:ZigBee:", identifier.getProtocol()); + } + + @Test + public void getProtocolWhenConstructedFromApplianceIdAndProtocolReturnsProtocol() { + var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:"); + assertEquals("hdm:LAN:", identifier.getProtocol()); + } +}