[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 <jacob-github@vindvejr.dk>
This commit is contained in:
jlaur 2021-11-01 18:44:19 +01:00 committed by GitHub
parent dd4019c172
commit 745bf76569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 514 additions and 374 deletions

View File

@ -20,12 +20,12 @@ import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType; import org.openhab.core.types.UnDefType;
/** /**
* The {@link ExtendedDeviceStateUtil} class contains utility methods for parsing * The {@link DeviceUtil} class contains utility methods for extracting
* ExtendedDeviceState information * 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 byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
private static final String TEMPERATURE_UNDEFINED = "32768"; private static final String TEMPERATURE_UNDEFINED = "32768";

View File

@ -16,7 +16,7 @@ package org.openhab.binding.miele.internal;
* The {@link FullyQualifiedApplianceIdentifier} class represents a fully qualified appliance identifier. * The {@link FullyQualifiedApplianceIdentifier} class represents a fully qualified appliance identifier.
* Example: "hdm:ZigBee:0123456789abcdef#210" * Example: "hdm:ZigBee:0123456789abcdef#210"
* *
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN) * @author Jacob Laursen - Initial contribution
*/ */
public class FullyQualifiedApplianceIdentifier { public class FullyQualifiedApplianceIdentifier {
private String uid; private String uid;

View File

@ -28,8 +28,10 @@ public class MieleBindingConstants {
public static final String BINDING_ID = "miele"; public static final String BINDING_ID = "miele";
public static final String APPLIANCE_ID = "uid"; public static final String APPLIANCE_ID = "uid";
public static final String DEVICE_CLASS = "dc"; public static final String DEVICE_CLASS = "deviceClass";
public static final String PROTOCOL_PROPERTY_NAME = "protocol"; 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 // JSON-RPC property names
public static final String SERIAL_NUMBER_PROPERTY_NAME = "serialNumber"; public static final String SERIAL_NUMBER_PROPERTY_NAME = "serialNumber";
@ -65,8 +67,14 @@ public class MieleBindingConstants {
// Miele devices classes // Miele devices classes
public static final String MIELE_DEVICE_CLASS_COFFEE_SYSTEM = "CoffeeSystem"; 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 = "Fridge";
public static final String MIELE_DEVICE_CLASS_FRIDGE_FREEZER = "FridgeFreezer"; 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 // Miele appliance states
public static final int STATE_UNKNOWN = 0; public static final int STATE_UNKNOWN = 0;

View File

@ -35,8 +35,6 @@ import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
/** /**
* The {@link MieleApplianceDiscoveryService} tracks appliances that are * The {@link MieleApplianceDiscoveryService} tracks appliances that are
* associated with the Miele@Home gateway * associated with the Miele@Home gateway
@ -47,9 +45,6 @@ import com.google.gson.JsonElement;
*/ */
public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener { 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 final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class);
private static final int SEARCH_TIME = 60; private static final int SEARCH_TIME = 60;
@ -104,16 +99,17 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
Map<String, Object> properties = new HashMap<>(2); Map<String, Object> properties = new HashMap<>(2);
FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); 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(APPLIANCE_ID, applianceIdentifier.getApplianceId());
properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber()); properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber());
String connectionType = appliance.getConnectionType();
for (JsonElement dc : appliance.DeviceClasses) { if (connectionType != null) {
String dcStr = dc.getAsString(); properties.put(CONNECTION_TYPE_PROPERTY_NAME, connectionType);
if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) {
properties.put(DEVICE_CLASS, dcStr.substring(MIELE_CLASS.length()));
break;
}
} }
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
@ -146,22 +142,9 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
// nothing to do // nothing to do
} }
@Override
public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) {
// nothing to do
}
private ThingUID getThingUID(HomeDevice appliance) { private ThingUID getThingUID(HomeDevice appliance) {
ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID(); ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID();
String modelId = null; String modelId = appliance.getDeviceClass();
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;
}
}
if (modelId != null) { if (modelId != null) {
ThingTypeUID thingTypeUID = getThingTypeUidFromModelId(modelId); ThingTypeUID thingTypeUID = getThingTypeUidFromModelId(modelId);
@ -183,7 +166,7 @@ public class MieleApplianceDiscoveryService extends AbstractDiscoveryService imp
* coffeemachine. At least until it is known if any models are actually reported * coffeemachine. At least until it is known if any models are actually reported
* as CoffeeMachine, we need this special mapping. * 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; return THING_TYPE_COFFEEMACHINE;
} }

View File

@ -26,7 +26,6 @@ import org.openhab.binding.miele.internal.MieleBindingConstants;
import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; 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.ThingTypeUID;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;

View File

@ -43,14 +43,6 @@ public interface ApplianceStatusListener {
*/ */
void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, 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 is called whenever an appliance is removed. * This method is called whenever an appliance is removed.
* *

View File

@ -42,8 +42,6 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) { PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
@ -88,9 +86,9 @@ public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
private final Logger logger = LoggerFactory.getLogger(CoffeeMachineChannelSelector.class); private final Logger logger = LoggerFactory.getLogger(CoffeeMachineChannelSelector.class);
private final static Map<String, String> programs = Collections.<String, String> emptyMap(); private static final Map<String, String> programs = Collections.<String, String> emptyMap();
private final static Map<String, String> phases = Collections.<String, String> emptyMap(); private static final Map<String, String> phases = Collections.<String, String> emptyMap();
private final String mieleID; private final String mieleID;
private final String channelID; private final String channelID;

View File

@ -14,9 +14,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.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_COFFEE_SYSTEM; 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.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -49,8 +47,6 @@ public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineCha
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID); CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -60,9 +56,9 @@ public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineCha
switch (selector) { switch (selector) {
case SWITCH: { case SWITCH: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn"); result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff"); result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
} }
break; break;
} }
@ -75,7 +71,7 @@ public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineCha
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -13,13 +13,12 @@
package org.openhab.binding.miele.internal.handler; 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.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.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 static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
@ -51,7 +50,7 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
private final Logger logger = LoggerFactory.getLogger(DishWasherHandler.class); private final Logger logger = LoggerFactory.getLogger(DishWasherHandler.class);
public DishWasherHandler(Thing thing) { public DishWasherHandler(Thing thing) {
super(thing, DishwasherChannelSelector.class, "Dishwasher"); super(thing, DishwasherChannelSelector.class, MIELE_DEVICE_CLASS_DISHWASHER);
} }
@Override @Override
@ -60,8 +59,6 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
DishwasherChannelSelector selector = (DishwasherChannelSelector) getValueSelectorFromChannelID(channelID); DishwasherChannelSelector selector = (DishwasherChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -71,9 +68,9 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
switch (selector) { switch (selector) {
case SWITCH: { case SWITCH: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
} }
break; break;
} }
@ -86,7 +83,7 @@ public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSe
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -48,8 +48,6 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
BRAND_ID("brandId", "brandId", StringType.class, true, false),
COMPANY_ID("companyId", "companyId", StringType.class, true, false),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) { PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
@ -152,12 +150,12 @@ public enum DishwasherChannelSelector implements ApplianceChannelSelector {
private final Logger logger = LoggerFactory.getLogger(DishwasherChannelSelector.class); private final Logger logger = LoggerFactory.getLogger(DishwasherChannelSelector.class);
private final static Map<String, String> programs = Map.ofEntries(entry("26", "Pots & Pans"), private static final Map<String, String> programs = Map.ofEntries(entry("26", "Pots & Pans"),
entry("27", "Clean Machine"), entry("28", "Economy"), entry("30", "Normal"), entry("32", "Sensor Wash"), 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("34", "Energy Saver"), entry("35", "China & Crystal"), entry("36", "Extra Quiet"),
entry("37", "SaniWash"), entry("38", "QuickPowerWash"), entry("42", "Tall items")); entry("37", "SaniWash"), entry("38", "QuickPowerWash"), entry("42", "Tall items"));
private final static Map<String, String> phases = Map.ofEntries(entry("2", "Pre-Wash"), entry("3", "Main Wash"), private static final Map<String, String> phases = Map.ofEntries(entry("2", "Pre-Wash"), entry("3", "Main Wash"),
entry("4", "Rinses"), entry("6", "Final rinse"), entry("7", "Drying"), entry("8", "Finished")); entry("4", "Rinses"), entry("6", "Final rinse"), entry("7", "Drying"), entry("8", "Finished"));
private final String mieleID; private final String mieleID;

View File

@ -17,7 +17,7 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map.Entry; 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.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
@ -42,8 +42,6 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
SUPERCOOL(null, SUPERCOOL_CHANNEL_ID, OnOffType.class, false), SUPERCOOL(null, SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
@ -148,7 +146,7 @@ public enum FridgeChannelSelector implements ApplianceChannelSelector {
public State getTemperatureState(String s) { public State getTemperatureState(String s) {
try { try {
return ExtendedDeviceStateUtil.getTemperatureState(s); return DeviceUtil.getTemperatureState(s);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.warn("An exception occurred while converting '{}' into a State", s); logger.warn("An exception occurred while converting '{}' into a State", s);
return UnDefType.UNDEF; return UnDefType.UNDEF;

View File

@ -17,7 +17,7 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map.Entry; 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.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
@ -43,8 +43,6 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
FREEZERSTATE("freezerState", "freezerstate", StringType.class, false), FREEZERSTATE("freezerState", "freezerstate", StringType.class, false),
@ -165,7 +163,7 @@ public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
public State getTemperatureState(String s) { public State getTemperatureState(String s) {
try { try {
return ExtendedDeviceStateUtil.getTemperatureState(s); return DeviceUtil.getTemperatureState(s);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.warn("An exception occurred while converting '{}' into a State", s); logger.warn("An exception occurred while converting '{}' into a State", s);
return UnDefType.UNDEF; return UnDefType.UNDEF;

View File

@ -14,7 +14,6 @@ package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.*; 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.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
@ -48,8 +47,6 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID); FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -59,17 +56,17 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
switch (selector) { switch (selector) {
case SUPERCOOL: { case SUPERCOOL: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling"); result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
} }
break; break;
} }
case SUPERFREEZE: { case SUPERFREEZE: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperFreezing"); result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperFreezing");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperFreezing"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperFreezing");
} }
break; break;
} }
@ -80,7 +77,7 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -94,7 +91,7 @@ public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerCha
protected void onAppliancePropertyChanged(DeviceProperty dp) { protected void onAppliancePropertyChanged(DeviceProperty dp) {
super.onAppliancePropertyChanged(dp); super.onAppliancePropertyChanged(dp);
if (!dp.Name.equals(STATE_PROPERTY_NAME)) { if (!STATE_PROPERTY_NAME.equals(dp.Name)) {
return; return;
} }

View File

@ -14,7 +14,6 @@ package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.*; 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.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
@ -49,8 +48,6 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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); FridgeChannelSelector selector = (FridgeChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -60,15 +57,15 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
switch (selector) { switch (selector) {
case SUPERCOOL: { case SUPERCOOL: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling"); result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
} }
break; break;
} }
case START: { case START: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
} }
break; break;
} }
@ -81,7 +78,7 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -95,7 +92,7 @@ public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector>
protected void onAppliancePropertyChanged(DeviceProperty dp) { protected void onAppliancePropertyChanged(DeviceProperty dp) {
super.onAppliancePropertyChanged(dp); super.onAppliancePropertyChanged(dp);
if (!dp.Name.equals(STATE_PROPERTY_NAME)) { if (!STATE_PROPERTY_NAME.equals(dp.Name)) {
return; return;
} }

View File

@ -37,8 +37,6 @@ public enum HobChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
PLATES("plateNumbers", "plates", DecimalType.class, true), PLATES("plateNumbers", "plates", DecimalType.class, true),

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.miele.internal.handler; 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.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
@ -26,7 +28,7 @@ import org.openhab.core.types.Command;
public class HobHandler extends MieleApplianceHandler<HobChannelSelector> { public class HobHandler extends MieleApplianceHandler<HobChannelSelector> {
public HobHandler(Thing thing) { public HobHandler(Thing thing) {
super(thing, HobChannelSelector.class, "Hob"); super(thing, HobChannelSelector.class, MIELE_DEVICE_CLASS_HOB);
} }
@Override @Override

View File

@ -39,8 +39,6 @@ public enum HoodChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
VENTILATION("ventilationPower", "ventilation", DecimalType.class, false), VENTILATION("ventilationPower", "ventilation", DecimalType.class, false),

View File

@ -13,9 +13,8 @@
package org.openhab.binding.miele.internal.handler; 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.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.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -39,7 +38,7 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(HoodHandler.class); private final Logger logger = LoggerFactory.getLogger(HoodHandler.class);
public HoodHandler(Thing thing) { public HoodHandler(Thing thing) {
super(thing, HoodChannelSelector.class, "Hood"); super(thing, HoodChannelSelector.class, MIELE_DEVICE_CLASS_HOOD);
} }
@Override @Override
@ -48,8 +47,6 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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); HoodChannelSelector selector = (HoodChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -59,15 +56,15 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
switch (selector) { switch (selector) {
case LIGHT: { case LIGHT: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startLighting"); result = bridgeHandler.invokeOperation(applianceId, modelID, "startLighting");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopLighting"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stopLighting");
} }
break; break;
} }
case STOP: { case STOP: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
} }
break; break;
} }
@ -78,7 +75,7 @@ public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -20,8 +20,8 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull;
import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil; import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier; import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject; 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.DeviceMetaData;
@ -84,8 +84,13 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelectorText) public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelectorText)
throws IllegalArgumentException { throws IllegalArgumentException {
for (ApplianceChannelSelector c : selectorType.getEnumConstants()) { E[] enumConstants = selectorType.getEnumConstants();
if (c.getChannelID() != null && c.getChannelID().equals(valueSelectorText)) { 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; return c;
} }
} }
@ -95,8 +100,13 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
public ApplianceChannelSelector getValueSelectorFromMieleID(String valueSelectorText) public ApplianceChannelSelector getValueSelectorFromMieleID(String valueSelectorText)
throws IllegalArgumentException { throws IllegalArgumentException {
for (ApplianceChannelSelector c : selectorType.getEnumConstants()) { E[] enumConstants = selectorType.getEnumConstants();
if (c.getMieleID() != null && c.getMieleID().equals(valueSelectorText)) { 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; return c;
} }
} }
@ -110,16 +120,14 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
if (applianceId != null) { if (applianceId != null) {
this.applianceId = applianceId; this.applianceId = applianceId;
if (getMieleBridgeHandler() != null) { this.onBridgeConnectionResumed();
ThingStatusInfo statusInfo = getBridge().getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
}
} }
} }
public void onBridgeConnectionResumed() { public void onBridgeConnectionResumed() {
if (getMieleBridgeHandler() != null) { Bridge bridge = getBridge();
ThingStatusInfo statusInfo = getBridge().getStatusInfo(); if (bridge != null && getMieleBridgeHandler() != null) {
ThingStatusInfo statusInfo = bridge.getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription()); updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
} }
} }
@ -149,43 +157,32 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier,
DeviceClassObject dco) { DeviceClassObject dco) {
String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
String modelID = StringUtils.right(dco.DeviceClass, if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) {
dco.DeviceClass.length() - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length()); return;
}
if (myApplianceId.equals(applicationIdentifier.getApplianceId())) {
if (modelID.equals(this.modelID)) {
for (JsonElement prop : dco.Properties.getAsJsonArray()) { for (JsonElement prop : dco.Properties.getAsJsonArray()) {
try { try {
DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class); DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class);
if (!dp.Name.equals(EXTENDED_DEVICE_STATE_PROPERTY_NAME)) { if (dp == null) {
dp.Value = StringUtils.trim(dp.Value); continue;
dp.Value = StringUtils.strip(dp.Value); }
if (!EXTENDED_DEVICE_STATE_PROPERTY_NAME.equals(dp.Name)) {
dp.Value = dp.Value.trim();
dp.Value = dp.Value.strip();
} }
onAppliancePropertyChanged(applicationIdentifier, dp); onAppliancePropertyChanged(applicationIdentifier, dp);
} catch (Exception p) { } catch (Exception p) {
// Ignore - this is due to an unrecognized and not yet reverse-engineered array property // 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)) {
return;
}
this.onAppliancePropertyChanged(dp);
}
@Override @Override
public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) { public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) {
String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID); String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
if (!myApplianceId.equals(applicationIdentifier.getApplianceId())) { if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) {
return; return;
} }
@ -202,14 +199,16 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
// only keep the enum, if any - that's all we care for events we receive via multicast // only keep the enum, if any - that's all we care for events we receive via multicast
// all other fields are nulled // all other fields are nulled
if (dmd != null) {
dmd.LocalizedID = null; dmd.LocalizedID = null;
dmd.LocalizedValue = null; dmd.LocalizedValue = null;
dmd.Filter = null; dmd.Filter = null;
dmd.description = null; dmd.description = null;
} }
} }
}
if (dp.Metadata != 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); JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class); dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata); metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata);
@ -217,9 +216,9 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
if (dp.Name.equals(EXTENDED_DEVICE_STATE_PROPERTY_NAME)) { if (dp.Name.equals(EXTENDED_DEVICE_STATE_PROPERTY_NAME)) {
if (!dp.Value.isEmpty()) { if (!dp.Value.isEmpty()) {
byte[] extendedStateBytes = ExtendedDeviceStateUtil.stringToBytes(dp.Value); byte[] extendedStateBytes = DeviceUtil.stringToBytes(dp.Value);
logger.trace("Extended device state for {}: {}", getThing().getUID(), logger.trace("Extended device state for {}: {}", getThing().getUID(),
ExtendedDeviceStateUtil.bytesToHex(extendedStateBytes)); DeviceUtil.bytesToHex(extendedStateBytes));
if (this instanceof ExtendedDeviceStateListener) { if (this instanceof ExtendedDeviceStateListener) {
((ExtendedDeviceStateListener) this).onApplianceExtendedStateChanged(extendedStateBytes); ((ExtendedDeviceStateListener) this).onApplianceExtendedStateChanged(extendedStateBytes);
} }
@ -234,7 +233,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
logger.trace("{} is not a valid channel for a {}", dp.Name, modelID); 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 != null) {
if (!selector.isProperty()) { if (!selector.isProperty()) {
@ -248,10 +247,11 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
} else { } else {
updateState(theChannelUID, UnDefType.UNDEF); updateState(theChannelUID, UnDefType.UNDEF);
} }
} else if (dpValue != null) { } else {
logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(), logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(),
getThing().getUID(), selector.getState(dpValue, dmd).toString()); getThing().getUID(), selector.getState(dpValue, dmd).toString());
Map<String, String> properties = editProperties(); @NonNull
Map<@NonNull String, @NonNull String> properties = editProperties();
properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString()); properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString());
updateProperties(properties); updateProperties(properties);
} }
@ -317,9 +317,19 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier(); FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
if (applianceId.equals(applianceIdentifier.getApplianceId())) { if (applianceId.equals(applianceIdentifier.getApplianceId())) {
Map<String, String> properties = editProperties(); @NonNull
properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol()); 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()); properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber());
String connectionType = appliance.getConnectionType();
if (connectionType != null) {
properties.put(CONNECTION_TYPE_PROPERTY_NAME, connectionType);
}
updateProperties(properties); updateProperties(properties);
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
@ -343,6 +353,9 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
} }
protected boolean isResultProcessable(JsonElement result) { protected boolean isResultProcessable(JsonElement result) {
return result != null && !result.isJsonNull(); if (result == null) {
throw new IllegalArgumentException("Provided result is null");
}
return !result.isJsonNull();
} }
} }

View File

@ -31,10 +31,13 @@ import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -44,7 +47,7 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream; 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.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.common.NamedThreadFactory; import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
@ -76,7 +79,10 @@ import com.google.gson.JsonParser;
**/ **/
public class MieleBridgeHandler extends BaseBridgeHandler { public class MieleBridgeHandler extends BaseBridgeHandler {
public static final Set<ThingTypeUID> 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 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])$"); .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 ExecutorService executor;
protected Future<?> eventListenerJob; protected Future<?> eventListenerJob;
protected List<HomeDevice> previousHomeDevices = new CopyOnWriteArrayList<>(); @NonNull
protected Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<String, HomeDevice>();
protected Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<String, HomeDevice>();
protected URL url; protected URL url;
protected Map<String, String> headers; protected Map<String, String> headers;
@ -105,6 +113,8 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
// Data structures to de-JSONify whatever Miele appliances are sending us // Data structures to de-JSONify whatever Miele appliances are sending us
public class HomeDevice { public class HomeDevice {
private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance";
public String Name; public String Name;
public String Status; public String Status;
public String ParentUID; public String ParentUID;
@ -125,9 +135,49 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
return new FullyQualifiedApplianceIdentifier(this.UID); return new FullyQualifiedApplianceIdentifier(this.UID);
} }
@NonNull
public String getSerialNumber() { public String getSerialNumber() {
return Properties.get("serial.number").getAsString(); 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 { public class DeviceClassObject {
@ -206,7 +256,11 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
private Runnable pollingRunnable = new Runnable() { private Runnable pollingRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) { if (!IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) {
logger.debug("Invalid IP address for the Miele@Home gateway : '{}'", getConfig().get(HOST));
return;
}
try { try {
if (isReachable((String) getConfig().get(HOST))) { if (isReachable((String) getConfig().get(HOST))) {
currentBridgeConnectionState = true; currentBridgeConnectionState = true;
@ -222,50 +276,52 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
onConnectionResumed(); onConnectionResumed();
} }
if (currentBridgeConnectionState) { if (!currentBridgeConnectionState || getThing().getStatus() != ThingStatus.ONLINE) {
if (getThing().getStatus() == ThingStatus.ONLINE) { return;
List<HomeDevice> currentHomeDevices = getHomeDevices();
for (HomeDevice hd : currentHomeDevices) {
boolean isExisting = false;
for (HomeDevice phd : previousHomeDevices) {
if (phd.UID.equals(hd.UID)) {
isExisting = true;
break;
} }
}
if (!isExisting) { List<HomeDevice> homeDevices = getHomeDevices();
for (HomeDevice hd : homeDevices) {
String key = hd.getApplianceIdentifier().getApplianceId();
if (!cachedHomeDevicesByApplianceId.containsKey(key)) {
logger.debug("A new appliance with ID '{}' has been added", hd.UID); logger.debug("A new appliance with ID '{}' has been added", hd.UID);
for (ApplianceStatusListener listener : applianceStatusListeners) { for (ApplianceStatusListener listener : applianceStatusListeners) {
listener.onApplianceAdded(hd); listener.onApplianceAdded(hd);
} }
} }
cachedHomeDevicesByApplianceId.put(key, hd);
cachedHomeDevicesByRemoteUid.put(hd.getRemoteUid(), hd);
} }
for (HomeDevice hd : previousHomeDevices) { @NonNull
boolean isCurrent = false; Set<@NonNull Entry<String, HomeDevice>> cachedEntries = cachedHomeDevicesByApplianceId.entrySet();
for (HomeDevice chd : currentHomeDevices) { @NonNull
if (chd.UID.equals(hd.UID)) { Iterator<@NonNull Entry<String, HomeDevice>> iterator = cachedEntries.iterator();
isCurrent = true;
break; while (iterator.hasNext()) {
} Entry<String, HomeDevice> cachedEntry = iterator.next();
} HomeDevice cachedHomeDevice = cachedEntry.getValue();
if (!isCurrent) { if (!homeDevices.stream().anyMatch(d -> d.UID.equals(cachedHomeDevice.UID))) {
logger.debug("The appliance with ID '{}' has been removed", hd); logger.debug("The appliance with ID '{}' has been removed", cachedHomeDevice.UID);
for (ApplianceStatusListener listener : applianceStatusListeners) { for (ApplianceStatusListener listener : applianceStatusListeners) {
listener.onApplianceRemoved(hd); listener.onApplianceRemoved(cachedHomeDevice);
}
cachedHomeDevicesByRemoteUid.remove(cachedHomeDevice.getRemoteUid());
iterator.remove();
} }
} }
}
previousHomeDevices = currentHomeDevices;
for (Thing appliance : getThing().getThings()) { for (Thing appliance : getThing().getThings()) {
if (appliance.getStatus() == ThingStatus.ONLINE) { if (appliance.getStatus() == ThingStatus.ONLINE) {
String applianceId = (String) appliance.getConfiguration().getProperties() String applianceId = (String) appliance.getConfiguration().getProperties().get(APPLIANCE_ID);
.get(APPLIANCE_ID); FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId(
String protocol = appliance.getProperties().get(PROTOCOL_PROPERTY_NAME); applianceId);
var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId,
protocol); if (applianceIdentifier == null) {
logger.error("The appliance with ID '{}' was not found in appliance list from bridge.",
applianceId);
continue;
}
Object[] args = new Object[2]; Object[] args = new Object[2];
args[0] = applianceIdentifier.getUid(); args[0] = applianceIdentifier.getUid();
@ -277,6 +333,11 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
try { try {
DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class); DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
// Skip com.prosyst.mbs.services.zigbee.hdm.deviceclasses.ReportingControl
if (dco == null || !dco.DeviceClass.startsWith(MIELE_CLASS)) {
continue;
}
for (ApplianceStatusListener listener : applianceStatusListeners) { for (ApplianceStatusListener listener : applianceStatusListeners) {
listener.onApplianceStateChanged(applianceIdentifier, dco); listener.onApplianceStateChanged(applianceIdentifier, dco);
} }
@ -288,14 +349,9 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
} }
} }
} }
}
}
} catch (Exception e) { } catch (Exception e) {
logger.debug("An exception occurred while polling an appliance :'{}'", e.getMessage()); 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));
}
} }
private boolean isReachable(String ipAddress) { private boolean isReachable(String ipAddress) {
@ -339,6 +395,15 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
return devices; return devices;
} }
private FullyQualifiedApplianceIdentifier getApplianceIdentifierFromApplianceId(String applianceId) {
HomeDevice homeDevice = this.cachedHomeDevicesByApplianceId.get(applianceId);
if (homeDevice == null) {
return null;
}
return homeDevice.getApplianceIdentifier();
}
private Runnable eventListenerRunnable = () -> { private Runnable eventListenerRunnable = () -> {
if (IP_PATTERN.matcher((String) getConfig().get(INTERFACE)).matches()) { if (IP_PATTERN.matcher((String) getConfig().get(INTERFACE)).matches()) {
while (true) { while (true) {
@ -378,16 +443,16 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
DeviceProperty dp = new DeviceProperty(); DeviceProperty dp = new DeviceProperty();
String id = null; String id = null;
String[] parts = StringUtils.split(event, "&"); String[] parts = event.split("&");
for (String p : parts) { for (String p : parts) {
String[] subparts = StringUtils.split(p, "="); String[] subparts = p.split("=");
switch (subparts[0]) { switch (subparts[0]) {
case "property": { case "property": {
dp.Name = subparts[1]; dp.Name = subparts[1];
break; break;
} }
case "value": { case "value": {
dp.Value = StringUtils.trim(StringUtils.strip(subparts[1])); dp.Value = subparts[1].strip().trim();
break; break;
} }
case "id": { case "id": {
@ -403,15 +468,19 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
// In XGW 3000 firmware 2.03 this was changed from UID (hdm:ZigBee:0123456789abcdef#210) // In XGW 3000 firmware 2.03 this was changed from UID (hdm:ZigBee:0123456789abcdef#210)
// to serial number (001234567890) // to serial number (001234567890)
FullyQualifiedApplianceIdentifier applianceIdentifier;
if (id.startsWith("hdm:")) { if (id.startsWith("hdm:")) {
for (ApplianceStatusListener listener : applianceStatusListeners) { applianceIdentifier = new FullyQualifiedApplianceIdentifier(id);
listener.onAppliancePropertyChanged(new FullyQualifiedApplianceIdentifier(id),
dp);
}
} else { } else {
for (ApplianceStatusListener listener : applianceStatusListeners) { HomeDevice device = cachedHomeDevicesByRemoteUid.get(id);
listener.onAppliancePropertyChanged(id, dp); 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) { } catch (SocketTimeoutException e) {
try { try {
@ -445,19 +514,27 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
} }
}; };
public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID, public JsonElement invokeOperation(String applianceId, String modelID, String methodName) {
String methodName) { if (getThing().getStatus() != ThingStatus.ONLINE) {
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 {
logger.debug("The Bridge is offline - operations can not be invoked."); logger.debug("The Bridge is offline - operations can not be invoked.");
return null; 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) { protected JsonElement invokeRPC(String methodName, Object[] args) {

View File

@ -22,7 +22,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TimeZone; 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.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
@ -49,8 +49,6 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false), PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false),
@ -167,7 +165,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
private final Logger logger = LoggerFactory.getLogger(OvenChannelSelector.class); private final Logger logger = LoggerFactory.getLogger(OvenChannelSelector.class);
private final static Map<String, String> phases = Map.ofEntries(entry("1", "Heating"), entry("2", "Temp. hold"), private static final Map<String, String> 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("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")); entry("10", "Defrost"), entry("11", "Cooling down"), entry("12", "Energy save phase"));
@ -241,7 +239,7 @@ public enum OvenChannelSelector implements ApplianceChannelSelector {
public State getTemperatureState(String s) { public State getTemperatureState(String s) {
try { try {
return ExtendedDeviceStateUtil.getTemperatureState(s); return DeviceUtil.getTemperatureState(s);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.warn("An exception occurred while converting '{}' into a State", s); logger.warn("An exception occurred while converting '{}' into a State", s);
return UnDefType.UNDEF; return UnDefType.UNDEF;

View File

@ -13,9 +13,8 @@
package org.openhab.binding.miele.internal.handler; 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.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.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -40,7 +39,7 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(OvenHandler.class); private final Logger logger = LoggerFactory.getLogger(OvenHandler.class);
public OvenHandler(Thing thing) { public OvenHandler(Thing thing) {
super(thing, OvenChannelSelector.class, "Oven"); super(thing, OvenChannelSelector.class, MIELE_DEVICE_CLASS_OVEN);
} }
@Override @Override
@ -49,8 +48,6 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID); OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -60,15 +57,15 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
switch (selector) { switch (selector) {
case SWITCH: { case SWITCH: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn"); result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff"); result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
} }
break; break;
} }
case STOP: { case STOP: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
} }
break; break;
} }
@ -81,7 +78,7 @@ public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -47,8 +47,6 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", 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_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) { PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
@ -155,7 +153,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
private final Logger logger = LoggerFactory.getLogger(TumbleDryerChannelSelector.class); private final Logger logger = LoggerFactory.getLogger(TumbleDryerChannelSelector.class);
private final static Map<String, String> programs = Map.ofEntries(entry("10", "Automatic Plus"), private static final Map<String, String> programs = Map.ofEntries(entry("10", "Automatic Plus"),
entry("23", "Cottons hygiene"), entry("30", "Minimum iron"), entry("31", "Gentle minimum iron"), 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("40", "Woollens handcare"), entry("50", "Delicates"), entry("60", "Warm Air"),
entry("70", "Cool air"), entry("80", "Express"), entry("90", "Cottons"), entry("100", "Gentle smoothing"), entry("70", "Cool air"), entry("80", "Express"), entry("90", "Cottons"), entry("100", "Gentle smoothing"),
@ -165,7 +163,7 @@ public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
entry("240", "Smoothing"), entry("65000", "Cottons, auto load control"), entry("240", "Smoothing"), entry("65000", "Cottons, auto load control"),
entry("65001", "Minimum iron, auto load control")); entry("65001", "Minimum iron, auto load control"));
private final static Map<String, String> phases = Map.ofEntries(entry("1", "Programme running"), private static final Map<String, String> phases = Map.ofEntries(entry("1", "Programme running"),
entry("2", "Drying"), entry("3", "Drying Machine iron"), entry("4", "Drying Hand iron"), 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("5", "Drying Normal"), entry("6", "Drying Normal+"), entry("7", "Cooling down"),
entry("8", "Drying Hand iron"), entry("10", "Finished")); entry("8", "Drying Hand iron"), entry("10", "Finished"));

View File

@ -13,9 +13,8 @@
package org.openhab.binding.miele.internal.handler; 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.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.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -40,7 +39,7 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class); private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class);
public TumbleDryerHandler(Thing thing) { public TumbleDryerHandler(Thing thing) {
super(thing, TumbleDryerChannelSelector.class, "TumbleDryer"); super(thing, TumbleDryerChannelSelector.class, MIELE_DEVICE_CLASS_TUMBLE_DRYER);
} }
@Override @Override
@ -49,8 +48,6 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
TumbleDryerChannelSelector selector = (TumbleDryerChannelSelector) getValueSelectorFromChannelID(channelID); TumbleDryerChannelSelector selector = (TumbleDryerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null; JsonElement result = null;
@ -60,9 +57,9 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
switch (selector) { switch (selector) {
case SWITCH: { case SWITCH: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
} }
break; break;
} }
@ -75,7 +72,7 @@ public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannel
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -22,8 +22,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils; import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.ExtendedDeviceStateUtil;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData; import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
@ -50,8 +49,6 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false), PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false), DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
BRAND_ID("brandId", "brandId", StringType.class, true, false),
COMPANY_ID("companyId", "companyId", StringType.class, true, false),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false), STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false),
STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false), STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) { PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
@ -98,7 +95,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0")); dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
try { try {
date.setTime(Long.valueOf(StringUtils.trim(s)) * 60000); date.setTime(Long.valueOf(s.trim()) * 60000);
} catch (Exception e) { } catch (Exception e) {
date.setTime(0); date.setTime(0);
} }
@ -174,7 +171,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
private final Logger logger = LoggerFactory.getLogger(WashingMachineChannelSelector.class); private final Logger logger = LoggerFactory.getLogger(WashingMachineChannelSelector.class);
private final static Map<String, String> programs = Map.ofEntries(entry("1", "Cottons"), entry("3", "Minimum iron"), private static final Map<String, String> programs = Map.ofEntries(entry("1", "Cottons"), entry("3", "Minimum iron"),
entry("4", "Delicates"), entry("8", "Woollens"), entry("9", "Silks"), entry("17", "Starch"), 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("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"), entry("24", "Denim"), entry("27", "Proofing"), entry("29", "Sportswear"), entry("31", "Automatic Plus"),
@ -183,7 +180,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
entry("95", "Down duvets"), entry("122", "Express 20"), entry("129", "Down filled items"), entry("95", "Down duvets"), entry("122", "Express 20"), entry("129", "Down filled items"),
entry("133", "Cottons Eco"), entry("146", "QuickPowerWash"), entry("65532", "Mix")); entry("133", "Cottons Eco"), entry("146", "QuickPowerWash"), entry("65532", "Mix"));
private final static Map<String, String> phases = Map.ofEntries(entry("1", "Pre-wash"), entry("4", "Washing"), private static final Map<String, String> phases = Map.ofEntries(entry("1", "Pre-wash"), entry("4", "Washing"),
entry("5", "Rinses"), entry("7", "Clean"), entry("9", "Drain"), entry("10", "Spin"), entry("5", "Rinses"), entry("7", "Clean"), entry("9", "Drain"), entry("10", "Spin"),
entry("11", "Anti-crease"), entry("12", "Finished")); entry("11", "Anti-crease"), entry("12", "Finished"));
@ -260,7 +257,7 @@ public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
public State getTemperatureState(String s) { public State getTemperatureState(String s) {
try { try {
return ExtendedDeviceStateUtil.getTemperatureState(s); return DeviceUtil.getTemperatureState(s);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.warn("An exception occurred while converting '{}' into a State", s); logger.warn("An exception occurred while converting '{}' into a State", s);
return UnDefType.UNDEF; return UnDefType.UNDEF;

View File

@ -13,13 +13,12 @@
package org.openhab.binding.miele.internal.handler; 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.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.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 static org.openhab.binding.miele.internal.MieleBindingConstants.WATER_CONSUMPTION_CHANNEL_ID;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
@ -51,7 +50,7 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
private final Logger logger = LoggerFactory.getLogger(WashingMachineHandler.class); private final Logger logger = LoggerFactory.getLogger(WashingMachineHandler.class);
public WashingMachineHandler(Thing thing) { public WashingMachineHandler(Thing thing) {
super(thing, WashingMachineChannelSelector.class, "WashingMachine"); super(thing, WashingMachineChannelSelector.class, MIELE_DEVICE_CLASS_WASHING_MACHINE);
} }
@Override @Override
@ -60,8 +59,6 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
String channelID = channelUID.getId(); String channelID = channelUID.getId();
String applianceId = (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);
WashingMachineChannelSelector selector = (WashingMachineChannelSelector) getValueSelectorFromChannelID( WashingMachineChannelSelector selector = (WashingMachineChannelSelector) getValueSelectorFromChannelID(
channelID); channelID);
@ -72,9 +69,9 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
switch (selector) { switch (selector) {
case SWITCH: { case SWITCH: {
if (command.equals(OnOffType.ON)) { if (command.equals(OnOffType.ON)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start"); result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
} else if (command.equals(OnOffType.OFF)) { } else if (command.equals(OnOffType.OFF)) {
result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop"); result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
} }
break; break;
} }
@ -87,7 +84,7 @@ public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineC
} }
} }
// process result // process result
if (isResultProcessable(result)) { if (result != null && isResultProcessable(result)) {
logger.debug("Result of operation is {}", result.getAsString()); logger.debug("Result of operation is {}", result.getAsString());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:miele:appliance">
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>Unique identifier for specific appliance on the gateway.</description>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -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

View File

@ -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.

View File

@ -27,12 +27,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -32,13 +32,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -29,13 +29,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -38,13 +38,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -38,13 +38,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -23,12 +23,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies the appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -47,13 +47,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -32,13 +32,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>

View File

@ -38,12 +38,7 @@
<representation-property>uid</representation-property> <representation-property>uid</representation-property>
<config-description> <config-description-ref uri="thing-type:miele:appliance"/>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
</parameter>
</config-description>
</thing-type> </thing-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -7,7 +7,7 @@
<!-- Miele Bridge --> <!-- Miele Bridge -->
<bridge-type id="xgw3000"> <bridge-type id="xgw3000">
<label>Miele XGW3000</label> <label>Miele XGW3000</label>
<description>The miele bridge represents the Miele@home XGW3000 gateway.</description> <description>The Miele bridge represents the Miele@home XGW3000 gateway.</description>
<properties> <properties>
<property name="vendor">Miele</property> <property name="vendor">Miele</property>
@ -25,7 +25,7 @@
<context>network-address</context> <context>network-address</context>
<label>Network Address of the Multicast Interface</label> <label>Network Address of the Multicast Interface</label>
<description>Network address of openHAB host interface where the binding will listen for multicast events coming <description>Network address of openHAB host interface where the binding will listen for multicast events coming
from the Miele@home gateway</description> from the Miele@home gateway.</description>
</parameter> </parameter>
<parameter name="userName" type="text" required="false"> <parameter name="userName" type="text" required="false">
<label>Username</label> <label>Username</label>
@ -36,7 +36,7 @@
<parameter name="password" type="text" required="false"> <parameter name="password" type="text" required="false">
<context>password</context> <context>password</context>
<label>Password</label> <label>Password</label>
<description>Password for the registered Miele@home</description> <description>Password for the registered Miele@home user.</description>
</parameter> </parameter>
</config-description> </config-description>
</bridge-type> </bridge-type>

View File

@ -22,17 +22,16 @@ import org.openhab.core.types.UnDefType;
/** /**
* This class provides test cases for {@link * 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 @Test
public void bytesToHexWhenTopBitIsUsedReturnsCorrectString() { public void bytesToHexWhenTopBitIsUsedReturnsCorrectString() {
String actual = ExtendedDeviceStateUtil String actual = DeviceUtil.bytesToHex(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef });
.bytesToHex(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef });
assertEquals("DEADBEEF", actual); assertEquals("DEADBEEF", actual);
} }
@ -45,27 +44,27 @@ public class ExtendedDeviceStateUtilTest extends JavaTest {
@Test @Test
public void stringToBytesWhenTopBitIsUsedReturnsSingleByte() { public void stringToBytesWhenTopBitIsUsedReturnsSingleByte() {
byte[] expected = new byte[] { (byte) 0x00, (byte) 0x80, (byte) 0x00 }; 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); assertArrayEquals(expected, actual);
} }
@Test @Test
public void getTemperatureStateWellFormedValueReturnsQuantityType() throws NumberFormatException { public void getTemperatureStateWellFormedValueReturnsQuantityType() throws NumberFormatException {
assertEquals(new QuantityType<>(42, SIUnits.CELSIUS), ExtendedDeviceStateUtil.getTemperatureState("42")); assertEquals(new QuantityType<>(42, SIUnits.CELSIUS), DeviceUtil.getTemperatureState("42"));
} }
@Test @Test
public void getTemperatureStateMagicValueReturnsUndefined() throws NumberFormatException { public void getTemperatureStateMagicValueReturnsUndefined() throws NumberFormatException {
assertEquals(UnDefType.UNDEF, ExtendedDeviceStateUtil.getTemperatureState("32768")); assertEquals(UnDefType.UNDEF, DeviceUtil.getTemperatureState("32768"));
} }
@Test @Test
public void getTemperatureStateNonNumericValueThrowsNumberFormatException() { public void getTemperatureStateNonNumericValueThrowsNumberFormatException() {
assertThrows(NumberFormatException.class, () -> ExtendedDeviceStateUtil.getTemperatureState("A")); assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState("A"));
} }
@Test @Test
public void getTemperatureStateNullValueThrowsNumberFormatException() { public void getTemperatureStateNullValueThrowsNumberFormatException() {
assertThrows(NumberFormatException.class, () -> ExtendedDeviceStateUtil.getTemperatureState(null)); assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState(null));
} }
} }

View File

@ -21,7 +21,7 @@ import org.openhab.core.test.java.JavaTest;
* This class provides test cases for {@link * This class provides test cases for {@link
* org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier} * 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 { public class FullyQualifiedApplianceIdentifierTest extends JavaTest {