--- /dev/null
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal;
+
+/**
+ * The {@link FullyQualifiedApplianceIdentifier} class represents a fully qualified appliance identifier.
+ * Example: "hdm:ZigBee:0123456789abcdef#210"
+ *
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ */
+public class FullyQualifiedApplianceIdentifier {
+ private String uid;
+ private String protocol;
+ private String applianceId;
+
+ public FullyQualifiedApplianceIdentifier(String uid) {
+ this.uid = uid;
+
+ int separatorPosition = this.uid.lastIndexOf(':') + 1;
+ this.protocol = uid.substring(0, separatorPosition);
+ this.applianceId = uid.substring(separatorPosition);
+ }
+
+ public FullyQualifiedApplianceIdentifier(String applianceId, String protocol) {
+ this.uid = protocol + applianceId;
+ this.protocol = protocol;
+ this.applianceId = applianceId;
+ }
+
+ /**
+ * @return UID of appliance (e.g. "hdm:ZigBee:0123456789abcdef#210")
+ */
+ public String getUid() {
+ return this.uid;
+ }
+
+ /**
+ * @return Appliance ID without protocol adapter information (e.g. "0123456789abcdef#210")
+ */
+ public String getApplianceId() {
+ return this.applianceId;
+ }
+
+ public String getId() {
+ return this.getApplianceId().replaceAll("[^a-zA-Z0-9_]", "_");
+ }
+
+ /**
+ * @return Protocol prefix of fully qualified appliance identifier (e.g. "hdmi:ZigBee:"")
+ */
+ public String getProtocol() {
+ return this.protocol;
+ }
+}
*
* @author Karel Goderis - Initial contribution
* @author Martin Lepsy - added constants for support of WiFi devices & protocol
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
@NonNullByDefault
public class MieleBindingConstants {
public static final String BINDING_ID = "miele";
public static final String APPLIANCE_ID = "uid";
public static final String DEVICE_CLASS = "dc";
- public static final String HDM_LAN = "hdm:LAN:";
- public static final String HDM_ZIGBEE = "hdm:ZigBee:";
public static final String PROTOCOL_PROPERTY_NAME = "protocol";
+ public static final String SERIAL_NUMBER_PROPERTY_NAME = "serialNumber";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_XGW3000 = new ThingTypeUID(BINDING_ID, "xgw3000");
import java.util.Map;
import java.util.Set;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.handler.ApplianceStatusListener;
import org.openhab.binding.miele.internal.handler.MieleApplianceHandler;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler;
*
* @author Karel Goderis - Initial contribution
* @author Martin Lepsy - Added protocol information in order so support WiFi devices
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener {
ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID();
Map<String, Object> properties = new HashMap<>(2);
- properties.put(PROTOCOL_PROPERTY_NAME, appliance.getProtocol());
- properties.put(APPLIANCE_ID, appliance.getApplianceId());
+ FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
+ properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol());
+ properties.put(APPLIANCE_ID, applianceIdentifier.getApplianceId());
+ properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber());
for (JsonElement dc : appliance.DeviceClasses) {
String dcStr = dc.getAsString();
}
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
- .withBridge(bridgeUID).withLabel((String) properties.get(DEVICE_CLASS)).build();
+ .withBridge(bridgeUID).withLabel((String) properties.get(DEVICE_CLASS))
+ .withRepresentationProperty(APPLIANCE_ID).build();
thingDiscovered(discoveryResult);
} else {
}
@Override
- public void onApplianceStateChanged(String uid, DeviceClassObject dco) {
+ public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco) {
// nothing to do
}
@Override
- public void onAppliancePropertyChanged(String uid, DeviceProperty dp) {
+ public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp) {
+ // nothing to do
+ }
+
+ @Override
+ public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) {
// nothing to do
}
modelID.replaceAll("[^a-zA-Z0-9_]", "_").toLowerCase());
if (getSupportedThingTypes().contains(thingTypeUID)) {
- ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getId());
+ ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getApplianceIdentifier().getId());
return thingUID;
} else {
return null;
*
* @author Karel Goderis - Initial contribution
* @author Martin Lepsy - Added check for Miele gateway for cleaner discovery
- *
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
@Component
public class MieleMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant {
}
return DiscoveryResultBuilder.create(uid).withProperties(properties)
- .withRepresentationProperty(MieleBindingConstants.HOST).withLabel("Miele XGW3000 Gateway")
- .build();
+ .withRepresentationProperty(MieleBindingConstants.HOST).withLabel("Miele XGW3000").build();
}
}
return null;
*/
package org.openhab.binding.miele.internal.handler;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice;
* an appliance has been removed or added.
*
* @author Karel Goderis - Initial contribution
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public interface ApplianceStatusListener {
/**
* This method is called whenever the state of the given appliance has changed.
*
- * @param uid the UID of the aplliance that has changed
+ * @param applianceIdentifier the fully qualified identifier of the appliance that has changed
* @param dco the POJO containing the new state (properties and/or operations)
*/
- void onApplianceStateChanged(String uid, DeviceClassObject dco);
+ void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceClassObject dco);
/**
* This method is called whenever a "property" of the given appliance has changed.
*
- * @param uid the UID of the aplliance that has changed
+ * @param applianceIdentifier the fully qualified identifier of the appliance that has changed
* @param dco the POJO containing the new state of the property
*/
- void onAppliancePropertyChanged(String uid, DeviceProperty dp);
+ void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applianceIdentifier, DeviceProperty dp);
+
+ /**
+ * This method is called whenever a "property" of the given appliance has changed.
+ *
+ * @param serialNumber The serial number of the appliance that has changed
+ * @param dco the POJO containing the new state of the property
+ */
+ void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp);
/**
* This method us called whenever an appliance is removed.
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
*
* @author Stephan Esch - Initial contribution
* @author Martin Lepsy - fixed handling of empty JSON results
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineChannelSelector> {
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SWITCH: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "switchOn");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "switchOff");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSelector> {
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
DishwasherChannelSelector selector = (DishwasherChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SWITCH: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "start");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stop");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerChannelSelector> {
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SUPERCOOL: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "startSuperCooling");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperCooling");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling");
}
break;
}
case SUPERFREEZE: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "startSuperFreezing");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperFreezing");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperFreezing");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperFreezing");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
*
* @author Karel Goderis - Initial contribution
* @author Martin Lepsy - fixed handling of empty JSON results
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector> {
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
FridgeChannelSelector selector = (FridgeChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SUPERCOOL: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "startSuperCooling");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startSuperCooling");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperCooling");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopSuperCooling");
}
break;
}
case START: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "start");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
- */
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ **/
public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(HoodHandler.class);
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
HoodChannelSelector selector = (HoodChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case LIGHT: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "startLighting");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "startLighting");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stopLighting");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stopLighting");
}
break;
}
case STOP: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stop");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop");
}
break;
}
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
*
* @author Karel Goderis - Initial contribution
* @author Martin Lepsy - Added check for JsonNull result
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannelSelector> extends BaseThingHandler
implements ApplianceStatusListener {
protected Gson gson = new Gson();
- protected String uid;
+ protected String applianceId;
protected MieleBridgeHandler bridgeHandler;
private Class<E> selectorType;
protected String modelID;
@Override
public void initialize() {
logger.debug("Initializing Miele appliance handler.");
- final String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
- if (uid != null) {
- this.uid = uid;
+ final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId != null) {
+ this.applianceId = applianceId;
if (getMieleBridgeHandler() != null) {
ThingStatusInfo statusInfo = getBridge().getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
@Override
public void dispose() {
logger.debug("Handler disposes. Unregistering listener.");
- if (uid != null) {
+ if (applianceId != null) {
MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
if (bridgeHandler != null) {
getMieleBridgeHandler().unregisterApplianceStatusListener(this);
}
- uid = null;
+ applianceId = null;
}
}
}
@Override
- public void onApplianceStateChanged(String UID, DeviceClassObject dco) {
- String myUID = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier,
+ DeviceClassObject dco) {
+ String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
String modelID = StringUtils.right(dco.DeviceClass,
dco.DeviceClass.length() - new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length());
- if (myUID.equals(UID)) {
+ if (myApplianceId.equals(applicationIdentifier.getApplianceId())) {
if (modelID.equals(this.modelID)) {
for (JsonElement prop : dco.Properties.getAsJsonArray()) {
try {
dp.Value = StringUtils.trim(dp.Value);
dp.Value = StringUtils.strip(dp.Value);
- onAppliancePropertyChanged(UID, dp);
+ onAppliancePropertyChanged(applicationIdentifier, dp);
} catch (Exception p) {
// Ignore - this is due to an unrecognized and not yet reverse-engineered array property
}
}
@Override
- public void onAppliancePropertyChanged(String UID, DeviceProperty dp) {
- String myUID = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ public void onAppliancePropertyChanged(String serialNumber, DeviceProperty dp) {
+ String mySerialNumber = getThing().getProperties().get(SERIAL_NUMBER_PROPERTY_NAME);
+ if (!mySerialNumber.equals(serialNumber)) {
+ return;
+ }
- if (myUID.equals(UID)) {
- try {
- DeviceMetaData dmd = null;
- if (dp.Metadata == null) {
- String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim());
- if (metadata != null) {
- JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
- dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
- // only keep the enum, if any - that's all we care for events we receive via multicast
- // all other fields are nulled
- dmd.LocalizedID = null;
- dmd.LocalizedValue = null;
- dmd.Filter = null;
- dmd.description = null;
- }
- }
- if (dp.Metadata != null) {
- String metadata = StringUtils.replace(dp.Metadata.toString(), "enum", "MieleEnum");
+ this.onAppliancePropertyChanged(dp);
+ }
+
+ @Override
+ public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) {
+ String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+
+ if (!myApplianceId.equals(applicationIdentifier.getApplianceId())) {
+ return;
+ }
+
+ this.onAppliancePropertyChanged(dp);
+ }
+
+ private void onAppliancePropertyChanged(DeviceProperty dp) {
+ try {
+ DeviceMetaData dmd = null;
+ if (dp.Metadata == null) {
+ String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim());
+ if (metadata != null) {
JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
- metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata);
+ // only keep the enum, if any - that's all we care for events we receive via multicast
+ // all other fields are nulled
+ dmd.LocalizedID = null;
+ dmd.LocalizedValue = null;
+ dmd.Filter = null;
+ dmd.description = null;
}
+ }
+ if (dp.Metadata != null) {
+ String metadata = StringUtils.replace(dp.Metadata.toString(), "enum", "MieleEnum");
+ JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
+ dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
+ metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata);
+ }
- ApplianceChannelSelector selector = null;
- try {
- selector = getValueSelectorFromMieleID(dp.Name);
- } catch (Exception h) {
- logger.trace("{} is not a valid channel for a {}", dp.Name, modelID);
- }
+ ApplianceChannelSelector selector = null;
+ try {
+ selector = getValueSelectorFromMieleID(dp.Name);
+ } catch (Exception h) {
+ logger.trace("{} is not a valid channel for a {}", dp.Name, modelID);
+ }
- String dpValue = StringUtils.trim(StringUtils.strip(dp.Value));
-
- if (selector != null) {
- if (!selector.isProperty()) {
- ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID());
-
- if (dp.Value != null) {
- logger.trace("Update state of {} with getState '{}'", theChannelUID,
- selector.getState(dpValue, dmd));
- updateState(theChannelUID, selector.getState(dpValue, dmd));
- } else {
- updateState(theChannelUID, UnDefType.UNDEF);
- }
- } else if (dpValue != null) {
- logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(),
- getThing().getUID(), selector.getState(dpValue, dmd).toString());
- Map<String, String> properties = editProperties();
- properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString());
- updateProperties(properties);
+ String dpValue = StringUtils.trim(StringUtils.strip(dp.Value));
+
+ if (selector != null) {
+ if (!selector.isProperty()) {
+ ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID());
+
+ if (dp.Value != null) {
+ logger.trace("Update state of {} with getState '{}'", theChannelUID,
+ selector.getState(dpValue, dmd));
+ updateState(theChannelUID, selector.getState(dpValue, dmd));
+ } else {
+ updateState(theChannelUID, UnDefType.UNDEF);
}
+ } else if (dpValue != null) {
+ logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(),
+ getThing().getUID(), selector.getState(dpValue, dmd).toString());
+ Map<String, String> properties = editProperties();
+ properties.put(selector.getChannelID(), selector.getState(dpValue, dmd).toString());
+ updateProperties(properties);
}
- } catch (IllegalArgumentException e) {
- logger.error("An exception occurred while processing a changed device property :'{}'", e.getMessage());
}
+ } catch (IllegalArgumentException e) {
+ logger.error("An exception occurred while processing a changed device property :'{}'", e.getMessage());
}
}
@Override
public void onApplianceRemoved(HomeDevice appliance) {
- if (uid != null) {
- if (uid.equals(appliance.getApplianceId())) {
- updateStatus(ThingStatus.OFFLINE);
- }
+ if (applianceId == null) {
+ return;
+ }
+
+ if (applianceId.equals(appliance.getApplianceIdentifier().getApplianceId())) {
+ updateStatus(ThingStatus.OFFLINE);
}
}
@Override
public void onApplianceAdded(HomeDevice appliance) {
- if (uid != null) {
- if (uid.equals(appliance.getApplianceId())) {
- Map<String, String> properties = editProperties();
- properties.put(PROTOCOL_PROPERTY_NAME, appliance.getProtocol());
- updateProperties(properties);
+ if (applianceId == null) {
+ return;
+ }
- updateStatus(ThingStatus.ONLINE);
- }
+ FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
+
+ if (applianceId.equals(applianceIdentifier.getApplianceId())) {
+ Map<String, String> properties = editProperties();
+ properties.put(PROTOCOL_PROPERTY_NAME, applianceIdentifier.getProtocol());
+ properties.put(SERIAL_NUMBER_PROPERTY_NAME, appliance.getSerialNumber());
+ updateProperties(properties);
+ updateStatus(ThingStatus.ONLINE);
}
}
import java.util.zip.GZIPInputStream;
import org.apache.commons.lang3.StringUtils;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - Fixed lifecycle issues
* @author Martin Lepsy - Added protocol information to support WiFi devices & some refactoring for HomeDevice
- */
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ **/
public class MieleBridgeHandler extends BaseBridgeHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000);
// Data structures to de-JSONify whatever Miele appliances are sending us
public class HomeDevice {
- private static final String PROTOCOL_LAN = "LAN";
-
public String Name;
public String Status;
public String ParentUID;
HomeDevice() {
}
- public String getId() {
- return getApplianceId().replaceAll("[^a-zA-Z0-9_]", "_");
- }
-
- public String getProtocol() {
- return ProtocolAdapterName.equals(PROTOCOL_LAN) ? HDM_LAN : HDM_ZIGBEE;
+ public FullyQualifiedApplianceIdentifier getApplianceIdentifier() {
+ return new FullyQualifiedApplianceIdentifier(this.UID);
}
- public String getApplianceId() {
- return ProtocolAdapterName.equals(PROTOCOL_LAN) ? StringUtils.right(UID, UID.length() - HDM_LAN.length())
- : StringUtils.right(UID, UID.length() - HDM_ZIGBEE.length());
+ public String getSerialNumber() {
+ return Properties.get("serial.number").getAsString();
}
}
String applianceId = (String) appliance.getConfiguration().getProperties()
.get(APPLIANCE_ID);
String protocol = appliance.getProperties().get(PROTOCOL_PROPERTY_NAME);
- if (protocol == null) {
- logger.error("Protocol property is missing for {}", applianceId);
- continue;
- }
- String UID = protocol + applianceId;
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId,
+ protocol);
Object[] args = new Object[2];
- args[0] = UID;
+ args[0] = applianceIdentifier.getUid();
args[1] = true;
JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args);
DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
for (ApplianceStatusListener listener : applianceStatusListeners) {
- listener.onApplianceStateChanged(applianceId, dco);
+ listener.onApplianceStateChanged(applianceIdentifier, dco);
}
} catch (Exception e) {
logger.debug("An exception occurred while quering an appliance : '{}'",
packet.getPort());
DeviceProperty dp = new DeviceProperty();
- String uid = null;
+ String id = null;
String[] parts = StringUtils.split(event, "&");
for (String p : parts) {
break;
}
case "id": {
- uid = subparts[1];
+ id = subparts[1];
break;
}
}
}
- for (ApplianceStatusListener listener : applianceStatusListeners) {
- listener.onAppliancePropertyChanged(uid, dp);
+ if (id == null) {
+ continue;
+ }
+
+ // In XGW 3000 firmware 2.03 this was changed from UID (hdm:ZigBee:0123456789abcdef#210)
+ // to serial number (001234567890)
+ if (id.startsWith("hdm:")) {
+ for (ApplianceStatusListener listener : applianceStatusListeners) {
+ listener.onAppliancePropertyChanged(new FullyQualifiedApplianceIdentifier(id),
+ dp);
+ }
+ } else {
+ for (ApplianceStatusListener listener : applianceStatusListeners) {
+ listener.onAppliancePropertyChanged(id, dp);
+ }
}
} catch (SocketTimeoutException e) {
try {
}
};
- public JsonElement invokeOperation(String UID, String modelID, String methodName) {
- return invokeOperation(UID, modelID, methodName, HDM_ZIGBEE);
- }
-
- public JsonElement invokeOperation(String UID, String modelID, String methodName, String protocol) {
+ public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
+ String methodName) {
if (getThing().getStatus() == ThingStatus.ONLINE) {
Object[] args = new Object[4];
- args[0] = protocol + UID;
+ args[0] = applianceIdentifier.getUid();
args[1] = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele" + modelID;
args[2] = methodName;
args[3] = null;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
- String protocol = (String) getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SWITCH: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "switchOn", protocol);
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOn");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "switchOff", protocol);
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "switchOff");
}
break;
}
case STOP: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stop", protocol);
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
- */
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ **/
public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class);
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
TumbleDryerChannelSelector selector = (TumbleDryerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
switch (selector) {
case SWITCH: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "start");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stop");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop");
}
break;
}
package org.openhab.binding.miele.internal.handler;
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
+import static org.openhab.binding.miele.internal.MieleBindingConstants.PROTOCOL_PROPERTY_NAME;
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
* @author Martin Lepsy - fixed handling of empty JSON results
- */
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ **/
public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(WashingMachineHandler.class);
super.handleCommand(channelUID, command);
String channelID = channelUID.getId();
- String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String protocol = getThing().getProperties().get(PROTOCOL_PROPERTY_NAME);
+ var applianceIdentifier = new FullyQualifiedApplianceIdentifier(applianceId, protocol);
WashingMachineChannelSelector selector = (WashingMachineChannelSelector) getValueSelectorFromChannelID(
channelID);
switch (selector) {
case SWITCH: {
if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "start");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "start");
} else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(uid, modelID, "stop");
+ result = bridgeHandler.invokeOperation(applianceIdentifier, modelID, "stop");
}
break;
}
<channel-type id="stop" advanced="false">
<item-type>Switch</item-type>
<label>Stop</label>
- <description>Stop the hood</description>
+ <description>Stop the appliance</description>
</channel-type>
<channel-type id="step" advanced="true">
<channel id="switch" typeId="switch"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="switch" typeId="switch"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="start" typeId="switch"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="start" typeId="switch"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="plate6time" typeId="time"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="stop" typeId="stop"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="switch" typeId="switch"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="step" typeId="step"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<channel id="spinningspeed" typeId="spinningspeed"/>
</channels>
+ <representation-property>uid</representation-property>
+
<config-description>
<parameter name="uid" type="text" required="true">
<label>ID</label>
<property name="vendor">Miele</property>
</properties>
+ <representation-property>ipAddress</representation-property>
+
<config-description>
<parameter name="ipAddress" type="text" required="true">
<context>network-address</context>
--- /dev/null
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+import org.openhab.core.test.java.JavaTest;
+
+/**
+ * This class provides test cases for {@link
+ * org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier}
+ *
+ * @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
+ */
+public class FullyQualifiedApplianceIdentifierTest extends JavaTest {
+
+ @Test
+ public void getUidWhenConstructedFromUidReturnsUid() {
+ var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210");
+ assertEquals("hdm:ZigBee:0123456789abcdef#210", identifier.getUid());
+ }
+
+ @Test
+ public void getUidWhenConstructedFromApplianceIdAndProtocolReturnsUid() {
+ var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:");
+ assertEquals("hdm:LAN:0123456789abcdef#210", identifier.getUid());
+ }
+
+ @Test
+ public void getApplianceIdWhenConstructedFromUidReturnsApplianceId() {
+ var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210");
+ assertEquals("0123456789abcdef#210", identifier.getApplianceId());
+ }
+
+ @Test
+ public void getApplianceIdWhenConstructedFromApplianceIdAndProtocolReturnsApplianceId() {
+ var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:");
+ assertEquals("0123456789abcdef#210", identifier.getApplianceId());
+ }
+
+ @Test
+ public void getIdWhenConstructedFromUidReturnsProtocol() {
+ var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210");
+ assertEquals("0123456789abcdef_210", identifier.getId());
+ }
+
+ @Test
+ public void getIdWhenConstructedFromApplianceIdAndProtocolReturnsProtocol() {
+ var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:");
+ assertEquals("0123456789abcdef_210", identifier.getId());
+ }
+
+ @Test
+ public void getProtocolWhenConstructedFromUidReturnsProtocol() {
+ var identifier = new FullyQualifiedApplianceIdentifier("hdm:ZigBee:0123456789abcdef#210");
+ assertEquals("hdm:ZigBee:", identifier.getProtocol());
+ }
+
+ @Test
+ public void getProtocolWhenConstructedFromApplianceIdAndProtocolReturnsProtocol() {
+ var identifier = new FullyQualifiedApplianceIdentifier("0123456789abcdef#210", "hdm:LAN:");
+ assertEquals("hdm:LAN:", identifier.getProtocol());
+ }
+}