added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.miele-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-miele" description="Miele@home Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-upnp</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.miele/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link MieleBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Martin Lepsy - added constants for support of WiFi devices & protocol
|
||||
*/
|
||||
@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";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_XGW3000 = new ThingTypeUID(BINDING_ID, "xgw3000");
|
||||
public static final ThingTypeUID THING_TYPE_DISHWASHER = new ThingTypeUID(BINDING_ID, "dishwasher");
|
||||
public static final ThingTypeUID THING_TYPE_OVEN = new ThingTypeUID(BINDING_ID, "oven");
|
||||
public static final ThingTypeUID THING_TYPE_FRIDGE = new ThingTypeUID(BINDING_ID, "fridge");
|
||||
public static final ThingTypeUID THING_TYPE_DRYER = new ThingTypeUID(BINDING_ID, "tumbledryer");
|
||||
public static final ThingTypeUID THING_TYPE_HOB = new ThingTypeUID(BINDING_ID, "hob");
|
||||
public static final ThingTypeUID THING_TYPE_FRIDGEFREEZER = new ThingTypeUID(BINDING_ID, "fridgefreezer");
|
||||
public static final ThingTypeUID THING_TYPE_HOOD = new ThingTypeUID(BINDING_ID, "hood");
|
||||
public static final ThingTypeUID THING_TYPE_WASHINGMACHINE = new ThingTypeUID(BINDING_ID, "washingmachine");
|
||||
public static final ThingTypeUID THING_TYPE_COFFEEMACHINE = new ThingTypeUID(BINDING_ID, "coffeemachine");
|
||||
|
||||
// Bridge config properties
|
||||
public static final String HOST = "ipAddress";
|
||||
public static final String INTERFACE = "interface";
|
||||
public static final String USER_NAME = "userName";
|
||||
public static final String PASSWORD = "password";
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.openhab.binding.miele.internal.MieleBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService;
|
||||
import org.openhab.binding.miele.internal.handler.CoffeeMachineHandler;
|
||||
import org.openhab.binding.miele.internal.handler.DishWasherHandler;
|
||||
import org.openhab.binding.miele.internal.handler.FridgeFreezerHandler;
|
||||
import org.openhab.binding.miele.internal.handler.FridgeHandler;
|
||||
import org.openhab.binding.miele.internal.handler.HobHandler;
|
||||
import org.openhab.binding.miele.internal.handler.HoodHandler;
|
||||
import org.openhab.binding.miele.internal.handler.MieleApplianceHandler;
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler;
|
||||
import org.openhab.binding.miele.internal.handler.OvenHandler;
|
||||
import org.openhab.binding.miele.internal.handler.TumbleDryerHandler;
|
||||
import org.openhab.binding.miele.internal.handler.WashingMachineHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link MieleHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.miele")
|
||||
public class MieleHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.concat(MieleBridgeHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
MieleApplianceHandler.SUPPORTED_THING_TYPES.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID,
|
||||
ThingUID bridgeUID) {
|
||||
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
|
||||
ThingUID mieleBridgeUID = getBridgeThingUID(thingTypeUID, thingUID, configuration);
|
||||
return super.createThing(thingTypeUID, configuration, mieleBridgeUID, null);
|
||||
}
|
||||
if (MieleApplianceHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
|
||||
ThingUID mieleApplianceUID = getApplianceUID(thingTypeUID, thingUID, configuration, bridgeUID);
|
||||
return super.createThing(thingTypeUID, configuration, mieleApplianceUID, bridgeUID);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"The thing type " + thingTypeUID + " is not supported by the miele binding.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing);
|
||||
registerApplianceDiscoveryService(handler);
|
||||
return handler;
|
||||
} else if (MieleApplianceHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_HOOD)) {
|
||||
return new HoodHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_FRIDGEFREEZER)) {
|
||||
return new FridgeFreezerHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_FRIDGE)) {
|
||||
return new FridgeHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_OVEN)) {
|
||||
return new OvenHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_HOB)) {
|
||||
return new HobHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_WASHINGMACHINE)) {
|
||||
return new WashingMachineHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_DRYER)) {
|
||||
return new TumbleDryerHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_DISHWASHER)) {
|
||||
return new DishWasherHandler(thing);
|
||||
}
|
||||
if (thing.getThingTypeUID().equals(THING_TYPE_COFFEEMACHINE)) {
|
||||
return new CoffeeMachineHandler(thing);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration) {
|
||||
if (thingUID == null) {
|
||||
String hostID = (String) configuration.get(HOST);
|
||||
thingUID = new ThingUID(thingTypeUID, hostID);
|
||||
}
|
||||
return thingUID;
|
||||
}
|
||||
|
||||
private ThingUID getApplianceUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration,
|
||||
ThingUID bridgeUID) {
|
||||
String applianceId = (String) configuration.get(APPLIANCE_ID);
|
||||
|
||||
if (thingUID == null) {
|
||||
thingUID = new ThingUID(thingTypeUID, applianceId, bridgeUID.getId());
|
||||
}
|
||||
return thingUID;
|
||||
}
|
||||
|
||||
private synchronized void registerApplianceDiscoveryService(MieleBridgeHandler bridgeHandler) {
|
||||
MieleApplianceDiscoveryService discoveryService = new MieleApplianceDiscoveryService(bridgeHandler);
|
||||
discoveryService.activate();
|
||||
this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof MieleBridgeHandler) {
|
||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
// remove discovery service, if bridge handler is removed
|
||||
MieleApplianceDiscoveryService service = (MieleApplianceDiscoveryService) bundleContext
|
||||
.getService(serviceReg.getReference());
|
||||
serviceReg.unregister();
|
||||
if (service != null) {
|
||||
service.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.discovery;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.miele.internal.handler.ApplianceStatusListener;
|
||||
import org.openhab.binding.miele.internal.handler.MieleApplianceHandler;
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler;
|
||||
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;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link MieleApplianceDiscoveryService} tracks appliances that are
|
||||
* associated with the Miele@Home gateway
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Martin Lepsy - Added protocol information in order so support WiFi devices
|
||||
*/
|
||||
public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class);
|
||||
|
||||
private static final int SEARCH_TIME = 60;
|
||||
|
||||
private MieleBridgeHandler mieleBridgeHandler;
|
||||
|
||||
public MieleApplianceDiscoveryService(MieleBridgeHandler mieleBridgeHandler) {
|
||||
super(MieleApplianceHandler.SUPPORTED_THING_TYPES, SEARCH_TIME, false);
|
||||
this.mieleBridgeHandler = mieleBridgeHandler;
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
mieleBridgeHandler.registerApplianceStatusListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
removeOlderResults(new Date().getTime());
|
||||
mieleBridgeHandler.unregisterApplianceStatusListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return MieleApplianceHandler.SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScan() {
|
||||
List<HomeDevice> appliances = mieleBridgeHandler.getHomeDevices();
|
||||
if (appliances != null) {
|
||||
for (HomeDevice l : appliances) {
|
||||
onApplianceAddedInternal(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceAdded(HomeDevice appliance) {
|
||||
onApplianceAddedInternal(appliance);
|
||||
}
|
||||
|
||||
private void onApplianceAddedInternal(HomeDevice appliance) {
|
||||
ThingUID thingUID = getThingUID(appliance);
|
||||
if (thingUID != null) {
|
||||
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());
|
||||
|
||||
for (JsonElement dc : appliance.DeviceClasses) {
|
||||
if (dc.getAsString().contains("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele")
|
||||
&& !dc.getAsString().equals("com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance")) {
|
||||
properties.put(DEVICE_CLASS, StringUtils.right(dc.getAsString(), dc.getAsString().length()
|
||||
- new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withBridge(bridgeUID).withLabel((String) properties.get(DEVICE_CLASS)).build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
} else {
|
||||
logger.debug("Discovered an unsupported appliance of vendor '{}' with id {}", appliance.Vendor,
|
||||
appliance.UID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceRemoved(HomeDevice appliance) {
|
||||
ThingUID thingUID = getThingUID(appliance);
|
||||
|
||||
if (thingUID != null) {
|
||||
thingRemoved(thingUID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceStateChanged(String uid, DeviceClassObject dco) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppliancePropertyChanged(String uid, DeviceProperty dp) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
private ThingUID getThingUID(HomeDevice appliance) {
|
||||
ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID();
|
||||
String modelID = null;
|
||||
|
||||
for (JsonElement dc : appliance.DeviceClasses) {
|
||||
if (dc.getAsString().contains("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele")
|
||||
&& !dc.getAsString().equals("com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance")) {
|
||||
modelID = StringUtils.right(dc.getAsString(), dc.getAsString().length()
|
||||
- new String("com.miele.xgw3000.gateway.hdm.deviceclasses.Miele").length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (modelID != null) {
|
||||
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID,
|
||||
StringUtils.lowerCase(modelID.replaceAll("[^a-zA-Z0-9_]", "_")));
|
||||
|
||||
if (getSupportedThingTypes().contains(thingTypeUID)) {
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, appliance.getId());
|
||||
return thingUID;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jmdns.ServiceInfo;
|
||||
|
||||
import org.openhab.binding.miele.internal.MieleBindingConstants;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.config.discovery.mdns.internal.MDNSDiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MieleMDNSDiscoveryParticipant} is responsible for discovering Miele XGW3000 Gateways. It uses the central
|
||||
* {@link MDNSDiscoveryService}.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Martin Lepsy - Added check for Miele gateway for cleaner discovery
|
||||
*
|
||||
*/
|
||||
@Component(immediate = true)
|
||||
public class MieleMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MieleMDNSDiscoveryParticipant.class);
|
||||
private static final String PATH_TO_CHECK_FOR_XGW3000 = "/rest/";
|
||||
private static final String SERVICE_NAME = "mieleathome";
|
||||
private static final String PATH_PROPERTY_NAME = "path";
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.singleton(MieleBindingConstants.THING_TYPE_XGW3000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceType() {
|
||||
return "_mieleathome._tcp.local.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscoveryResult createResult(ServiceInfo service) {
|
||||
if (isMieleGateway(service)) {
|
||||
ThingUID uid = getThingUID(service);
|
||||
|
||||
if (uid != null) {
|
||||
Map<String, Object> properties = new HashMap<>(2);
|
||||
|
||||
InetAddress[] addresses = service.getInetAddresses();
|
||||
if (addresses.length > 0 && addresses[0] != null) {
|
||||
properties.put(MieleBindingConstants.HOST, addresses[0].getHostAddress());
|
||||
|
||||
Socket socket = null;
|
||||
try {
|
||||
socket = new Socket(addresses[0], 80);
|
||||
InetAddress ourAddress = socket.getLocalAddress();
|
||||
properties.put(MieleBindingConstants.INTERFACE, ourAddress.getHostAddress());
|
||||
} catch (IOException e) {
|
||||
logger.error("An exception occurred while connecting to the Miele Gateway : '{}'",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return DiscoveryResultBuilder.create(uid).withProperties(properties)
|
||||
.withRepresentationProperty(uid.getId()).withLabel("Miele XGW3000 Gateway").build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThingUID getThingUID(ServiceInfo service) {
|
||||
if (service.getType() != null) {
|
||||
if (service.getType().equals(getServiceType())) {
|
||||
logger.trace("Discovered a Miele@Home gateway thing with name '{}'", service.getName());
|
||||
return new ThingUID(MieleBindingConstants.THING_TYPE_XGW3000, service.getName().replace(" ", "_"));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if service is a Miele XGW3000 Gateway
|
||||
*
|
||||
* application must be mieleathome
|
||||
* must contain path with value /rest/
|
||||
*
|
||||
* @param service the service to check
|
||||
* @return true, if the discovered service is a Miele XGW3000 Gateway
|
||||
*/
|
||||
private boolean isMieleGateway(ServiceInfo service) {
|
||||
return service.getApplication().contains(SERVICE_NAME) && service.getPropertyString(PATH_PROPERTY_NAME) != null
|
||||
&& service.getPropertyString(PATH_PROPERTY_NAME).equalsIgnoreCase(PATH_TO_CHECK_FOR_XGW3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} class defines a common interface for
|
||||
* all the data structures used by appliance thing handlers. It is used to traverse
|
||||
* the channels that possibly exist for an appliance, and convert data
|
||||
* returned by the appliance to a ESH compatible State
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public interface ApplianceChannelSelector {
|
||||
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
/**
|
||||
* Returns the ESH ChannelID for the given datapoint
|
||||
*/
|
||||
String getChannelID();
|
||||
|
||||
/**
|
||||
* Returns the Miele defined ID for the given datapoint
|
||||
*/
|
||||
String getMieleID();
|
||||
|
||||
/**
|
||||
* Returns true if the given datapoint is to be considered as a Property
|
||||
* instead of a regular modifiable datapoint
|
||||
*/
|
||||
boolean isProperty();
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a State for the given string, taking into
|
||||
* account the metadata provided. The meta data is sent by
|
||||
* the Miele appliance and is used to decide the State type
|
||||
*
|
||||
* @param s - the value to be used to instantiate the State
|
||||
* @param dmd - the device meta data
|
||||
*/
|
||||
State getState(String s, DeviceMetaData dmd);
|
||||
|
||||
/**
|
||||
* Returns "compatible" Type for this datapoint
|
||||
*/
|
||||
Class<? extends Type> getTypeClass();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@link ApplianceStatusListener} is notified when an appliance status has changed or
|
||||
* an appliance has been removed or added.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
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 dco the POJO containing the new state (properties and/or operations)
|
||||
*/
|
||||
void onApplianceStateChanged(String uid, 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 dco the POJO containing the new state of the property
|
||||
*/
|
||||
void onAppliancePropertyChanged(String uid, DeviceProperty dp);
|
||||
|
||||
/**
|
||||
* This method us called whenever an appliance is removed.
|
||||
*
|
||||
* @param appliance The XGW homedevice definition of the appliance that was removed
|
||||
*/
|
||||
void onApplianceRemoved(HomeDevice appliance);
|
||||
|
||||
/**
|
||||
* This method us called whenever an appliance is added.
|
||||
*
|
||||
* @param appliance The XGW homedevice definition of the appliance that was removed
|
||||
*/
|
||||
void onApplianceAdded(HomeDevice appliance);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for coffee machines
|
||||
*
|
||||
* @author Stephan Esch - Initial contribution
|
||||
*/
|
||||
public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PROGRAMID("programId", "program", StringType.class, false),
|
||||
PROGRAMTYPE("programType", "type", StringType.class, false),
|
||||
PROGRAMPHASE("phase", "phase", StringType.class, false),
|
||||
// lightingStatus signalFailure signalInfo
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
SWITCH(null, "switch", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CoffeeMachineChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
CoffeeMachineChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass,
|
||||
boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link CoffeeMachineHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Stephan Esch - Initial contribution
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CoffeeMachineHandler.class);
|
||||
|
||||
public CoffeeMachineHandler(Thing thing) {
|
||||
super(thing, CoffeeMachineChannelSelector.class, "CoffeeSystem");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SWITCH: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "switchOn");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "switchOff");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link DishWasherHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DishWasherHandler.class);
|
||||
|
||||
public DishWasherHandler(Thing thing) {
|
||||
super(thing, DishwasherChannelSelector.class, "Dishwasher");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
DishwasherChannelSelector selector = (DishwasherChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SWITCH: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "start");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stop");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for dishwashers
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
|
||||
*/
|
||||
public enum DishwasherChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PROGRAMID("programId", "program", StringType.class, false),
|
||||
PROGRAMPHASE("phase", "phase", StringType.class, false),
|
||||
START_TIME("startTime", "start", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DURATION("duration", "duration", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
SWITCH(null, "switch", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DishwasherChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
DishwasherChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass,
|
||||
boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for fridges
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public enum FridgeChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
SUPERCOOL(null, "supercool", OnOffType.class, false),
|
||||
FRIDGECURRENTTEMP("currentTemperature", "current", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
FRIDGETARGETTEMP("targetTemperature", "target", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
START(null, "start", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FridgeChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
FridgeChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass, boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for fridges with
|
||||
* a freezer compartment
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
FREEZERSTATE("freezerState", "freezerstate", StringType.class, false),
|
||||
FRIDGESTATE("fridgeState", "fridgestate", StringType.class, false),
|
||||
SUPERCOOL(null, "supercool", OnOffType.class, false),
|
||||
SUPERFREEZE(null, "superfreeze", OnOffType.class, false),
|
||||
FREEZERCURRENTTEMP("freezerCurrentTemperature", "freezercurrent", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
FREEZERTARGETTEMP("freezerTargetTemperature", "freezertarget", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
FRIDGECURRENTTEMP("fridgeCurrentTemperature", "fridgecurrent", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
FRIDGETARGETTEMP("fridgeTargetTemperature", "fridgetarget", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
START(null, "start", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FridgeFreezerChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
FridgeFreezerChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass,
|
||||
boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link FridgeFreezerHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FridgeFreezerHandler.class);
|
||||
|
||||
public FridgeFreezerHandler(Thing thing) {
|
||||
super(thing, FridgeFreezerChannelSelector.class, "FridgeFreezer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SUPERCOOL: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "startSuperCooling");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperCooling");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SUPERFREEZE: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "startSuperFreezing");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperFreezing");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link FridgeHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FridgeHandler.class);
|
||||
|
||||
public FridgeHandler(Thing thing) {
|
||||
super(thing, FridgeChannelSelector.class, "Fridge");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
FridgeChannelSelector selector = (FridgeChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SUPERCOOL: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "startSuperCooling");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stopSuperCooling");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "start");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for hobs
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public enum HobChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PLATES("plateNumbers", "plates", DecimalType.class, true),
|
||||
PLATE1_POWER("plate1PowerStep", "plate1power", DecimalType.class, false),
|
||||
PLATE1_HEAT("plate1RemainingHeat", "plate1heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
// If there is remaining heat, the device metadata contains some informative string which can not be
|
||||
// converted into a DecimalType. We therefore ignore the metadata and return the device property value as a
|
||||
// State
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE1_TIME("plate1RemainingTime", "plate1time", StringType.class, false),
|
||||
PLATE2_POWER("plate2PowerStep", "plate2power", DecimalType.class, false),
|
||||
PLATE2_HEAT("plate2RemainingHeat", "plate2heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE2_TIME("plate2RemainingTime", "plate2time", StringType.class, false),
|
||||
PLATE3_POWER("plate3PowerStep", "plate3power", DecimalType.class, false),
|
||||
PLATE3_HEAT("plate3RemainingHeat", "plate3heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE3_TIME("plate3RemainingTime", "plate3time", StringType.class, false),
|
||||
PLATE4_POWER("plate4PowerStep", "plate4power", DecimalType.class, false),
|
||||
PLATE4_HEAT("plate4RemainingHeat", "plate4heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE4_TIME("plate4RemainingTime", "plate4time", StringType.class, false),
|
||||
PLATE5_POWER("plate5PowerStep", "plate5power", DecimalType.class, false),
|
||||
PLATE5_HEAT("plate5RemainingHeat", "plate5heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE5_TIME("plate5RemainingTime", "plate5time", StringType.class, false),
|
||||
PLATE6_POWER("plate6PowerStep", "plate6power", DecimalType.class, false),
|
||||
PLATE6_HEAT("plate6RemainingHeat", "plate6heat", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
PLATE6_TIME("plate6RemainingTime", "plate6time", StringType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HobChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
HobChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass, boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
|
||||
/**
|
||||
* The {@link HobHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
*/
|
||||
public class HobHandler extends MieleApplianceHandler<HobChannelSelector> {
|
||||
|
||||
public HobHandler(Thing thing) {
|
||||
super(thing, HobChannelSelector.class, "Hob");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for ventilation hoods
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*/
|
||||
public enum HoodChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
VENTILATION("ventilationPower", "ventilation", DecimalType.class, false),
|
||||
LIGHT("lightingStatus", "light", OnOffType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("ON");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("OFF");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
STOP(null, "stop", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HoodChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
HoodChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass, boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link HoodHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HoodHandler.class);
|
||||
|
||||
public HoodHandler(Thing thing) {
|
||||
super(thing, HoodChannelSelector.class, "Hood");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
HoodChannelSelector selector = (HoodChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case LIGHT: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "startLighting");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stopLighting");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stop");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
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.DeviceOperation;
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* The {@link MieleApplianceHandler} is an abstract class
|
||||
* responsible for handling commands, which are sent to one
|
||||
* of the channels of the appliance that understands/"talks"
|
||||
* the {@link ApplianceChannelSelector} datapoints
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Martin Lepsy - Added check for JsonNull result
|
||||
*/
|
||||
public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannelSelector> extends BaseThingHandler
|
||||
implements ApplianceStatusListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MieleApplianceHandler.class);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
|
||||
.of(THING_TYPE_DISHWASHER, THING_TYPE_OVEN, THING_TYPE_FRIDGE, THING_TYPE_DRYER, THING_TYPE_HOB,
|
||||
THING_TYPE_FRIDGEFREEZER, THING_TYPE_HOOD, THING_TYPE_WASHINGMACHINE, THING_TYPE_COFFEEMACHINE)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
protected Gson gson = new Gson();
|
||||
|
||||
protected String uid;
|
||||
protected MieleBridgeHandler bridgeHandler;
|
||||
private Class<E> selectorType;
|
||||
protected String modelID;
|
||||
|
||||
protected Map<String, String> metaDataCache = new HashMap<>();
|
||||
|
||||
public MieleApplianceHandler(Thing thing, Class<E> selectorType, String modelID) {
|
||||
super(thing);
|
||||
this.selectorType = selectorType;
|
||||
this.modelID = modelID;
|
||||
}
|
||||
|
||||
public ApplianceChannelSelector getValueSelectorFromChannelID(String valueSelectorText)
|
||||
throws IllegalArgumentException {
|
||||
for (ApplianceChannelSelector c : selectorType.getEnumConstants()) {
|
||||
if (c.getChannelID() != null && c.getChannelID().equals(valueSelectorText)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not valid value selector");
|
||||
}
|
||||
|
||||
public ApplianceChannelSelector getValueSelectorFromMieleID(String valueSelectorText)
|
||||
throws IllegalArgumentException {
|
||||
for (ApplianceChannelSelector c : selectorType.getEnumConstants()) {
|
||||
if (c.getMieleID() != null && c.getMieleID().equals(valueSelectorText)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not valid value selector");
|
||||
}
|
||||
|
||||
@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;
|
||||
if (getMieleBridgeHandler() != null) {
|
||||
ThingStatusInfo statusInfo = getBridge().getStatusInfo();
|
||||
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBridgeConnectionResumed() {
|
||||
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) {
|
||||
MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
getMieleBridgeHandler().unregisterApplianceStatusListener(this);
|
||||
}
|
||||
uid = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Here we could handle commands that are common to all Miele Appliances, but so far I don't know of any
|
||||
if (command instanceof RefreshType) {
|
||||
// Placeholder for future refinement
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceStateChanged(String UID, DeviceClassObject dco) {
|
||||
String myUID = ((String) getThing().getProperties().get(PROTOCOL_PROPERTY_NAME))
|
||||
+ (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 (modelID.equals(this.modelID)) {
|
||||
for (JsonElement prop : dco.Properties.getAsJsonArray()) {
|
||||
try {
|
||||
DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class);
|
||||
dp.Value = StringUtils.trim(dp.Value);
|
||||
dp.Value = StringUtils.strip(dp.Value);
|
||||
|
||||
onAppliancePropertyChanged(UID, dp);
|
||||
} catch (Exception p) {
|
||||
// Ignore - this is due to an unrecognized and not yet reverse-engineered array property
|
||||
}
|
||||
}
|
||||
|
||||
for (JsonElement operation : dco.Operations.getAsJsonArray()) {
|
||||
try {
|
||||
DeviceOperation devop = gson.fromJson(operation, DeviceOperation.class);
|
||||
DeviceMetaData pmd = gson.fromJson(devop.Metadata, DeviceMetaData.class);
|
||||
} 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().getProperties().get(PROTOCOL_PROPERTY_NAME))
|
||||
+ (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
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) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject jsonMetaData = (JsonObject) parser.parse(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");
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject jsonMetaData = (JsonObject) parser.parse(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);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceRemoved(HomeDevice appliance) {
|
||||
if (uid != null) {
|
||||
if (uid.equals(appliance.UID)) {
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplianceAdded(HomeDevice appliance) {
|
||||
if (uid != null) {
|
||||
if (uid.equals(appliance.UID)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized MieleBridgeHandler getMieleBridgeHandler() {
|
||||
if (this.bridgeHandler == null) {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
return null;
|
||||
}
|
||||
ThingHandler handler = bridge.getHandler();
|
||||
if (handler instanceof MieleBridgeHandler) {
|
||||
this.bridgeHandler = (MieleBridgeHandler) handler;
|
||||
this.bridgeHandler.registerApplianceStatusListener(this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this.bridgeHandler;
|
||||
}
|
||||
|
||||
protected boolean isResultProcessable(JsonElement result) {
|
||||
return result != null && !result.isJsonNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringReader;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.MulticastSocket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* The {@link MieleBridgeHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public class MieleBridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000);
|
||||
|
||||
private static final Pattern IP_PATTERN = Pattern
|
||||
.compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
|
||||
|
||||
protected static final int POLLING_PERIOD = 15; // in seconds
|
||||
protected static final int JSON_RPC_PORT = 2810;
|
||||
protected static final String JSON_RPC_MULTICAST_IP1 = "239.255.68.139";
|
||||
protected static final String JSON_RPC_MULTICAST_IP2 = "224.255.68.139";
|
||||
private boolean lastBridgeConnectionState = false;
|
||||
private boolean currentBridgeConnectionState = false;
|
||||
|
||||
protected Random rand = new Random();
|
||||
protected Gson gson = new Gson();
|
||||
private final Logger logger = LoggerFactory.getLogger(MieleBridgeHandler.class);
|
||||
|
||||
protected List<ApplianceStatusListener> applianceStatusListeners = new CopyOnWriteArrayList<>();
|
||||
protected ScheduledFuture<?> pollingJob;
|
||||
protected ScheduledFuture<?> eventListenerJob;
|
||||
|
||||
protected List<HomeDevice> previousHomeDevices = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected URL url;
|
||||
protected Map<String, String> headers;
|
||||
|
||||
// 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;
|
||||
public String ProtocolAdapterName;
|
||||
public String Vendor;
|
||||
public String UID;
|
||||
public String Type;
|
||||
public JsonArray DeviceClasses;
|
||||
public String Version;
|
||||
public String TimestampAdded;
|
||||
public JsonObject Error;
|
||||
public JsonObject Properties;
|
||||
|
||||
HomeDevice() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return getApplianceId().replaceAll("[^a-zA-Z0-9_]", "_");
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return ProtocolAdapterName.equals(PROTOCOL_LAN) ? HDM_LAN : HDM_ZIGBEE;
|
||||
}
|
||||
|
||||
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 class DeviceClassObject {
|
||||
public String DeviceClassType;
|
||||
public JsonArray Operations;
|
||||
public String DeviceClass;
|
||||
public JsonArray Properties;
|
||||
|
||||
DeviceClassObject() {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceOperation {
|
||||
public String Name;
|
||||
public String Arguments;
|
||||
public JsonObject Metadata;
|
||||
|
||||
DeviceOperation() {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceProperty {
|
||||
public String Name;
|
||||
public String Value;
|
||||
public int Polling;
|
||||
public JsonObject Metadata;
|
||||
|
||||
DeviceProperty() {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceMetaData {
|
||||
public String Filter;
|
||||
public String description;
|
||||
public String LocalizedID;
|
||||
public String LocalizedValue;
|
||||
public JsonObject MieleEnum;
|
||||
public String access;
|
||||
}
|
||||
|
||||
public MieleBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing the Miele bridge handler.");
|
||||
|
||||
if (getConfig().get(HOST) != null && getConfig().get(INTERFACE) != null) {
|
||||
if (IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()
|
||||
&& IP_PATTERN.matcher((String) getConfig().get(INTERFACE)).matches()) {
|
||||
try {
|
||||
url = new URL("http://" + (String) getConfig().get(HOST) + "/remote/json-rpc");
|
||||
} catch (MalformedURLException e) {
|
||||
logger.debug("An exception occurred while defining an URL :'{}'", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// for future usage - no headers to be set for now
|
||||
headers = new HashMap<>();
|
||||
|
||||
onUpdate();
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Invalid IP address for the Miele@Home gateway or multicast interface:" + getConfig().get(HOST)
|
||||
+ "/" + getConfig().get(INTERFACE));
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Cannot connect to the Miele gateway. host IP address or multicast interface are not set.");
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable pollingRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (IP_PATTERN.matcher((String) getConfig().get(HOST)).matches()) {
|
||||
try {
|
||||
if (isReachable((String) getConfig().get(HOST))) {
|
||||
currentBridgeConnectionState = true;
|
||||
} else {
|
||||
currentBridgeConnectionState = false;
|
||||
lastBridgeConnectionState = false;
|
||||
onConnectionLost();
|
||||
}
|
||||
|
||||
if (!lastBridgeConnectionState && currentBridgeConnectionState) {
|
||||
logger.debug("Connection to Miele Gateway {} established.", getConfig().get(HOST));
|
||||
lastBridgeConnectionState = true;
|
||||
onConnectionResumed();
|
||||
}
|
||||
|
||||
if (currentBridgeConnectionState) {
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
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) {
|
||||
logger.debug("A new appliance with ID '{}' has been added", hd.UID);
|
||||
for (ApplianceStatusListener listener : applianceStatusListeners) {
|
||||
listener.onApplianceAdded(hd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (HomeDevice hd : previousHomeDevices) {
|
||||
boolean isCurrent = false;
|
||||
for (HomeDevice chd : currentHomeDevices) {
|
||||
if (chd.UID.equals(hd.UID)) {
|
||||
isCurrent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isCurrent) {
|
||||
logger.debug("The appliance with ID '{}' has been removed", hd);
|
||||
for (ApplianceStatusListener listener : applianceStatusListeners) {
|
||||
listener.onApplianceRemoved(hd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousHomeDevices = currentHomeDevices;
|
||||
|
||||
for (Thing appliance : getThing().getThings()) {
|
||||
if (appliance.getStatus() == ThingStatus.ONLINE) {
|
||||
String UID = appliance.getProperties().get(PROTOCOL_PROPERTY_NAME)
|
||||
+ (String) appliance.getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
Object[] args = new Object[2];
|
||||
args[0] = UID;
|
||||
args[1] = true;
|
||||
JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args);
|
||||
|
||||
if (result != null) {
|
||||
for (JsonElement obj : result.getAsJsonArray()) {
|
||||
try {
|
||||
DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
|
||||
|
||||
for (ApplianceStatusListener listener : applianceStatusListeners) {
|
||||
listener.onApplianceStateChanged(UID, dco);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("An exception occurred while quering an appliance : '{}'",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("An exception occurred while polling an appliance :'{}'", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Invalid IP address for the Miele@Home gateway : '{}'", getConfig().get(HOST));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReachable(String ipAddress) {
|
||||
try {
|
||||
// note that InetAddress.isReachable is unreliable, see
|
||||
// http://stackoverflow.com/questions/9922543/why-does-inetaddress-isreachable-return-false-when-i-can-ping-the-ip-address
|
||||
// That's why we do an HTTP access instead
|
||||
|
||||
// If there is no connection, this line will fail
|
||||
JsonElement result = invokeRPC("system.listMethods", null);
|
||||
if (result == null) {
|
||||
logger.debug("{} is not reachable", ipAddress);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug("{} is reachable", ipAddress);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public List<HomeDevice> getHomeDevices() {
|
||||
List<HomeDevice> devices = new ArrayList<>();
|
||||
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
String[] args = new String[1];
|
||||
args[0] = "(type=SuperVision)";
|
||||
JsonElement result = invokeRPC("HDAccess/getHomeDevices", args);
|
||||
|
||||
for (JsonElement obj : result.getAsJsonArray()) {
|
||||
HomeDevice hd = gson.fromJson(obj, HomeDevice.class);
|
||||
devices.add(hd);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("An exception occurred while getting the home devices :'{}'", e.getMessage());
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
private Runnable eventListenerRunnable = () -> {
|
||||
if (IP_PATTERN.matcher((String) getConfig().get(INTERFACE)).matches()) {
|
||||
while (true) {
|
||||
// Get the address that we are going to connect to.
|
||||
InetAddress address1 = null;
|
||||
InetAddress address2 = null;
|
||||
try {
|
||||
address1 = InetAddress.getByName(JSON_RPC_MULTICAST_IP1);
|
||||
address2 = InetAddress.getByName(JSON_RPC_MULTICAST_IP2);
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("An exception occurred while setting up the multicast receiver : '{}'",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
byte[] buf = new byte[256];
|
||||
MulticastSocket clientSocket = null;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
clientSocket = new MulticastSocket(JSON_RPC_PORT);
|
||||
clientSocket.setSoTimeout(100);
|
||||
|
||||
clientSocket.setInterface(InetAddress.getByName((String) getConfig().get(INTERFACE)));
|
||||
clientSocket.joinGroup(address1);
|
||||
clientSocket.joinGroup(address2);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
buf = new byte[256];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
clientSocket.receive(packet);
|
||||
|
||||
String event = new String(packet.getData());
|
||||
logger.debug("Received a multicast event '{}' from '{}:{}'", event, packet.getAddress(),
|
||||
packet.getPort());
|
||||
|
||||
DeviceProperty dp = new DeviceProperty();
|
||||
String uid = null;
|
||||
|
||||
String[] parts = StringUtils.split(event, "&");
|
||||
for (String p : parts) {
|
||||
String[] subparts = StringUtils.split(p, "=");
|
||||
switch (subparts[0]) {
|
||||
case "property": {
|
||||
dp.Name = subparts[1];
|
||||
break;
|
||||
}
|
||||
case "value": {
|
||||
dp.Value = subparts[1];
|
||||
break;
|
||||
}
|
||||
case "id": {
|
||||
uid = subparts[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ApplianceStatusListener listener : applianceStatusListeners) {
|
||||
listener.onAppliancePropertyChanged(uid, dp);
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.debug("An exception occurred while receiving multicast packets : '{}'", ex.getMessage());
|
||||
}
|
||||
|
||||
// restart the cycle with a clean slate
|
||||
try {
|
||||
if (clientSocket != null) {
|
||||
clientSocket.leaveGroup(address1);
|
||||
clientSocket.leaveGroup(address2);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while leaving multicast group : '{}'", e.getMessage());
|
||||
}
|
||||
if (clientSocket != null) {
|
||||
clientSocket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Invalid IP address for the multicast interface : '{}'", getConfig().get(INTERFACE));
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
Object[] args = new Object[4];
|
||||
args[0] = protocol + UID;
|
||||
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.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected JsonElement invokeRPC(String methodName, Object[] args) {
|
||||
int id = rand.nextInt(Integer.MAX_VALUE);
|
||||
|
||||
JsonObject req = new JsonObject();
|
||||
req.addProperty("jsonrpc", "2.0");
|
||||
req.addProperty("id", id);
|
||||
req.addProperty("method", methodName);
|
||||
|
||||
JsonElement result = null;
|
||||
|
||||
JsonArray params = new JsonArray();
|
||||
if (args != null) {
|
||||
for (Object o : args) {
|
||||
params.add(gson.toJsonTree(o));
|
||||
}
|
||||
}
|
||||
req.add("params", params);
|
||||
|
||||
String requestData = req.toString();
|
||||
String responseData = null;
|
||||
try {
|
||||
responseData = post(url, headers, requestData);
|
||||
} catch (Exception e) {
|
||||
logger.debug("An exception occurred while posting data : '{}'", e.getMessage());
|
||||
}
|
||||
|
||||
if (responseData != null) {
|
||||
logger.debug("The request '{}' yields '{}'", requestData, responseData);
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject resp = (JsonObject) parser.parse(new StringReader(responseData));
|
||||
|
||||
result = resp.get("result");
|
||||
JsonElement error = resp.get("error");
|
||||
|
||||
if (error != null && !error.isJsonNull()) {
|
||||
if (error.isJsonPrimitive()) {
|
||||
logger.debug("A remote exception occurred: '{}'", error.getAsString());
|
||||
} else if (error.isJsonObject()) {
|
||||
JsonObject o = error.getAsJsonObject();
|
||||
Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
|
||||
String message = (o.has("message") ? o.get("message").getAsString() : null);
|
||||
String data = (o.has("data") ? (o.get("data") instanceof JsonObject ? o.get("data").toString()
|
||||
: o.get("data").getAsString()) : null);
|
||||
logger.debug("A remote exception occurred: '{}':'{}':'{}'", code, message, data);
|
||||
} else {
|
||||
logger.debug("An unknown remote exception occurred: '{}'", error.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String post(URL url, Map<String, String> headers, String data) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
connection.addRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
connection.addRequestProperty("Accept-Encoding", "gzip");
|
||||
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
connection.connect();
|
||||
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
out = connection.getOutputStream();
|
||||
|
||||
out.write(data.getBytes());
|
||||
out.flush();
|
||||
|
||||
int statusCode = connection.getResponseCode();
|
||||
if (statusCode != HttpURLConnection.HTTP_OK) {
|
||||
logger.debug("An unexpected status code was returned: '{}'", statusCode);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
String responseEncoding = connection.getHeaderField("Content-Encoding");
|
||||
responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
InputStream in = connection.getInputStream();
|
||||
try {
|
||||
in = connection.getInputStream();
|
||||
if ("gzip".equalsIgnoreCase(responseEncoding)) {
|
||||
in = new GZIPInputStream(in);
|
||||
}
|
||||
in = new BufferedInputStream(in);
|
||||
|
||||
byte[] buff = new byte[1024];
|
||||
int n;
|
||||
while ((n = in.read(buff)) > 0) {
|
||||
bos.write(buff, 0, n);
|
||||
}
|
||||
bos.flush();
|
||||
bos.close();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
return bos.toString();
|
||||
}
|
||||
|
||||
private synchronized void onUpdate() {
|
||||
logger.debug("Scheduling the Miele polling job");
|
||||
if (pollingJob == null || pollingJob.isCancelled()) {
|
||||
logger.trace("Scheduling the Miele polling job period is {}", POLLING_PERIOD);
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, POLLING_PERIOD, TimeUnit.SECONDS);
|
||||
logger.trace("Scheduling the Miele polling job Job is done ?{}", pollingJob.isDone());
|
||||
}
|
||||
logger.debug("Scheduling the Miele event listener job");
|
||||
|
||||
if (eventListenerJob == null || eventListenerJob.isCancelled()) {
|
||||
eventListenerJob = scheduler.schedule(eventListenerRunnable, 0, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever the connection to the given {@link MieleBridge} is lost.
|
||||
*
|
||||
*/
|
||||
public void onConnectionLost() {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever the connection to the given {@link MieleBridge} is resumed.
|
||||
*
|
||||
* @param bridge the hue bridge the connection is resumed to
|
||||
*/
|
||||
public void onConnectionResumed() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
for (Thing thing : getThing().getThings()) {
|
||||
MieleApplianceHandler<?> handler = (MieleApplianceHandler<?>) thing.getHandler();
|
||||
if (handler != null) {
|
||||
handler.onBridgeConnectionResumed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean registerApplianceStatusListener(ApplianceStatusListener applianceStatusListener) {
|
||||
if (applianceStatusListener == null) {
|
||||
throw new IllegalArgumentException("It's not allowed to pass a null ApplianceStatusListener.");
|
||||
}
|
||||
boolean result = applianceStatusListeners.add(applianceStatusListener);
|
||||
if (result && isInitialized()) {
|
||||
onUpdate();
|
||||
|
||||
for (HomeDevice hd : getHomeDevices()) {
|
||||
applianceStatusListener.onApplianceAdded(hd);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean unregisterApplianceStatusListener(ApplianceStatusListener applianceStatusListener) {
|
||||
boolean result = applianceStatusListeners.remove(applianceStatusListener);
|
||||
if (result && isInitialized()) {
|
||||
onUpdate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Nothing to do here - the XGW bridge does not handle commands, for now
|
||||
if (command instanceof RefreshType) {
|
||||
// Placeholder for future refinement
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (pollingJob != null) {
|
||||
pollingJob.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for ovens
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
|
||||
*/
|
||||
public enum OvenChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PROGRAMID("programId", "program", StringType.class, false),
|
||||
PROGRAMPHASE("phase", "phase", StringType.class, false),
|
||||
START_TIME("startTime", "start", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DURATION("duration", "duration", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
TARGET_TEMP("targetTemperature", "target", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
MEASURED_TEMP("measuredTemperature", "measured", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DEVICE_TEMP_ONE("deviceTemperature1", "temp1", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DEVICE_TEMP_TWO("deviceTemperature2", "temp2", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
STOP(null, "stop", OnOffType.class, false),
|
||||
SWITCH(null, "switch", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OvenChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
OvenChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass, boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.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.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link OvenHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OvenHandler.class);
|
||||
|
||||
public OvenHandler(Thing thing) {
|
||||
super(thing, OvenChannelSelector.class, "Oven");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
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);
|
||||
|
||||
OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SWITCH: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "switchOn", protocol);
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "switchOff", protocol);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stop", protocol);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for tumble dryers
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
|
||||
*/
|
||||
public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PROGRAMID("programId", "program", StringType.class, false),
|
||||
PROGRAMTYPE("programType", "type", StringType.class, false),
|
||||
PROGRAMPHASE("phase", "phase", StringType.class, false),
|
||||
START_TIME("startTime", "start", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DURATION("duration", "duration", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DRYING_STEP("dryingStep", "step", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
SWITCH(null, "switch", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TumbleDryerChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
TumbleDryerChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass,
|
||||
boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link TumbleDryerHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class);
|
||||
|
||||
public TumbleDryerHandler(Thing thing) {
|
||||
super(thing, TumbleDryerChannelSelector.class, "TumbleDryer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
TumbleDryerChannelSelector selector = (TumbleDryerChannelSelector) getValueSelectorFromChannelID(channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SWITCH: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "start");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stop");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceMetaData;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.Type;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link ApplianceChannelSelector} for washing machines
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
|
||||
*/
|
||||
public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
|
||||
|
||||
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
|
||||
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
|
||||
BRAND_ID("brandId", "brandId", StringType.class, true),
|
||||
COMPANY_ID("companyId", "companyId", StringType.class, true),
|
||||
STATE("state", "state", StringType.class, false),
|
||||
PROGRAMID("programId", "program", StringType.class, false),
|
||||
PROGRAMTYPE("programType", "type", StringType.class, false),
|
||||
PROGRAMPHASE("phase", "phase", StringType.class, false),
|
||||
START_TIME("startTime", "start", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
DURATION("duration", "duration", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(StringUtils.trim(s)) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
try {
|
||||
date.setTime(Long.valueOf(s) * 60000);
|
||||
} catch (Exception e) {
|
||||
date.setTime(0);
|
||||
}
|
||||
return getState(dateFormatter.format(date));
|
||||
}
|
||||
},
|
||||
TARGET_TEMP("targetTemperature", "target", DecimalType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
return getState(s);
|
||||
}
|
||||
},
|
||||
SPINNING_SPEED("spinningSpeed", "spinningspeed", StringType.class, false) {
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("0".equals(s)) {
|
||||
return getState("Without spinning");
|
||||
}
|
||||
if ("256".equals(s)) {
|
||||
return getState("Rinsing");
|
||||
}
|
||||
return getState(Integer.toString((Integer.valueOf(s) * 10)));
|
||||
}
|
||||
},
|
||||
DOOR("signalDoor", "door", OpenClosedType.class, false) {
|
||||
@Override
|
||||
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if ("true".equals(s)) {
|
||||
return getState("OPEN");
|
||||
}
|
||||
|
||||
if ("false".equals(s)) {
|
||||
return getState("CLOSED");
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
},
|
||||
SWITCH(null, "switch", OnOffType.class, false);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WashingMachineChannelSelector.class);
|
||||
|
||||
private final String mieleID;
|
||||
private final String channelID;
|
||||
private final Class<? extends Type> typeClass;
|
||||
private final boolean isProperty;
|
||||
|
||||
WashingMachineChannelSelector(String propertyID, String channelID, Class<? extends Type> typeClass,
|
||||
boolean isProperty) {
|
||||
this.mieleID = propertyID;
|
||||
this.channelID = channelID;
|
||||
this.typeClass = typeClass;
|
||||
this.isProperty = isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMieleID() {
|
||||
return mieleID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelID() {
|
||||
return channelID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Type> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty() {
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState(String s, DeviceMetaData dmd) {
|
||||
if (dmd != null) {
|
||||
String localizedValue = getMieleEnum(s, dmd);
|
||||
if (localizedValue == null) {
|
||||
localizedValue = dmd.LocalizedValue;
|
||||
}
|
||||
if (localizedValue == null) {
|
||||
localizedValue = s;
|
||||
}
|
||||
|
||||
return getState(localizedValue);
|
||||
} else {
|
||||
return getState(s);
|
||||
}
|
||||
}
|
||||
|
||||
public State getState(String s) {
|
||||
try {
|
||||
Method valueOf = typeClass.getMethod("valueOf", String.class);
|
||||
State state = (State) valueOf.invoke(typeClass, s);
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while converting '{}' into a State", s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getMieleEnum(String s, DeviceMetaData dmd) {
|
||||
if (dmd.MieleEnum != null) {
|
||||
for (Entry<String, JsonElement> enumEntry : dmd.MieleEnum.entrySet()) {
|
||||
if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
|
||||
return enumEntry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.handler;
|
||||
|
||||
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
|
||||
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* The {@link WashingMachineHandler} is responsible for handling commands,
|
||||
* which are sent to one of the channels
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
* @author Kai Kreuzer - fixed handling of REFRESH commands
|
||||
* @author Martin Lepsy - fixed handling of empty JSON results
|
||||
*/
|
||||
public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineChannelSelector> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WashingMachineHandler.class);
|
||||
|
||||
public WashingMachineHandler(Thing thing) {
|
||||
super(thing, WashingMachineChannelSelector.class, "WashingMachine");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
super.handleCommand(channelUID, command);
|
||||
|
||||
String channelID = channelUID.getId();
|
||||
String uid = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||
|
||||
WashingMachineChannelSelector selector = (WashingMachineChannelSelector) getValueSelectorFromChannelID(
|
||||
channelID);
|
||||
JsonElement result = null;
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
switch (selector) {
|
||||
case SWITCH: {
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "start");
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
result = bridgeHandler.invokeOperation(uid, modelID, "stop");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
logger.debug("{} is a read-only channel that does not accept commands",
|
||||
selector.getChannelID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// process result
|
||||
if (isResultProcessable(result)) {
|
||||
logger.debug("Result of operation is {}", result.getAsString());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn(
|
||||
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
|
||||
channelID, command.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="miele" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Miele Binding</name>
|
||||
<description>This is the binding for Miele@home appliances</description>
|
||||
<author>Karel Goderis</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="state" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Status</label>
|
||||
<description>Current status of the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="program" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Program</label>
|
||||
<description>Current program or function running on the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="type" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Program Type</label>
|
||||
<description>Type of the program running on the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="phase" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Phase</label>
|
||||
<description>Current phase of the program running on the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="start" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Start Time</label>
|
||||
<description>Programmed start time of the program</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="duration" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Duration</label>
|
||||
<description>Duration of the program running on the appliance</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="elapsed" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Elapsed Time</label>
|
||||
<description>Time elapsed in the program running on the appliance</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="finish" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Finish Time</label>
|
||||
<description>Time to finish the program running on the appliance</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="door" advanced="false">
|
||||
<item-type>Contact</item-type>
|
||||
<label>Door</label>
|
||||
<description>Current state of the door of the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switch" advanced="false">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Switch</label>
|
||||
<description>Switch the appliance on or off</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="stop" advanced="false">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Stop</label>
|
||||
<description>Stop the hood</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="step" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Step</label>
|
||||
<description>Current step in the program running on the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="target" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Target Temperature</label>
|
||||
<description>Target temperature to be reached by the oven</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="measured" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Measured Temperature</label>
|
||||
<description>Actual measured temperature in the oven</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temp" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Program temperature in the oven</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="supercool" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Super Cool</label>
|
||||
<description>Start Super Cooling</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="current" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Current Temperature</label>
|
||||
<description>Current temperature in the fridge</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="freezerstate" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Status</label>
|
||||
<description>Current status of the freezer compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fridgestate" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Status</label>
|
||||
<description>Current status of the fridge compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="superfreeze" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Super Freeze</label>
|
||||
<description>Start Super Freezing</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="freezercurrent" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Current Temperature</label>
|
||||
<description>Current temperature in the freezer compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="freezertarget" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Target Temperature</label>
|
||||
<description>Target temperature to be reached by the freezer compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fridgecurrent" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Current Temperature</label>
|
||||
<description>Current temperature in the fridge compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fridgetarget" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Target Temperature</label>
|
||||
<description>Target temperature to be reached by the fridge compartment</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="plates" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Plates</label>
|
||||
<description>Number of heating zones/plates on the hob</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="power" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Power Step</label>
|
||||
<description>Power level of the heating zone/plate</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heat" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Remaining Heat</label>
|
||||
<description>Remaining heat level of the heating zone/plate</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="time" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Remaining Time</label>
|
||||
<description>Remaining time of the heating zone/plate</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ventilation" advanced="false">
|
||||
<item-type>Number</item-type>
|
||||
<label>Ventilation Power</label>
|
||||
<description>Current ventilation power</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="spinningspeed" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Spinning Speed</label>
|
||||
<description>Spinning speed in the program running on the appliance</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Coffee machine -->
|
||||
<thing-type id="coffeemachine">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Coffee Machine</label>
|
||||
<description>This is a Miele@home compatible coffee machine</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="type" typeId="type"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Dishwasher -->
|
||||
<thing-type id="dishwasher">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Dishwasher</label>
|
||||
<description>This is a Miele@home compatible dishwasher</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="elapsed" typeId="elapsed"/>
|
||||
<channel id="finish" typeId="finish"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Fridge -->
|
||||
<thing-type id="fridge">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Fridge</label>
|
||||
<description>This is a Miele@home compatible fridge</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="supercool" typeId="supercool"/>
|
||||
<channel id="current" typeId="current"/>
|
||||
<channel id="target" typeId="target"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="start" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Fridge freezer -->
|
||||
<thing-type id="fridgefreezer">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Fridge Freezer</label>
|
||||
<description>This is a Miele@home compatible fridgefreezer</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="freezerstate" typeId="freezerstate"/>
|
||||
<channel id="fridgestate" typeId="fridgestate"/>
|
||||
<channel id="supercool" typeId="supercool"/>
|
||||
<channel id="superfreeze" typeId="superfreeze"/>
|
||||
<channel id="freezercurrent" typeId="freezercurrent"/>
|
||||
<channel id="freezertarget" typeId="freezertarget"/>
|
||||
<channel id="fridgecurrent" typeId="fridgecurrent"/>
|
||||
<channel id="fridgetarget" typeId="fridgetarget"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="start" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Hob -->
|
||||
<thing-type id="hob">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Hob</label>
|
||||
<description>This is a Miele@home compatible hob</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="plate1power" typeId="power"/>
|
||||
<channel id="plate1heat" typeId="heat"/>
|
||||
<channel id="plate1time" typeId="time"/>
|
||||
<channel id="plate2power" typeId="power"/>
|
||||
<channel id="plate2heat" typeId="heat"/>
|
||||
<channel id="plate2time" typeId="time"/>
|
||||
<channel id="plate3power" typeId="power"/>
|
||||
<channel id="plate3heat" typeId="heat"/>
|
||||
<channel id="plate3time" typeId="time"/>
|
||||
<channel id="plate4power" typeId="power"/>
|
||||
<channel id="plate4heat" typeId="heat"/>
|
||||
<channel id="plate4time" typeId="time"/>
|
||||
<channel id="plate5power" typeId="power"/>
|
||||
<channel id="plate5heat" typeId="heat"/>
|
||||
<channel id="plate5time" typeId="time"/>
|
||||
<channel id="plate6power" typeId="power"/>
|
||||
<channel id="plate6heat" typeId="heat"/>
|
||||
<channel id="plate6time" typeId="time"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Hood -->
|
||||
<thing-type id="hood">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Hood</label>
|
||||
<description>This is a Miele@home compatible hood</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="light" typeId="switch"/>
|
||||
<channel id="ventilation" typeId="ventilation"/>
|
||||
<channel id="stop" typeId="stop"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies the appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Oven -->
|
||||
<thing-type id="oven">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Oven</label>
|
||||
<description>This is a Miele@home compatible oven</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="type" typeId="type"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="elapsed" typeId="elapsed"/>
|
||||
<channel id="finish" typeId="finish"/>
|
||||
<channel id="target" typeId="target"/>
|
||||
<channel id="measured" typeId="measured"/>
|
||||
<channel id="temp1" typeId="temp"/>
|
||||
<channel id="temp2" typeId="temp"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="stop" typeId="stop"/>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Tumble dryer -->
|
||||
<thing-type id="tumbledryer">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Tumbledryer</label>
|
||||
<description>This is a Miele@home compatible tumbledryer</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="type" typeId="type"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="elapsed" typeId="elapsed"/>
|
||||
<channel id="finish" typeId="finish"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
<channel id="step" typeId="step"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Washing machine -->
|
||||
<thing-type id="washingmachine">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="xgw3000"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Washing Machine</label>
|
||||
<description>This is a Miele@home compatible washing machine</description>
|
||||
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="type" typeId="type"/>
|
||||
<channel id="phase" typeId="phase"/>
|
||||
<channel id="start" typeId="start"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="elapsed" typeId="elapsed"/>
|
||||
<channel id="finish" typeId="finish"/>
|
||||
<channel id="door" typeId="door"/>
|
||||
<channel id="switch" typeId="switch"/>
|
||||
<channel id="target" typeId="target"/>
|
||||
<channel id="spinningspeed" typeId="spinningspeed"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="uid" type="text">
|
||||
<label>ID</label>
|
||||
<description>The identifier identifies one certain appliance on the ZigBee network.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miele"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Miele Bridge -->
|
||||
<bridge-type id="xgw3000">
|
||||
<label>Miele XGW3000</label>
|
||||
<description>The miele bridge represents the Miele@home XGW3000 gateway.</description>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">Miele</property>
|
||||
</properties>
|
||||
|
||||
<config-description>
|
||||
<parameter name="ipAddress" type="text">
|
||||
<context>network-address</context>
|
||||
<label>Network Address</label>
|
||||
<description>Network address of the Miele@home gateway.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="interface" type="text">
|
||||
<context>network-address</context>
|
||||
<label>Network Address of the Multicast Interface</label>
|
||||
<description>Network address of openHAB host interface where the binding will listen for multicast events coming
|
||||
from the Miele@home gateway</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="userName" type="text">
|
||||
<label>Username</label>
|
||||
<description>
|
||||
Name of a registered Miele@home user.
|
||||
</description>
|
||||
<required>false</required>
|
||||
</parameter>
|
||||
<parameter name="password" type="text">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password for the registered Miele@home</description>
|
||||
<required>false</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user