Simplify discovery registration (#18609)
Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>pull/18725/head
parent
84e1508054
commit
fe31deff03
|
@ -14,15 +14,9 @@ package org.openhab.binding.wemo.internal;
|
||||||
|
|
||||||
import static org.openhab.binding.wemo.internal.WemoBindingConstants.UDN;
|
import static org.openhab.binding.wemo.internal.WemoBindingConstants.UDN;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.wemo.internal.discovery.WemoLinkDiscoveryService;
|
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoBridgeHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoBridgeHandler;
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoCoffeeHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoCoffeeHandler;
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoCrockpotHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoCrockpotHandler;
|
||||||
|
@ -33,17 +27,14 @@ import org.openhab.binding.wemo.internal.handler.WemoLightHandler;
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoMakerHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoMakerHandler;
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoMotionHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoMotionHandler;
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoSwitchHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoSwitchHandler;
|
||||||
import org.openhab.core.config.discovery.DiscoveryService;
|
|
||||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.io.transport.upnp.UpnpIOService;
|
import org.openhab.core.io.transport.upnp.UpnpIOService;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
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.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
import org.osgi.framework.ServiceRegistration;
|
|
||||||
import org.osgi.service.component.annotations.Activate;
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
@ -63,18 +54,14 @@ public class WemoHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(WemoHandlerFactory.class);
|
private final Logger logger = LoggerFactory.getLogger(WemoHandlerFactory.class);
|
||||||
|
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = WemoBindingConstants.SUPPORTED_THING_TYPES;
|
|
||||||
|
|
||||||
private final UpnpIOService upnpIOService;
|
private final UpnpIOService upnpIOService;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
|
return WemoBindingConstants.SUPPORTED_THING_TYPES.contains(thingTypeUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public WemoHandlerFactory(final @Reference UpnpIOService upnpIOService,
|
public WemoHandlerFactory(final @Reference UpnpIOService upnpIOService,
|
||||||
final @Reference HttpClientFactory httpClientFactory) {
|
final @Reference HttpClientFactory httpClientFactory) {
|
||||||
|
@ -90,9 +77,7 @@ public class WemoHandlerFactory extends BaseThingHandlerFactory {
|
||||||
if (thingTypeUID.equals(WemoBindingConstants.THING_TYPE_BRIDGE)) {
|
if (thingTypeUID.equals(WemoBindingConstants.THING_TYPE_BRIDGE)) {
|
||||||
logger.debug("Creating a WemoBridgeHandler for thing '{}' with UDN '{}'", thing.getUID(),
|
logger.debug("Creating a WemoBridgeHandler for thing '{}' with UDN '{}'", thing.getUID(),
|
||||||
thing.getConfiguration().get(UDN));
|
thing.getConfiguration().get(UDN));
|
||||||
WemoBridgeHandler handler = new WemoBridgeHandler((Bridge) thing);
|
return new WemoBridgeHandler((Bridge) thing, upnpIOService, httpClient);
|
||||||
registerDeviceDiscoveryService(handler);
|
|
||||||
return handler;
|
|
||||||
} else if (WemoBindingConstants.THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
|
} else if (WemoBindingConstants.THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
|
||||||
logger.debug("Creating a WemoInsightHandler for thing '{}' with UDN '{}'", thing.getUID(),
|
logger.debug("Creating a WemoInsightHandler for thing '{}' with UDN '{}'", thing.getUID(),
|
||||||
thing.getConfiguration().get(UDN));
|
thing.getConfiguration().get(UDN));
|
||||||
|
@ -141,21 +126,4 @@ public class WemoHandlerFactory extends BaseThingHandlerFactory {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
|
||||||
if (thingHandler instanceof WemoBridgeHandler) {
|
|
||||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
|
||||||
if (serviceReg != null) {
|
|
||||||
serviceReg.unregister();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void registerDeviceDiscoveryService(WemoBridgeHandler wemoBridgeHandler) {
|
|
||||||
WemoLinkDiscoveryService discoveryService = new WemoLinkDiscoveryService(wemoBridgeHandler, upnpIOService,
|
|
||||||
httpClient);
|
|
||||||
this.discoveryServiceRegs.put(wemoBridgeHandler.getThing().getUID(),
|
|
||||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
|
||||||
import static org.openhab.binding.wemo.internal.WemoUtil.*;
|
import static org.openhab.binding.wemo.internal.WemoUtil.*;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -28,16 +27,16 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.openhab.binding.wemo.internal.exception.WemoException;
|
||||||
import org.openhab.binding.wemo.internal.ApiController;
|
|
||||||
import org.openhab.binding.wemo.internal.handler.WemoBridgeHandler;
|
import org.openhab.binding.wemo.internal.handler.WemoBridgeHandler;
|
||||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
|
||||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
|
import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
|
||||||
import org.openhab.core.io.transport.upnp.UpnpIOService;
|
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.ServiceScope;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.w3c.dom.CharacterData;
|
import org.w3c.dom.CharacterData;
|
||||||
|
@ -54,8 +53,10 @@ import org.xml.sax.InputSource;
|
||||||
* @author Hans-Jörg Merk - Initial contribution
|
* @author Hans-Jörg Merk - Initial contribution
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Component(scope = ServiceScope.PROTOTYPE, service = WemoLinkDiscoveryService.class)
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class WemoLinkDiscoveryService extends AbstractDiscoveryService implements UpnpIOParticipant {
|
public class WemoLinkDiscoveryService extends AbstractThingHandlerDiscoveryService<WemoBridgeHandler>
|
||||||
|
implements UpnpIOParticipant {
|
||||||
|
|
||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_MZ100);
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_MZ100);
|
||||||
private static final String NORMALIZE_ID_REGEX = "[^a-zA-Z0-9_]";
|
private static final String NORMALIZE_ID_REGEX = "[^a-zA-Z0-9_]";
|
||||||
|
@ -63,153 +64,118 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
private static final int SCAN_INTERVAL_SECONDS = 120;
|
private static final int SCAN_INTERVAL_SECONDS = 120;
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(WemoLinkDiscoveryService.class);
|
private final Logger logger = LoggerFactory.getLogger(WemoLinkDiscoveryService.class);
|
||||||
private final WemoBridgeHandler wemoBridgeHandler;
|
|
||||||
private final WemoLinkScan scanningRunnable;
|
private final WemoLinkScan scanningRunnable;
|
||||||
private final UpnpIOService service;
|
|
||||||
private final ApiController apiController;
|
|
||||||
|
|
||||||
private @Nullable ScheduledFuture<?> scanningJob;
|
private @Nullable ScheduledFuture<?> scanningJob;
|
||||||
|
|
||||||
public WemoLinkDiscoveryService(final WemoBridgeHandler wemoBridgeHandler, final UpnpIOService upnpIOService,
|
public WemoLinkDiscoveryService() {
|
||||||
final HttpClient httpClient) {
|
super(WemoBridgeHandler.class, SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS);
|
||||||
super(DISCOVERY_TIMEOUT_SECONDS);
|
|
||||||
this.service = upnpIOService;
|
|
||||||
this.wemoBridgeHandler = wemoBridgeHandler;
|
|
||||||
this.apiController = new ApiController(httpClient);
|
|
||||||
|
|
||||||
this.scanningRunnable = new WemoLinkScan();
|
this.scanningRunnable = new WemoLinkScan();
|
||||||
this.activate(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
|
||||||
return SUPPORTED_THING_TYPES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startScan() {
|
public void startScan() {
|
||||||
logger.trace("Starting WeMoEndDevice discovery on WeMo Link {}", wemoBridgeHandler.getThing().getUID());
|
logger.trace("Starting WeMoEndDevice discovery on WeMo Link {}", thingHandler.getThing().getUID());
|
||||||
|
|
||||||
|
String endDevicesResponse;
|
||||||
try {
|
try {
|
||||||
String devUDN = "uuid:" + wemoBridgeHandler.getThing().getConfiguration().get(UDN).toString();
|
endDevicesResponse = thingHandler.getEndDevices(this);
|
||||||
logger.trace("devUDN = '{}'", devUDN);
|
} catch (WemoException e) {
|
||||||
|
logger.debug("Failed to get endDevices for bridge '{}'", thingHandler.getThing().getUID(), e);
|
||||||
|
return;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Stop scanning
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String soapHeader = "\"urn:Belkin:service:bridge:1#GetEndDevices\"";
|
try {
|
||||||
String content = """
|
String stringParser = substringBetween(endDevicesResponse, "<DeviceLists>", "</DeviceLists>");
|
||||||
<?xml version="1.0"?>\
|
|
||||||
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\
|
|
||||||
<s:Body>\
|
|
||||||
<u:GetEndDevices xmlns:u="urn:Belkin:service:bridge:1">\
|
|
||||||
<DevUDN>\
|
|
||||||
"""
|
|
||||||
+ devUDN + """
|
|
||||||
</DevUDN>\
|
|
||||||
<ReqListType>PAIRED_LIST</ReqListType>\
|
|
||||||
</u:GetEndDevices>\
|
|
||||||
</s:Body>\
|
|
||||||
</s:Envelope>\
|
|
||||||
""";
|
|
||||||
|
|
||||||
URL descriptorURL = service.getDescriptorURL(this);
|
stringParser = unescapeXml(stringParser);
|
||||||
|
|
||||||
if (descriptorURL != null) {
|
// check if there are already paired devices with WeMo Link
|
||||||
String deviceURL = substringBefore(descriptorURL.toString(), "/setup.xml");
|
if ("0".equals(stringParser)) {
|
||||||
String wemoURL = deviceURL + "/upnp/control/bridge1";
|
logger.debug("There are no devices connected with WeMo Link. Exit discovery");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String endDeviceRequest = apiController.executeCall(wemoURL, soapHeader, content);
|
// Build parser for received <DeviceList>
|
||||||
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
|
// see
|
||||||
|
// https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
||||||
|
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||||
|
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||||
|
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||||
|
dbf.setXIncludeAware(false);
|
||||||
|
dbf.setExpandEntityReferences(false);
|
||||||
|
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||||
|
InputSource is = new InputSource();
|
||||||
|
is.setCharacterStream(new StringReader(stringParser));
|
||||||
|
|
||||||
logger.trace("endDeviceRequest answered '{}'", endDeviceRequest);
|
Document doc = db.parse(is);
|
||||||
|
NodeList nodes = doc.getElementsByTagName("DeviceInfo");
|
||||||
|
|
||||||
try {
|
// iterate the devices
|
||||||
String stringParser = substringBetween(endDeviceRequest, "<DeviceLists>", "</DeviceLists>");
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
|
Element element = (Element) nodes.item(i);
|
||||||
|
|
||||||
stringParser = unescapeXml(stringParser);
|
NodeList deviceIndex = element.getElementsByTagName("DeviceIndex");
|
||||||
|
Element line = (Element) deviceIndex.item(0);
|
||||||
|
logger.trace("DeviceIndex: {}", getCharacterDataFromElement(line));
|
||||||
|
|
||||||
// check if there are already paired devices with WeMo Link
|
NodeList deviceID = element.getElementsByTagName("DeviceID");
|
||||||
if ("0".equals(stringParser)) {
|
line = (Element) deviceID.item(0);
|
||||||
logger.debug("There are no devices connected with WeMo Link. Exit discovery");
|
String endDeviceID = getCharacterDataFromElement(line);
|
||||||
return;
|
logger.trace("DeviceID: {}", endDeviceID);
|
||||||
|
|
||||||
|
NodeList friendlyName = element.getElementsByTagName("FriendlyName");
|
||||||
|
line = (Element) friendlyName.item(0);
|
||||||
|
String endDeviceName = getCharacterDataFromElement(line);
|
||||||
|
logger.trace("FriendlyName: {}", endDeviceName);
|
||||||
|
|
||||||
|
NodeList vendor = element.getElementsByTagName("Manufacturer");
|
||||||
|
line = (Element) vendor.item(0);
|
||||||
|
String endDeviceVendor = getCharacterDataFromElement(line);
|
||||||
|
logger.trace("Manufacturer: {}", endDeviceVendor);
|
||||||
|
|
||||||
|
NodeList model = element.getElementsByTagName("ModelCode");
|
||||||
|
line = (Element) model.item(0);
|
||||||
|
String endDeviceModelID = getCharacterDataFromElement(line);
|
||||||
|
endDeviceModelID = endDeviceModelID.replaceAll(NORMALIZE_ID_REGEX, "_");
|
||||||
|
|
||||||
|
logger.trace("ModelCode: {}", endDeviceModelID);
|
||||||
|
|
||||||
|
if (SUPPORTED_THING_TYPES.contains(new ThingTypeUID(BINDING_ID, endDeviceModelID))) {
|
||||||
|
logger.debug("Discovered a WeMo LED Light thing with ID '{}'", endDeviceID);
|
||||||
|
|
||||||
|
ThingUID bridgeUID = thingHandler.getThing().getUID();
|
||||||
|
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, endDeviceModelID);
|
||||||
|
|
||||||
|
if (thingTypeUID.equals(THING_TYPE_MZ100)) {
|
||||||
|
String thingLightId = endDeviceID;
|
||||||
|
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, thingLightId);
|
||||||
|
|
||||||
|
Map<String, Object> properties = new HashMap<>(1);
|
||||||
|
properties.put(DEVICE_ID, endDeviceID);
|
||||||
|
|
||||||
|
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||||
|
.withProperties(properties).withBridge(thingHandler.getThing().getUID())
|
||||||
|
.withLabel(endDeviceName).build();
|
||||||
|
|
||||||
|
thingDiscovered(discoveryResult);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Build parser for received <DeviceList>
|
logger.debug("Discovered an unsupported device :");
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
logger.debug("DeviceIndex : {}", getCharacterDataFromElement(line));
|
||||||
// see
|
logger.debug("DeviceID : {}", endDeviceID);
|
||||||
// https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
logger.debug("FriendlyName: {}", endDeviceName);
|
||||||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
logger.debug("Manufacturer: {}", endDeviceVendor);
|
||||||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
logger.debug("ModelCode : {}", endDeviceModelID);
|
||||||
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
|
||||||
dbf.setXIncludeAware(false);
|
|
||||||
dbf.setExpandEntityReferences(false);
|
|
||||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
|
||||||
InputSource is = new InputSource();
|
|
||||||
is.setCharacterStream(new StringReader(stringParser));
|
|
||||||
|
|
||||||
Document doc = db.parse(is);
|
|
||||||
NodeList nodes = doc.getElementsByTagName("DeviceInfo");
|
|
||||||
|
|
||||||
// iterate the devices
|
|
||||||
for (int i = 0; i < nodes.getLength(); i++) {
|
|
||||||
Element element = (Element) nodes.item(i);
|
|
||||||
|
|
||||||
NodeList deviceIndex = element.getElementsByTagName("DeviceIndex");
|
|
||||||
Element line = (Element) deviceIndex.item(0);
|
|
||||||
logger.trace("DeviceIndex: {}", getCharacterDataFromElement(line));
|
|
||||||
|
|
||||||
NodeList deviceID = element.getElementsByTagName("DeviceID");
|
|
||||||
line = (Element) deviceID.item(0);
|
|
||||||
String endDeviceID = getCharacterDataFromElement(line);
|
|
||||||
logger.trace("DeviceID: {}", endDeviceID);
|
|
||||||
|
|
||||||
NodeList friendlyName = element.getElementsByTagName("FriendlyName");
|
|
||||||
line = (Element) friendlyName.item(0);
|
|
||||||
String endDeviceName = getCharacterDataFromElement(line);
|
|
||||||
logger.trace("FriendlyName: {}", endDeviceName);
|
|
||||||
|
|
||||||
NodeList vendor = element.getElementsByTagName("Manufacturer");
|
|
||||||
line = (Element) vendor.item(0);
|
|
||||||
String endDeviceVendor = getCharacterDataFromElement(line);
|
|
||||||
logger.trace("Manufacturer: {}", endDeviceVendor);
|
|
||||||
|
|
||||||
NodeList model = element.getElementsByTagName("ModelCode");
|
|
||||||
line = (Element) model.item(0);
|
|
||||||
String endDeviceModelID = getCharacterDataFromElement(line);
|
|
||||||
endDeviceModelID = endDeviceModelID.replaceAll(NORMALIZE_ID_REGEX, "_");
|
|
||||||
|
|
||||||
logger.trace("ModelCode: {}", endDeviceModelID);
|
|
||||||
|
|
||||||
if (SUPPORTED_THING_TYPES.contains(new ThingTypeUID(BINDING_ID, endDeviceModelID))) {
|
|
||||||
logger.debug("Discovered a WeMo LED Light thing with ID '{}'", endDeviceID);
|
|
||||||
|
|
||||||
ThingUID bridgeUID = wemoBridgeHandler.getThing().getUID();
|
|
||||||
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, endDeviceModelID);
|
|
||||||
|
|
||||||
if (thingTypeUID.equals(THING_TYPE_MZ100)) {
|
|
||||||
String thingLightId = endDeviceID;
|
|
||||||
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, thingLightId);
|
|
||||||
|
|
||||||
Map<String, Object> properties = new HashMap<>(1);
|
|
||||||
properties.put(DEVICE_ID, endDeviceID);
|
|
||||||
|
|
||||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
|
||||||
.withProperties(properties).withBridge(wemoBridgeHandler.getThing().getUID())
|
|
||||||
.withLabel(endDeviceName).build();
|
|
||||||
|
|
||||||
thingDiscovered(discoveryResult);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.debug("Discovered an unsupported device :");
|
|
||||||
logger.debug("DeviceIndex : {}", getCharacterDataFromElement(line));
|
|
||||||
logger.debug("DeviceID : {}", endDeviceID);
|
|
||||||
logger.debug("FriendlyName: {}", endDeviceName);
|
|
||||||
logger.debug("Manufacturer: {}", endDeviceVendor);
|
|
||||||
logger.debug("ModelCode : {}", endDeviceModelID);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("Failed to parse endDevices for bridge '{}'", wemoBridgeHandler.getThing().getUID(), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Failed to get endDevices for bridge '{}'", wemoBridgeHandler.getThing().getUID(), e);
|
logger.warn("Failed to parse endDevices for bridge '{}'", thingHandler.getThing().getUID(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,8 +186,8 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
ScheduledFuture<?> job = scanningJob;
|
ScheduledFuture<?> job = scanningJob;
|
||||||
|
|
||||||
if (job == null || job.isCancelled()) {
|
if (job == null || job.isCancelled()) {
|
||||||
this.scanningJob = scheduler.scheduleWithFixedDelay(this.scanningRunnable,
|
this.scanningJob = scheduler.scheduleWithFixedDelay(scanningRunnable, LINK_DISCOVERY_SERVICE_INITIAL_DELAY,
|
||||||
LINK_DISCOVERY_SERVICE_INITIAL_DELAY, SCAN_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
SCAN_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||||
} else {
|
} else {
|
||||||
logger.trace("scanningJob active");
|
logger.trace("scanningJob active");
|
||||||
}
|
}
|
||||||
|
@ -232,7 +198,7 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
logger.debug("Stop WeMo device background discovery");
|
logger.debug("Stop WeMo device background discovery");
|
||||||
|
|
||||||
ScheduledFuture<?> job = scanningJob;
|
ScheduledFuture<?> job = scanningJob;
|
||||||
if (job != null && !job.isCancelled()) {
|
if (job != null) {
|
||||||
job.cancel(true);
|
job.cancel(true);
|
||||||
}
|
}
|
||||||
scanningJob = null;
|
scanningJob = null;
|
||||||
|
@ -240,7 +206,7 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUDN() {
|
public String getUDN() {
|
||||||
return (String) this.wemoBridgeHandler.getThing().getConfiguration().get(UDN);
|
return (String) thingHandler.getThing().getConfiguration().get(UDN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -255,7 +221,7 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
public void onStatusChanged(boolean status) {
|
public void onStatusChanged(boolean status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCharacterDataFromElement(Element e) {
|
private static String getCharacterDataFromElement(Element e) {
|
||||||
Node child = e.getFirstChild();
|
Node child = e.getFirstChild();
|
||||||
if (child instanceof CharacterData cd) {
|
if (child instanceof CharacterData cd) {
|
||||||
return cd.getData();
|
return cd.getData();
|
||||||
|
@ -263,7 +229,7 @@ public class WemoLinkDiscoveryService extends AbstractDiscoveryService implement
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WemoLinkScan implements Runnable {
|
private class WemoLinkScan implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
startScan();
|
startScan();
|
||||||
|
|
|
@ -13,17 +13,27 @@
|
||||||
package org.openhab.binding.wemo.internal.handler;
|
package org.openhab.binding.wemo.internal.handler;
|
||||||
|
|
||||||
import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
|
import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
|
||||||
|
import static org.openhab.binding.wemo.internal.WemoUtil.substringBefore;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.wemo.internal.ApiController;
|
||||||
|
import org.openhab.binding.wemo.internal.discovery.WemoLinkDiscoveryService;
|
||||||
|
import org.openhab.binding.wemo.internal.exception.WemoException;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
|
||||||
|
import org.openhab.core.io.transport.upnp.UpnpIOService;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingStatusDetail;
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -40,10 +50,13 @@ public class WemoBridgeHandler extends BaseBridgeHandler {
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BRIDGE);
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BRIDGE);
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(WemoBridgeHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(WemoBridgeHandler.class);
|
||||||
|
private final UpnpIOService service;
|
||||||
|
private final ApiController apiController;
|
||||||
|
|
||||||
public WemoBridgeHandler(Bridge bridge) {
|
public WemoBridgeHandler(final Bridge bridge, final UpnpIOService upnpIOService, final HttpClient httpClient) {
|
||||||
super(bridge);
|
super(bridge);
|
||||||
logger.debug("Creating a WemoBridgeHandler for thing '{}'", getThing().getUID());
|
this.service = upnpIOService;
|
||||||
|
this.apiController = new ApiController(httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -66,4 +79,44 @@ public class WemoBridgeHandler extends BaseBridgeHandler {
|
||||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
// Not needed, all commands are handled in the {@link WemoLightHandler}
|
// Not needed, all commands are handled in the {@link WemoLightHandler}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
|
return Set.of(WemoLinkDiscoveryService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndDevices(UpnpIOParticipant participant) throws InterruptedException, WemoException {
|
||||||
|
String devUDN = "uuid:" + getConfig().get(UDN).toString();
|
||||||
|
logger.trace("getEndDevices for devUDN '{}'", devUDN);
|
||||||
|
|
||||||
|
String soapHeader = "\"urn:Belkin:service:bridge:1#GetEndDevices\"";
|
||||||
|
String content = """
|
||||||
|
<?xml version="1.0"?>\
|
||||||
|
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\
|
||||||
|
<s:Body>\
|
||||||
|
<u:GetEndDevices xmlns:u="urn:Belkin:service:bridge:1">\
|
||||||
|
<DevUDN>\
|
||||||
|
"""
|
||||||
|
+ devUDN + """
|
||||||
|
</DevUDN>\
|
||||||
|
<ReqListType>PAIRED_LIST</ReqListType>\
|
||||||
|
</u:GetEndDevices>\
|
||||||
|
</s:Body>\
|
||||||
|
</s:Envelope>\
|
||||||
|
""";
|
||||||
|
|
||||||
|
URL descriptorURL = service.getDescriptorURL(participant);
|
||||||
|
if (descriptorURL == null) {
|
||||||
|
throw new WemoException("Descriptor URL for participant " + participant.getUDN() + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
String deviceURL = substringBefore(descriptorURL.toString(), "/setup.xml");
|
||||||
|
String wemoURL = deviceURL + "/upnp/control/bridge1";
|
||||||
|
|
||||||
|
String endDeviceResponse = apiController.executeCall(wemoURL, soapHeader, content);
|
||||||
|
|
||||||
|
logger.trace("endDeviceRequest answered '{}'", endDeviceResponse);
|
||||||
|
|
||||||
|
return endDeviceResponse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue