From 52135b5d24c9c9b55de6360d77bbe6313abc1419 Mon Sep 17 00:00:00 2001 From: jlaur Date: Mon, 1 Nov 2021 18:44:19 +0100 Subject: [PATCH] [miele] Clean up properties and improve reliability and performance (#11423) * Use appliance cache for getting full UID with protocol prefix instead of relying on property. * Set bare protocol name as property. * Fix potential null pointer access warnings. * Remove unused import. * Renamed property protocol to protocolAdapter for correctness. * Add connectionType property. * Add appliance model property. * Remove useless properties brandId and companyId always having value MI. * Rename property dc to deviceClass and set it consistently (not only from auto-discovered things). * Added constants for remaining handlers with hardcoded device classes. * Fix SCA: AuthorContributionDescriptionCheck * Fix SCA: ModifierOrderCheck * Rename ExtendedDeviceStateUtil to be a bit more generic. * Extract device class string parsing to utility method. * Fix SCA: ForbiddenPackageUsageCheck * Fix redundant null check. * Fix potential null pointer access warnings. * Fix unsafe null type conversion (type annotations) * Share same configuration (UID) for all appliance types. * Refer to gateway instead of ZigBee network in configuration. * Remove dependency to seriaNumber property for multicast channel updates. * Simplified filtering of irrelevant device class. * Remove devices from remoteUid cache also when disappearing from gateway, although this is a quite rare scenario. * Add default i18n properties file. * Add partial Danish translation. Fixes #11422 Signed-off-by: Jacob Laursen Signed-off-by: Nick Waterton --- ...edDeviceStateUtil.java => DeviceUtil.java} | 8 +- .../FullyQualifiedApplianceIdentifier.java | 2 +- .../miele/internal/MieleBindingConstants.java | 12 +- .../MieleApplianceDiscoveryService.java | 39 +-- .../MieleMDNSDiscoveryParticipant.java | 1 - .../handler/ApplianceStatusListener.java | 8 - .../handler/CoffeeMachineChannelSelector.java | 6 +- .../handler/CoffeeMachineHandler.java | 10 +- .../internal/handler/DishWasherHandler.java | 13 +- .../handler/DishwasherChannelSelector.java | 6 +- .../handler/FridgeChannelSelector.java | 6 +- .../handler/FridgeFreezerChannelSelector.java | 6 +- .../handler/FridgeFreezerHandler.java | 15 +- .../miele/internal/handler/FridgeHandler.java | 13 +- .../internal/handler/HobChannelSelector.java | 2 - .../miele/internal/handler/HobHandler.java | 4 +- .../internal/handler/HoodChannelSelector.java | 2 - .../miele/internal/handler/HoodHandler.java | 15 +- .../handler/MieleApplianceHandler.java | 119 ++++---- .../internal/handler/MieleBridgeHandler.java | 273 +++++++++++------- .../internal/handler/OvenChannelSelector.java | 8 +- .../miele/internal/handler/OvenHandler.java | 15 +- .../handler/TumbleDryerChannelSelector.java | 6 +- .../internal/handler/TumbleDryerHandler.java | 13 +- .../WashingMachineChannelSelector.java | 13 +- .../handler/WashingMachineHandler.java | 13 +- .../main/resources/OH-INF/config/config.xml | 15 + .../resources/OH-INF/i18n/miele.properties | 107 +++++++ .../resources/OH-INF/i18n/miele_da.properties | 40 +++ .../resources/OH-INF/thing/coffeemachine.xml | 7 +- .../resources/OH-INF/thing/dishwasher.xml | 8 +- .../main/resources/OH-INF/thing/fridge.xml | 8 +- .../resources/OH-INF/thing/fridgefreezer.xml | 8 +- .../src/main/resources/OH-INF/thing/hob.xml | 8 +- .../src/main/resources/OH-INF/thing/hood.xml | 7 +- .../src/main/resources/OH-INF/thing/oven.xml | 8 +- .../resources/OH-INF/thing/tumbledryer.xml | 8 +- .../resources/OH-INF/thing/washingmachine.xml | 7 +- .../main/resources/OH-INF/thing/xgw3000.xml | 6 +- ...StateUtilTest.java => DeviceUtilTest.java} | 19 +- ...FullyQualifiedApplianceIdentifierTest.java | 2 +- 41 files changed, 513 insertions(+), 373 deletions(-) rename bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/{ExtendedDeviceStateUtil.java => DeviceUtil.java} (90%) create mode 100644 bundles/org.openhab.binding.miele/src/main/resources/OH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties create mode 100644 bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele_da.properties rename bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/{ExtendedDeviceStateUtilTest.java => DeviceUtilTest.java} (68%) diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtil.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java similarity index 90% rename from bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtil.java rename to bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java index dfe1fd0411c98..c80b28d38f0d4 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtil.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/DeviceUtil.java @@ -20,12 +20,12 @@ import org.openhab.core.types.UnDefType; /** - * The {@link ExtendedDeviceStateUtil} class contains utility methods for parsing - * ExtendedDeviceState information + * The {@link DeviceUtil} class contains utility methods for extracting + * and parsing device information, for example from ExtendedDeviceState. * - * @author Jacob Laursen - Added power/water consumption channels + * @author Jacob Laursen - Initial contribution */ -public class ExtendedDeviceStateUtil { +public class DeviceUtil { private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); private static final String TEMPERATURE_UNDEFINED = "32768"; 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 index 2ecfd854f4ee6..4d896fd195ed1 100644 --- 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 @@ -16,7 +16,7 @@ * 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) + * @author Jacob Laursen - Initial contribution */ public class FullyQualifiedApplianceIdentifier { private String uid; 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 d286209118f76..0ac74412f827b 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 @@ -28,8 +28,10 @@ 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 PROTOCOL_PROPERTY_NAME = "protocol"; + public static final String DEVICE_CLASS = "deviceClass"; + public static final String MODEL_PROPERTY_NAME = "model"; + public static final String PROTOCOL_ADAPTER_PROPERTY_NAME = "protocolAdapter"; + public static final String CONNECTION_TYPE_PROPERTY_NAME = "connectionType"; // JSON-RPC property names public static final String SERIAL_NUMBER_PROPERTY_NAME = "serialNumber"; @@ -65,8 +67,14 @@ public class MieleBindingConstants { // Miele devices classes public static final String MIELE_DEVICE_CLASS_COFFEE_SYSTEM = "CoffeeSystem"; + public static final String MIELE_DEVICE_CLASS_DISHWASHER = "Dishwasher"; public static final String MIELE_DEVICE_CLASS_FRIDGE = "Fridge"; public static final String MIELE_DEVICE_CLASS_FRIDGE_FREEZER = "FridgeFreezer"; + public static final String MIELE_DEVICE_CLASS_HOB = "Hob"; + public static final String MIELE_DEVICE_CLASS_HOOD = "Hood"; + public static final String MIELE_DEVICE_CLASS_OVEN = "Oven"; + public static final String MIELE_DEVICE_CLASS_TUMBLE_DRYER = "TumbleDryer"; + public static final String MIELE_DEVICE_CLASS_WASHING_MACHINE = "WashingMachine"; // Miele appliance states public static final int STATE_UNKNOWN = 0; 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 e604686cbe4b5..5c0de6e09951a 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 @@ -35,8 +35,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonElement; - /** * The {@link MieleApplianceDiscoveryService} tracks appliances that are * associated with the Miele@Home gateway @@ -47,9 +45,6 @@ */ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener { - private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance"; - private static final String MIELE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele"; - private final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class); private static final int SEARCH_TIME = 60; @@ -104,16 +99,17 @@ private void onApplianceAddedInternal(HomeDevice appliance) { Map properties = new HashMap<>(2); FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); - properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol()); + properties.put(MODEL_PROPERTY_NAME, appliance.getApplianceModel()); + String deviceClass = appliance.getDeviceClass(); + if (deviceClass != null) { + properties.put(DEVICE_CLASS, deviceClass); + } + properties.put(PROTOCOL_ADAPTER_PROPERTY_NAME, appliance.ProtocolAdapterName); properties.put(APPLIANCE_ID, applianceIdentifier.getApplianceId()); properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber()); - - for (JsonElement dc : appliance.DeviceClasses) { - String dcStr = dc.getAsString(); - if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) { - properties.put(DEVICE_CLASS, dcStr.substring(MIELE_CLASS.length())); - break; - } + String connectionType = appliance.getConnectionType(); + if (connectionType != null) { + properties.put(CONNECTION_TYPE_PROPERTY_NAME, connectionType); } DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) @@ -146,22 +142,9 @@ public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applian // nothing to do } - @Override - public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) { - // nothing to do - } - private ThingUID getThingUID(HomeDevice appliance) { ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID(); - String modelId = null; - - for (JsonElement dc : appliance.DeviceClasses) { - String dcStr = dc.getAsString(); - if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) { - modelId = dcStr.substring(MIELE_CLASS.length()); - break; - } - } + String modelId = appliance.getDeviceClass(); if (modelId != null) { ThingTypeUID thingTypeUID = getThingTypeUidFromModelId(modelId); @@ -183,7 +166,7 @@ private ThingTypeUID getThingTypeUidFromModelId(String modelId) { * coffeemachine. At least until it is known if any models are actually reported * as CoffeeMachine, we need this special mapping. */ - if (modelId.equals(MIELE_DEVICE_CLASS_COFFEE_SYSTEM)) { + if (MIELE_DEVICE_CLASS_COFFEE_SYSTEM.equals(modelId)) { return THING_TYPE_COFFEEMACHINE; } 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 eef48f6b0222f..729065323c8ca 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 @@ -26,7 +26,6 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; -import org.openhab.core.config.discovery.mdns.internal.MDNSDiscoveryService; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.osgi.service.component.annotations.Component; 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 9573812405df5..f11db64965dbe 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 @@ -43,14 +43,6 @@ public interface ApplianceStatusListener { */ 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 is called whenever an appliance is removed. * 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 c88a70863654f..bde856833f8b1 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 @@ -42,8 +42,6 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) { @@ -88,9 +86,9 @@ public State getState(String s, DeviceMetaData dmd) { private final Logger logger = LoggerFactory.getLogger(CoffeeMachineChannelSelector.class); - private final static Map programs = Collections. emptyMap(); + private static final Map programs = Collections. emptyMap(); - private final static Map phases = Collections. emptyMap(); + private static final Map phases = Collections. emptyMap(); private final String mieleID; private final String channelID; 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 2b5083d362120..cff9f134c5cc7 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 @@ -14,9 +14,7 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID; import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_COFFEE_SYSTEM; -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; @@ -49,8 +47,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelID = channelUID.getId(); String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); - String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); - var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol); CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID); JsonElement result = null; @@ -60,9 +56,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (selector) { case SWITCH: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff"); } break; } @@ -75,7 +71,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } // process result - if (isResultProcessable(result)) { + if (result != null && isResultProcessable(result)) { logger.debug("Result of operation is {}", result.getAsString()); } } catch (IllegalArgumentException e) { diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishWasherHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishWasherHandler.java index de0d8d61de711..d1281f43b82db 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishWasherHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/DishWasherHandler.java @@ -13,13 +13,12 @@ package org.openhab.binding.miele.internal.handler; import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID; +import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_DISHWASHER; import static org.openhab.binding.miele.internal.MieleBindingConstants.POWER_CONSUMPTION_CHANNEL_ID; -import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME; import static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID; import java.math.BigDecimal; -import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; @@ -51,7 +50,7 @@ public class DishWasherHandler extends MieleApplianceHandler programs = Map.ofEntries(entry("26", "Pots & Pans"), + private static final Map programs = Map.ofEntries(entry("26", "Pots & Pans"), entry("27", "Clean Machine"), entry("28", "Economy"), entry("30", "Normal"), entry("32", "Sensor Wash"), entry("34", "Energy Saver"), entry("35", "China & Crystal"), entry("36", "Extra Quiet"), entry("37", "SaniWash"), entry("38", "QuickPowerWash"), entry("42", "Tall items")); - private final static Map phases = Map.ofEntries(entry("2", "Pre-Wash"), entry("3", "Main Wash"), + private static final Map phases = Map.ofEntries(entry("2", "Pre-Wash"), entry("3", "Main Wash"), entry("4", "Rinses"), entry("6", "Final rinse"), entry("7", "Drying"), entry("8", "Finished")); private final String mieleID; diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeChannelSelector.java index 469e3a92582d7..c97cffd0fc527 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeChannelSelector.java @@ -17,7 +17,7 @@ import java.lang.reflect.Method; import java.util.Map.Entry; -import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil; +import org.openhab.binding.miele.internal.DeviceUtil; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -42,8 +42,6 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), SUPERCOOL(null, SUPERCOOL_CHANNEL_ID, OnOffType.class, false), @@ -148,7 +146,7 @@ public State getState(String s) { public State getTemperatureState(String s) { try { - return ExtendedDeviceStateUtil.getTemperatureState(s); + return DeviceUtil.getTemperatureState(s); } catch (NumberFormatException e) { 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/FridgeFreezerChannelSelector.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerChannelSelector.java index baed695db30a6..14d332dc0a1dc 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerChannelSelector.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerChannelSelector.java @@ -17,7 +17,7 @@ import java.lang.reflect.Method; import java.util.Map.Entry; -import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil; +import org.openhab.binding.miele.internal.DeviceUtil; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -43,8 +43,6 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), FREEZERSTATE("freezerState", "freezerstate", StringType.class, false), @@ -165,7 +163,7 @@ public State getState(String s) { public State getTemperatureState(String s) { try { - return ExtendedDeviceStateUtil.getTemperatureState(s); + return DeviceUtil.getTemperatureState(s); } catch (NumberFormatException e) { 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/FridgeFreezerHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerHandler.java index a3f665cb684cc..be9d5926cfbd1 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeFreezerHandler.java @@ -14,7 +14,6 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*; -import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; @@ -48,8 +47,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelID = channelUID.getId(); String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); - String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME); - var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol); FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID); JsonElement result = null; @@ -59,17 +56,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (selector) { case SUPERCOOL: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling"); } break; } case SUPERFREEZE: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperFreezing"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperFreezing"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperFreezing"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperFreezing"); } break; } @@ -80,7 +77,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } // process result - if (isResultProcessable(result)) { + if (result != null && isResultProcessable(result)) { logger.debug("Result of operation is {}", result.getAsString()); } } catch (IllegalArgumentException e) { @@ -94,7 +91,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { protected void onAppliancePropertyChanged(DeviceProperty dp) { super.onAppliancePropertyChanged(dp); - if (!dp.Name.equals(STATE_PROPERTY_NAME)) { + if (!STATE_PROPERTY_NAME.equals(dp.Name)) { return; } diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeHandler.java index 79462a5be3b3e..84bd5509f88f0 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/FridgeHandler.java @@ -14,7 +14,6 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*; -import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; @@ -49,8 +48,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelID = channelUID.getId(); 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; @@ -60,15 +57,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (selector) { case SUPERCOOL: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling"); } break; } case START: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "start"); } break; } @@ -81,7 +78,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } // process result - if (isResultProcessable(result)) { + if (result != null && isResultProcessable(result)) { logger.debug("Result of operation is {}", result.getAsString()); } } catch (IllegalArgumentException e) { @@ -95,7 +92,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { protected void onAppliancePropertyChanged(DeviceProperty dp) { super.onAppliancePropertyChanged(dp); - if (!dp.Name.equals(STATE_PROPERTY_NAME)) { + if (!STATE_PROPERTY_NAME.equals(dp.Name)) { return; } 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 a8a3ef2089729..abe82e7c86d65 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 @@ -37,8 +37,6 @@ public enum HobChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), PLATES("plateNumbers", "plates", DecimalType.class, true), diff --git a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobHandler.java index 538421b253dcc..bbbde6f630030 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/HobHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.miele.internal.handler; +import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_HOB; + import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.types.Command; @@ -26,7 +28,7 @@ public class HobHandler extends MieleApplianceHandler { public HobHandler(Thing thing) { - super(thing, HobChannelSelector.class, "Hob"); + super(thing, HobChannelSelector.class, MIELE_DEVICE_CLASS_HOB); } @Override 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 bdedbf2a3f013..623c9830d335c 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 @@ -39,8 +39,6 @@ public enum HoodChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), VENTILATION("ventilationPower", "ventilation", DecimalType.class, false), 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 f89c11d0db577..2211e0d26c848 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,9 +13,8 @@ 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 static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_HOOD; -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; @@ -39,7 +38,7 @@ public class HoodHandler extends MieleApplianceHandler { private final Logger logger = LoggerFactory.getLogger(HoodHandler.class); public HoodHandler(Thing thing) { - super(thing, HoodChannelSelector.class, "Hood"); + super(thing, HoodChannelSelector.class, MIELE_DEVICE_CLASS_HOOD); } @Override @@ -48,8 +47,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelID = channelUID.getId(); 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; @@ -59,15 +56,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (selector) { case LIGHT: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startLighting"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "startLighting"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopLighting"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stopLighting"); } break; } case STOP: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stop"); } break; } @@ -78,7 +75,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } // process result - if (isResultProcessable(result)) { + if (result != null && isResultProcessable(result)) { logger.debug("Result of operation is {}", result.getAsString()); } } catch (IllegalArgumentException e) { 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 b2a7b70e78b61..9c84e1a912a3e 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 @@ -20,8 +20,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang3.StringUtils; -import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil; +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.miele.internal.DeviceUtil; 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; @@ -84,8 +84,13 @@ public MieleApplianceHandler(Thing thing, Class selectorType, String modelID) public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelectorText) throws IllegalArgumentException { - for (ApplianceChannelSelector c : selectorType.getEnumConstants()) { - if (c.getChannelID() != null && c.getChannelID().equals(valueSelectorText)) { + E[] enumConstants = selectorType.getEnumConstants(); + if (enumConstants == null) { + throw new IllegalArgumentException( + String.format("Could not get enum constants for value selector: %s", valueSelectorText)); + } + for (ApplianceChannelSelector c : enumConstants) { + if (c != null && c.getChannelID() != null && c.getChannelID().equals(valueSelectorText)) { return c; } } @@ -95,8 +100,13 @@ public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelect public ApplianceChannelSelector getValueSelectorFromMieleID(String valueSelectorText) throws IllegalArgumentException { - for (ApplianceChannelSelector c : selectorType.getEnumConstants()) { - if (c.getMieleID() != null && c.getMieleID().equals(valueSelectorText)) { + E[] enumConstants = selectorType.getEnumConstants(); + if (enumConstants == null) { + throw new IllegalArgumentException( + String.format("Could not get enum constants for value selector: %s", valueSelectorText)); + } + for (ApplianceChannelSelector c : enumConstants) { + if (c != null && c.getMieleID() != null && c.getMieleID().equals(valueSelectorText)) { return c; } } @@ -110,16 +120,14 @@ public void initialize() { 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()); - } + this.onBridgeConnectionResumed(); } } public void onBridgeConnectionResumed() { - if (getMieleBridgeHandler() != null) { - ThingStatusInfo statusInfo = getBridge().getStatusInfo(); + Bridge bridge = getBridge(); + if (bridge != null && getMieleBridgeHandler() != null) { + ThingStatusInfo statusInfo = bridge.getStatusInfo(); updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription()); } } @@ -149,43 +157,32 @@ public void handleCommand(ChannelUID channelUID, Command command) { 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 (myApplianceId.equals(applicationIdentifier.getApplianceId())) { - if (modelID.equals(this.modelID)) { - for (JsonElement prop : dco.Properties.getAsJsonArray()) { - try { - DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class); - if (!dp.Name.equals(EXTENDED_DEVICE_STATE_PROPERTY_NAME)) { - dp.Value = StringUtils.trim(dp.Value); - dp.Value = StringUtils.strip(dp.Value); - } - - onAppliancePropertyChanged(applicationIdentifier, dp); - } catch (Exception p) { - // Ignore - this is due to an unrecognized and not yet reverse-engineered array property - } - } - } - } - } - - @Override - public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) { - String mySerialNumber = getThing().getProperties().get(SERIAL_NUMBER_PROPERTY_NAME); - if (!mySerialNumber.equals(serialNumber)) { + if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) { return; } - this.onAppliancePropertyChanged(dp); + for (JsonElement prop : dco.Properties.getAsJsonArray()) { + try { + DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class); + if (dp == null) { + continue; + } + if (!EXTENDED_DEVICE_STATE_PROPERTY_NAME.equals(dp.Name)) { + dp.Value = dp.Value.trim(); + dp.Value = dp.Value.strip(); + } + onAppliancePropertyChanged(applicationIdentifier, dp); + } catch (Exception p) { + // Ignore - this is due to an unrecognized and not yet reverse-engineered array property + } + } } @Override public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) { String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); - if (!myApplianceId.equals(applicationIdentifier.getApplianceId())) { + if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) { return; } @@ -202,14 +199,16 @@ protected void onAppliancePropertyChanged(DeviceProperty dp) { 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 (dmd != null) { + 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"); + String metadata = dp.Metadata.toString().replace("enum", "MieleEnum"); JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata); @@ -217,9 +216,9 @@ protected void onAppliancePropertyChanged(DeviceProperty dp) { if (dp.Name.equals(EXTENDED_DEVICE_STATE_PROPERTY_NAME)) { if (!dp.Value.isEmpty()) { - byte[] extendedStateBytes = ExtendedDeviceStateUtil.stringToBytes(dp.Value); + byte[] extendedStateBytes = DeviceUtil.stringToBytes(dp.Value); logger.trace("Extended device state for {}: {}", getThing().getUID(), - ExtendedDeviceStateUtil.bytesToHex(extendedStateBytes)); + DeviceUtil.bytesToHex(extendedStateBytes)); if (this instanceof ExtendedDeviceStateListener) { ((ExtendedDeviceStateListener) this).onApplianceExtendedStateChanged(extendedStateBytes); } @@ -234,7 +233,7 @@ protected void onAppliancePropertyChanged(DeviceProperty dp) { logger.trace("{} is not a valid channel for a {}", dp.Name, modelID); } - String dpValue = StringUtils.trim(StringUtils.strip(dp.Value)); + String dpValue = dp.Value.strip().trim(); if (selector != null) { if (!selector.isProperty()) { @@ -248,10 +247,11 @@ protected void onAppliancePropertyChanged(DeviceProperty dp) { } else { updateState(theChannelUID, UnDefType.UNDEF); } - } else if (dpValue != null) { + } else { logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(), getThing().getUID(), selector.getState(dpValue, dmd).toString()); - Map properties = editProperties(); + @NonNull + Map<@NonNull String, @NonNull String> properties = editProperties(); properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString()); updateProperties(properties); } @@ -317,9 +317,19 @@ public void onApplianceAdded(HomeDevice appliance) { FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); if (applianceId.equals(applianceIdentifier.getApplianceId())) { - Map properties = editProperties(); - properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol()); + @NonNull + Map<@NonNull String, @NonNull String> properties = editProperties(); + properties.put(MODEL_PROPERTY_NAME, appliance.getApplianceModel()); + String deviceClass = appliance.getDeviceClass(); + if (deviceClass != null) { + properties.put(DEVICE_CLASS, deviceClass); + } + properties.put(PROTOCOL_ADAPTER_PROPERTY_NAME, appliance.ProtocolAdapterName); properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber()); + String connectionType = appliance.getConnectionType(); + if (connectionType != null) { + properties.put(CONNECTION_TYPE_PROPERTY_NAME, connectionType); + } updateProperties(properties); updateStatus(ThingStatus.ONLINE); } @@ -343,6 +353,9 @@ private synchronized MieleBridgeHandler getMieleBridgeHandler() { } protected boolean isResultProcessable(JsonElement result) { - return result != null && !result.isJsonNull(); + if (result == null) { + throw new IllegalArgumentException("Provided result is null"); + } + return !result.isJsonNull(); } } 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 9f88b5f0075c4..78ea4e8bc6233 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 @@ -31,10 +31,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; 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; @@ -44,7 +47,7 @@ import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; -import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.common.NamedThreadFactory; import org.openhab.core.thing.Bridge; @@ -76,7 +79,10 @@ **/ public class MieleBridgeHandler extends BaseBridgeHandler { - public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000); + @NonNull + public static final Set<@NonNull ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000); + + private static final String MIELE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele"; 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])$"); @@ -97,7 +103,9 @@ public class MieleBridgeHandler extends BaseBridgeHandler { protected ExecutorService executor; protected Future eventListenerJob; - protected List previousHomeDevices = new CopyOnWriteArrayList<>(); + @NonNull + protected Map cachedHomeDevicesByApplianceId = new ConcurrentHashMap(); + protected Map cachedHomeDevicesByRemoteUid = new ConcurrentHashMap(); protected URL url; protected Map headers; @@ -105,6 +113,8 @@ public class MieleBridgeHandler extends BaseBridgeHandler { // Data structures to de-JSONify whatever Miele appliances are sending us public class HomeDevice { + private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance"; + public String Name; public String Status; public String ParentUID; @@ -125,9 +135,49 @@ public FullyQualifiedApplianceIdentifier getApplianceIdentifier() { return new FullyQualifiedApplianceIdentifier(this.UID); } + @NonNull public String getSerialNumber() { return Properties.get("serial.number").getAsString(); } + + @NonNull + public String getRemoteUid() { + JsonElement remoteUid = Properties.get("remote.uid"); + if (remoteUid == null) { + // remote.uid and serial.number seems to be the same. If remote.uid + // is missing for some reason, it makes sense to provide fallback + // to serial number. + return getSerialNumber(); + } + return remoteUid.getAsString(); + } + + public String getConnectionType() { + JsonElement connectionType = Properties.get("connection.type"); + if (connectionType == null) { + return null; + } + return connectionType.getAsString(); + } + + @NonNull + public String getApplianceModel() { + JsonElement model = Properties.get("miele.model"); + if (model == null) { + return ""; + } + return model.getAsString(); + } + + public String getDeviceClass() { + for (JsonElement dc : DeviceClasses) { + String dcStr = dc.getAsString(); + if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) { + return dcStr.substring(MIELE_CLASS.length()); + } + } + return null; + } } public class DeviceClassObject { @@ -206,95 +256,101 @@ public void initialize() { private Runnable pollingRunnable = new Runnable() { @Override public void run() { - if (IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) { - try { - if (isReachable((String) getConfig().get(HOST))) { - currentBridgeConnectionState = true; - } else { - currentBridgeConnectionState = false; - lastBridgeConnectionState = false; - onConnectionLost(); + if (!IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) { + logger.debug("Invalid IP address for the Miele@Home gateway : '{}'", getConfig().get(HOST)); + return; + } + + 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 && currentBridgeConnectionState) { - logger.debug("Connection to Miele Gateway {} established.", getConfig().get(HOST)); - lastBridgeConnectionState = true; - onConnectionResumed(); + @NonNull + Set<@NonNull Entry> cachedEntries = cachedHomeDevicesByApplianceId.entrySet(); + @NonNull + Iterator<@NonNull Entry> 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 (ApplianceStatusListener listener : applianceStatusListeners) { + listener.onApplianceRemoved(cachedHomeDevice); + } + cachedHomeDevicesByRemoteUid.remove(cachedHomeDevice.getRemoteUid()); + iterator.remove(); } + } - if (currentBridgeConnectionState) { - if (getThing().getStatus() == ThingStatus.ONLINE) { - List currentHomeDevices = getHomeDevices(); - for (HomeDevice hd : currentHomeDevices) { - boolean isExisting = false; - for (HomeDevice phd : previousHomeDevices) { - if (phd.UID.equals(hd.UID)) { - isExisting = true; - break; - } - } - if (!isExisting) { - logger.debug("A new appliance with ID '{}' has been added", hd.UID); - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onApplianceAdded(hd); - } - } - } + for (Thing appliance : getThing().getThings()) { + if (appliance.getStatus() == ThingStatus.ONLINE) { + String applianceId = (String) appliance.getConfiguration().getProperties().get(APPLIANCE_ID); + FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId( + applianceId); - for (HomeDevice hd : previousHomeDevices) { - boolean isCurrent = false; - for (HomeDevice chd : currentHomeDevices) { - if (chd.UID.equals(hd.UID)) { - isCurrent = true; - break; - } - } - if (!isCurrent) { - logger.debug("The appliance with ID '{}' has been removed", hd); - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onApplianceRemoved(hd); + if (applianceIdentifier == null) { + logger.error("The appliance with ID '{}' was not found in appliance list from bridge.", + applianceId); + continue; + } + + Object[] args = new Object[2]; + args[0] = applianceIdentifier.getUid(); + args[1] = true; + JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args); + + if (result != null) { + 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; } - } - } - previousHomeDevices = currentHomeDevices; - - for (Thing appliance : getThing().getThings()) { - if (appliance.getStatus() == ThingStatus.ONLINE) { - String applianceId = (String) appliance.getConfiguration().getProperties() - .get(APPLIANCE_ID); - String protocol = appliance.getProperties().get(PROTOCOL_PROPERTY_NAME); - var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, - protocol); - - Object[] args = new Object[2]; - args[0] = applianceIdentifier.getUid(); - args[1] = true; - JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args); - - if (result != null) { - for (JsonElement obj : result.getAsJsonArray()) { - try { - DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class); - - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onApplianceStateChanged(applianceIdentifier, dco); - } - } catch (Exception e) { - logger.debug("An exception occurred while querying an appliance : '{}'", - e.getMessage()); - } - } + for (ApplianceStatusListener listener : applianceStatusListeners) { + listener.onApplianceStateChanged(applianceIdentifier, dco); } + } catch (Exception e) { + logger.debug("An exception occurred while querying an appliance : '{}'", + e.getMessage()); } } } } - } catch (Exception e) { - logger.debug("An exception occurred while polling an appliance :'{}'", e.getMessage()); } - } else { - logger.debug("Invalid IP address for the Miele@Home gateway : '{}'", getConfig().get(HOST)); + } catch (Exception e) { + logger.debug("An exception occurred while polling an appliance :'{}'", e.getMessage()); } } @@ -339,6 +395,15 @@ public List getHomeDevices() { return devices; } + private FullyQualifiedApplianceIdentifier getApplianceIdentifierFromApplianceId(String applianceId) { + HomeDevice homeDevice = this.cachedHomeDevicesByApplianceId.get(applianceId); + if (homeDevice == null) { + return null; + } + + return homeDevice.getApplianceIdentifier(); + } + private Runnable eventListenerRunnable = () -> { if (IP_PATTERN.matcher((String) getConfig().get(INTERFACE)).matches()) { while (true) { @@ -378,16 +443,16 @@ public List getHomeDevices() { DeviceProperty dp = new DeviceProperty(); String id = null; - String[] parts = StringUtils.split(event, "&"); + String[] parts = event.split("&"); for (String p : parts) { - String[] subparts = StringUtils.split(p, "="); + String[] subparts = p.split("="); switch (subparts[0]) { case "property": { dp.Name = subparts[1]; break; } case "value": { - dp.Value = StringUtils.trim(StringUtils.strip(subparts[1])); + dp.Value = subparts[1].strip().trim(); break; } case "id": { @@ -403,15 +468,19 @@ public List getHomeDevices() { // In XGW 3000 firmware 2.03 this was changed from UID (hdm:ZigBee:0123456789abcdef#210) // to serial number (001234567890) + FullyQualifiedApplianceIdentifier applianceIdentifier; if (id.startsWith("hdm:")) { - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onAppliancePropertyChanged(new FullyQualifiedApplianceIdentifier(id), - dp); - } + applianceIdentifier = new FullyQualifiedApplianceIdentifier(id); } else { - for (ApplianceStatusListener listener : applianceStatusListeners) { - listener.onAppliancePropertyChanged(id, dp); + HomeDevice device = cachedHomeDevicesByRemoteUid.get(id); + if (device == null) { + logger.debug("Multicast event not handled as id {} is unknown.", id); + continue; } + applianceIdentifier = device.getApplianceIdentifier(); + } + for (ApplianceStatusListener listener : applianceStatusListeners) { + listener.onAppliancePropertyChanged(applianceIdentifier, dp); } } catch (SocketTimeoutException e) { try { @@ -445,19 +514,27 @@ public List getHomeDevices() { } }; - public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID, - String methodName) { - if (getThing().getStatus() == ThingStatus.ONLINE) { - Object[] args = new Object[4]; - args[0] = applianceIdentifier.getUid(); - args[1] = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele" + modelID; - args[2] = methodName; - args[3] = null; - return invokeRPC("HDAccess/invokeDCOOperation", args); - } else { + public JsonElement invokeOperation(String applianceId, String modelID, String methodName) { + if (getThing().getStatus() != ThingStatus.ONLINE) { logger.debug("The Bridge is offline - operations can not be invoked."); return null; } + + FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId(applianceId); + if (applianceIdentifier == null) { + logger.error( + "The appliance with ID '{}' was not found in appliance list from bridge - operations can not be invoked.", + applianceId); + return null; + } + + 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); } protected JsonElement invokeRPC(String methodName, Object[] args) { 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 7866c2e806118..f6814a8163a9a 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 @@ -22,7 +22,7 @@ import java.util.Map.Entry; import java.util.TimeZone; -import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil; +import org.openhab.binding.miele.internal.DeviceUtil; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -49,8 +49,6 @@ public enum OvenChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false), @@ -167,7 +165,7 @@ public State getState(String s, DeviceMetaData dmd) { private final Logger logger = LoggerFactory.getLogger(OvenChannelSelector.class); - private final static Map phases = Map.ofEntries(entry("1", "Heating"), entry("2", "Temp. hold"), + private static final Map phases = Map.ofEntries(entry("1", "Heating"), entry("2", "Temp. hold"), entry("3", "Door Open"), entry("4", "Pyrolysis"), entry("7", "Lighting"), entry("8", "Searing phase"), entry("10", "Defrost"), entry("11", "Cooling down"), entry("12", "Energy save phase")); @@ -241,7 +239,7 @@ public State getState(String s) { public State getTemperatureState(String s) { try { - return ExtendedDeviceStateUtil.getTemperatureState(s); + return DeviceUtil.getTemperatureState(s); } catch (NumberFormatException e) { 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 20d2fdb0ae09c..13799659e9ad3 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 @@ -13,9 +13,8 @@ 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 static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_OVEN; -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; @@ -40,7 +39,7 @@ public class OvenHandler extends MieleApplianceHandler { private final Logger logger = LoggerFactory.getLogger(OvenHandler.class); public OvenHandler(Thing thing) { - super(thing, OvenChannelSelector.class, "Oven"); + super(thing, OvenChannelSelector.class, MIELE_DEVICE_CLASS_OVEN); } @Override @@ -49,8 +48,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelID = channelUID.getId(); 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; @@ -60,15 +57,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (selector) { case SWITCH: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn"); } else if (command.equals(OnOffType.OFF)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff"); } break; } case STOP: { if (command.equals(OnOffType.ON)) { - result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); + result = bridgeHandler.invokeOperation(applianceId, modelID, "stop"); } break; } @@ -81,7 +78,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } // process result - if (isResultProcessable(result)) { + if (result != null && isResultProcessable(result)) { logger.debug("Result of operation is {}", result.getAsString()); } } catch (IllegalArgumentException e) { 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 8c990860a9dc8..1a375da9e4447 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 @@ -47,8 +47,6 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector { PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true), - BRAND_ID("brandId", "brandId", StringType.class, true), - COMPANY_ID("companyId", "companyId", StringType.class, true), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) { @@ -155,7 +153,7 @@ public State getState(String s, DeviceMetaData dmd) { private final Logger logger = LoggerFactory.getLogger(TumbleDryerChannelSelector.class); - private final static Map programs = Map.ofEntries(entry("10", "Automatic Plus"), + private static final Map programs = Map.ofEntries(entry("10", "Automatic Plus"), entry("23", "Cottons hygiene"), entry("30", "Minimum iron"), entry("31", "Gentle minimum iron"), entry("40", "Woollens handcare"), entry("50", "Delicates"), entry("60", "Warm Air"), entry("70", "Cool air"), entry("80", "Express"), entry("90", "Cottons"), entry("100", "Gentle smoothing"), @@ -165,7 +163,7 @@ public State getState(String s, DeviceMetaData dmd) { entry("240", "Smoothing"), entry("65000", "Cottons, auto load control"), entry("65001", "Minimum iron, auto load control")); - private final static Map phases = Map.ofEntries(entry("1", "Programme running"), + private static final Map phases = Map.ofEntries(entry("1", "Programme running"), entry("2", "Drying"), entry("3", "Drying Machine iron"), entry("4", "Drying Hand iron"), entry("5", "Drying Normal"), entry("6", "Drying Normal+"), entry("7", "Cooling down"), entry("8", "Drying Hand iron"), entry("10", "Finished")); 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 3ffe5cc12a513..7e56d6381fc5e 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,9 +13,8 @@ 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 static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_TUMBLE_DRYER; -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; @@ -40,7 +39,7 @@ public class TumbleDryerHandler extends MieleApplianceHandler programs = Map.ofEntries(entry("1", "Cottons"), entry("3", "Minimum iron"), + private static final Map programs = Map.ofEntries(entry("1", "Cottons"), entry("3", "Minimum iron"), entry("4", "Delicates"), entry("8", "Woollens"), entry("9", "Silks"), entry("17", "Starch"), entry("18", "Rinse"), entry("21", "Drain/Spin"), entry("22", "Curtains"), entry("23", "Shirts"), entry("24", "Denim"), entry("27", "Proofing"), entry("29", "Sportswear"), entry("31", "Automatic Plus"), @@ -183,7 +180,7 @@ public State getState(String s, DeviceMetaData dmd) { entry("95", "Down duvets"), entry("122", "Express 20"), entry("129", "Down filled items"), entry("133", "Cottons Eco"), entry("146", "QuickPowerWash"), entry("65532", "Mix")); - private final static Map phases = Map.ofEntries(entry("1", "Pre-wash"), entry("4", "Washing"), + private static final Map phases = Map.ofEntries(entry("1", "Pre-wash"), entry("4", "Washing"), entry("5", "Rinses"), entry("7", "Clean"), entry("9", "Drain"), entry("10", "Spin"), entry("11", "Anti-crease"), entry("12", "Finished")); @@ -260,7 +257,7 @@ public State getState(String s) { public State getTemperatureState(String s) { try { - return ExtendedDeviceStateUtil.getTemperatureState(s); + return DeviceUtil.getTemperatureState(s); } catch (NumberFormatException e) { 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/WashingMachineHandler.java b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineHandler.java index 350281a45fba0..4c330cb07bc82 100644 --- a/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineHandler.java +++ b/bundles/org.openhab.binding.miele/src/main/java/org/openhab/binding/miele/internal/handler/WashingMachineHandler.java @@ -13,13 +13,12 @@ package org.openhab.binding.miele.internal.handler; import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID; +import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_WASHING_MACHINE; import static org.openhab.binding.miele.internal.MieleBindingConstants.POWER_CONSUMPTION_CHANNEL_ID; -import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME; import static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID; import java.math.BigDecimal; -import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; @@ -51,7 +50,7 @@ public class WashingMachineHandler extends MieleApplianceHandler + + + + + + Unique identifier for specific appliance on the gateway. + + + + diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties new file mode 100644 index 0000000000000..94e0949fa0e6d --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele.properties @@ -0,0 +1,107 @@ +# binding + +binding.miele.name = Miele Binding +binding.miele.description = This is the binding for Miele@home appliances + +# thing types + +thing-type.miele.coffeemachine.label = Coffee Machine +thing-type.miele.coffeemachine.description = This is a Miele@home compatible coffee machine +thing-type.miele.dishwasher.label = Dishwasher +thing-type.miele.dishwasher.description = This is a Miele@home compatible dishwasher +thing-type.miele.fridge.label = Fridge +thing-type.miele.fridge.description = This is a Miele@home compatible fridge +thing-type.miele.fridgefreezer.label = Fridge Freezer +thing-type.miele.fridgefreezer.description = This is a Miele@home compatible fridgefreezer +thing-type.miele.hob.label = Hob +thing-type.miele.hob.description = This is a Miele@home compatible hob +thing-type.miele.hood.label = Hood +thing-type.miele.hood.description = This is a Miele@home compatible hood +thing-type.miele.oven.label = Oven +thing-type.miele.oven.description = This is a Miele@home compatible oven +thing-type.miele.tumbledryer.label = Tumbledryer +thing-type.miele.tumbledryer.description = This is a Miele@home compatible tumbledryer +thing-type.miele.washingmachine.label = Washing Machine +thing-type.miele.washingmachine.description = This is a Miele@home compatible washing machine +thing-type.miele.xgw3000.label = Miele XGW3000 +thing-type.miele.xgw3000.description = The Miele bridge represents the Miele@home XGW3000 gateway. + +# thing types config + +thing-type.config.miele.appliance.uid.label = ID +thing-type.config.miele.appliance.uid.description = Unique identifier for specific appliance on the gateway. +thing-type.config.miele.xgw3000.interface.label = Network Address of the Multicast Interface +thing-type.config.miele.xgw3000.interface.description = Network address of openHAB host interface where the binding will listen for multicast events coming from the Miele@home gateway. +thing-type.config.miele.xgw3000.ipAddress.label = Network Address +thing-type.config.miele.xgw3000.ipAddress.description = Network address of the Miele@home gateway. +thing-type.config.miele.xgw3000.password.label = Password +thing-type.config.miele.xgw3000.password.description = Password for the registered Miele@home user. +thing-type.config.miele.xgw3000.userName.label = Username +thing-type.config.miele.xgw3000.userName.description = Name of a registered Miele@home user. + +# channel types + +channel-type.miele.currentTemperature.label = Current Temperature +channel-type.miele.currentTemperature.description = Current temperature of the appliance +channel-type.miele.door.label = Door +channel-type.miele.door.description = Current state of the door of the appliance +channel-type.miele.duration.label = Duration +channel-type.miele.duration.description = Duration of the program running on the appliance +channel-type.miele.duration.state.pattern = %1$tH:%1$tM +channel-type.miele.elapsed.label = Elapsed Time +channel-type.miele.elapsed.description = Time elapsed in the program running on the appliance +channel-type.miele.elapsed.state.pattern = %1$tH:%1$tM +channel-type.miele.finish.label = Finish Time +channel-type.miele.finish.description = Time to finish the program running on the appliance +channel-type.miele.finish.state.pattern = %1$tH:%1$tM +channel-type.miele.freezerstate.label = Status +channel-type.miele.freezerstate.description = Current status of the freezer compartment +channel-type.miele.fridgestate.label = Status +channel-type.miele.fridgestate.description = Current status of the fridge compartment +channel-type.miele.heat.label = Remaining Heat +channel-type.miele.heat.description = Remaining heat level of the heating zone/plate +channel-type.miele.phase.label = Phase +channel-type.miele.phase.description = Current phase of the program running on the appliance +channel-type.miele.plates.label = Plates +channel-type.miele.plates.description = Number of heating zones/plates on the hob +channel-type.miele.power.label = Power Step +channel-type.miele.power.description = Power level of the heating zone/plate +channel-type.miele.powerConsumption.label = Power Consumption +channel-type.miele.powerConsumption.description = Power consumption by the currently running program on the appliance +channel-type.miele.program.label = Program +channel-type.miele.program.description = Current program or function running on the appliance +channel-type.miele.rawPhase.label = Raw Phase +channel-type.miele.rawPhase.description = Current phase of the program running on the appliance as raw number +channel-type.miele.rawProgram.label = Raw Program +channel-type.miele.rawProgram.description = Current program or function running on the appliance as raw number +channel-type.miele.rawState.label = Raw State +channel-type.miele.rawState.description = Current status of the appliance as raw number +channel-type.miele.spinningspeed.label = Spinning Speed +channel-type.miele.spinningspeed.description = Spinning speed in the program running on the appliance +channel-type.miele.start.label = Start Time +channel-type.miele.start.description = Programmed start time of the program +channel-type.miele.start.state.pattern = %1$tH:%1$tM +channel-type.miele.state.label = State +channel-type.miele.state.description = Current status of the appliance +channel-type.miele.step.label = Step +channel-type.miele.step.description = Current step in the program running on the appliance +channel-type.miele.stop.label = Stop +channel-type.miele.stop.description = Stop the appliance +channel-type.miele.supercool.label = Super Cool +channel-type.miele.supercool.description = Start or stop Super Cooling +channel-type.miele.superfreeze.label = Super Freeze +channel-type.miele.superfreeze.description = Start or stop Super Freezing +channel-type.miele.switch.label = Switch +channel-type.miele.switch.description = Switch the appliance on or off +channel-type.miele.targetTemperature.label = Target Temperature +channel-type.miele.targetTemperature.description = Target temperature to be reached by the appliance +channel-type.miele.temperature.label = Temperature +channel-type.miele.temperature.description = Temperature reported by the appliance +channel-type.miele.time.label = Remaining Time +channel-type.miele.time.description = Remaining time of the heating zone/plate +channel-type.miele.type.label = Program Type +channel-type.miele.type.description = Type of the program running on the appliance +channel-type.miele.ventilation.label = Ventilation Power +channel-type.miele.ventilation.description = Current ventilation power +channel-type.miele.waterConsumption.label = Water Consumption +channel-type.miele.waterConsumption.description = Water consumption by the currently running program on the appliance diff --git a/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele_da.properties b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele_da.properties new file mode 100644 index 0000000000000..488269b00a7d9 --- /dev/null +++ b/bundles/org.openhab.binding.miele/src/main/resources/OH-INF/i18n/miele_da.properties @@ -0,0 +1,40 @@ +# binding + +binding.miele.name = Miele Binding +binding.miele.description = Dette er bindingen til Miele@home-husholdningsapparater + +# thing types + +thing-type.miele.coffeemachine.label = Kaffemaskine +thing-type.miele.coffeemachine.description = Dette er en Miele@home-kompatibel kaffemaskine +thing-type.miele.dishwasher.label = Opvaskemaskine +thing-type.miele.dishwasher.description = Dette er en Miele@home-kompatibel opvaskemaskine +thing-type.miele.fridge.label = Køleskab +thing-type.miele.fridge.description = Dette er et Miele@home-kompatibelt køleskab +thing-type.miele.fridgefreezer.label = Kølefryseskab +thing-type.miele.fridgefreezer.description = Dette er et Miele@home-kompatibelt kølefryseskab +thing-type.miele.hob.label = Kogeplader +thing-type.miele.hob.description = Dette er Miele@home-kompatible kogeplader +thing-type.miele.hood.label = Emhætte +thing-type.miele.hood.description = Dette er en Miele@home-kompatibel emhætte +thing-type.miele.oven.label = Ovn +thing-type.miele.oven.description = Dette er en Miele@home-kompatibel ovn +thing-type.miele.tumbledryer.label = Tørretumbler +thing-type.miele.tumbledryer.description = Dette er en Miele@home-kompatibel tørretumbler +thing-type.miele.washingmachine.label = Vaskemaskine +thing-type.miele.washingmachine.description = Dette er en Miele@home-kompatibel vaskemaskine +thing-type.miele.xgw3000.label = Miele XGW3000 +thing-type.miele.xgw3000.description = Miele-bridgen repræsenterer Miele@home XGW3000-gateway'en. + +# thing types config + +thing-type.config.miele.appliance.uid.label = ID +thing-type.config.miele.appliance.uid.description = Unik identifikator til specifikt husholdningsapparat på gateway'en. +thing-type.config.miele.xgw3000.interface.label = Netværksadresse til multicast-interfacet +thing-type.config.miele.xgw3000.interface.description = Netværksadresse til openHAB værts-interfacet hvor bindingen vil lytte på multicast-hændelser fra Miele@home-gateway'en. +thing-type.config.miele.xgw3000.ipAddress.label = Netværksadresse +thing-type.config.miele.xgw3000.ipAddress.description = Netværksadresse til Miele@home-gateway'en. +thing-type.config.miele.xgw3000.password.label = Adgangskode +thing-type.config.miele.xgw3000.password.description = Adgangskode til registreret Miele@home-bruger. +thing-type.config.miele.xgw3000.userName.label = Brugernavn +thing-type.config.miele.xgw3000.userName.description = Navn på en registeret Miele@home-bruger. 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 d7ae1d8bcff2a..92d7a9c3cddb2 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 @@ -27,12 +27,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - + 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 a50081e91ac73..dbcd85123999e 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 @@ -32,13 +32,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 8e154f1870ffa..4685f0cfad650 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 @@ -29,13 +29,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 c257767d84063..23fe18099daea 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 @@ -38,13 +38,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 a3897500329d8..ce387651976b4 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 @@ -38,13 +38,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 a396b962de4e3..77888cc0c0c88 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 @@ -23,12 +23,7 @@ uid - - - - The identifier identifies the appliance on the ZigBee network. - - + 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 58fa91d19679e..98dd1b5b7e0d5 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 @@ -47,13 +47,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 ea5716fb9a26b..8655a1f3674d6 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 @@ -32,13 +32,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - - + 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 39a2a95b5ac62..d704a54bffc05 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 @@ -38,12 +38,7 @@ uid - - - - The identifier identifies one certain appliance on the ZigBee network. - - + 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 bdabbde84332c..58d9ca23cbac4 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 @@ -7,7 +7,7 @@ - The miele bridge represents the Miele@home XGW3000 gateway. + The Miele bridge represents the Miele@home XGW3000 gateway. Miele @@ -25,7 +25,7 @@ network-address Network address of openHAB host interface where the binding will listen for multicast events coming - from the Miele@home gateway + from the Miele@home gateway. @@ -36,7 +36,7 @@ password - Password for the registered Miele@home + Password for the registered Miele@home user. diff --git a/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtilTest.java b/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/DeviceUtilTest.java similarity index 68% rename from bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtilTest.java rename to bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/DeviceUtilTest.java index 88c2518aa6536..d159dd5416f90 100644 --- a/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/ExtendedDeviceStateUtilTest.java +++ b/bundles/org.openhab.binding.miele/src/test/java/org/openhab/binding/miele/internal/DeviceUtilTest.java @@ -22,17 +22,16 @@ /** * This class provides test cases for {@link - * org.openhab.binding.miele.internal.ExtendedDeviceStateUtil} + * org.openhab.binding.miele.internal.DeviceUtil} * - * @author Jacob Laursen - Added power/water consumption channels + * @author Jacob Laursen - Initial contribution */ -public class ExtendedDeviceStateUtilTest extends JavaTest { +public class DeviceUtilTest extends JavaTest { @Test public void bytesToHexWhenTopBitIsUsedReturnsCorrectString() { - String actual = ExtendedDeviceStateUtil - .bytesToHex(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef }); + String actual = DeviceUtil.bytesToHex(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef }); assertEquals("DEADBEEF", actual); } @@ -45,27 +44,27 @@ public void bytesToHexWhenTopBitIsUsedReturnsCorrectString() { @Test public void stringToBytesWhenTopBitIsUsedReturnsSingleByte() { byte[] expected = new byte[] { (byte) 0x00, (byte) 0x80, (byte) 0x00 }; - byte[] actual = ExtendedDeviceStateUtil.stringToBytes("\u0000\u0080\u0000"); + byte[] actual = DeviceUtil.stringToBytes("\u0000\u0080\u0000"); assertArrayEquals(expected, actual); } @Test public void getTemperatureStateWellFormedValueReturnsQuantityType() throws NumberFormatException { - assertEquals(new QuantityType<>(42, SIUnits.CELSIUS), ExtendedDeviceStateUtil.getTemperatureState("42")); + assertEquals(new QuantityType<>(42, SIUnits.CELSIUS), DeviceUtil.getTemperatureState("42")); } @Test public void getTemperatureStateMagicValueReturnsUndefined() throws NumberFormatException { - assertEquals(UnDefType.UNDEF, ExtendedDeviceStateUtil.getTemperatureState("32768")); + assertEquals(UnDefType.UNDEF, DeviceUtil.getTemperatureState("32768")); } @Test public void getTemperatureStateNonNumericValueThrowsNumberFormatException() { - assertThrows(NumberFormatException.class, () -> ExtendedDeviceStateUtil.getTemperatureState("A")); + assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState("A")); } @Test public void getTemperatureStateNullValueThrowsNumberFormatException() { - assertThrows(NumberFormatException.class, () -> ExtendedDeviceStateUtil.getTemperatureState(null)); + assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState(null)); } } 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 index f352b6cff5f43..5b8923da3a320 100644 --- 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 @@ -21,7 +21,7 @@ * This class provides test cases for {@link * org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier} * - * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) + * @author Jacob Laursen - Initial contribution */ public class FullyQualifiedApplianceIdentifierTest extends JavaTest {