From c884e1b576b663d9128ef216760ebe04d54fda48 Mon Sep 17 00:00:00 2001 From: lolodomo Date: Fri, 10 Apr 2020 21:25:16 +0200 Subject: [PATCH] [lgwebos] Detection of power on/off with UPnP Also fix #7119 Signed-off-by: Laurent Garnier --- .../internal/LGWebOSHandlerFactory.java | 7 +- .../LGWebOSUpnpDiscoveryParticipant.java | 13 ++- .../discovery/LGWebOSUpnpDiscoverySearch.java | 89 ------------------- .../handler/LGWebOSConfiguration.java | 3 +- .../internal/handler/LGWebOSHandler.java | 69 +++++++++++++- 5 files changed, 84 insertions(+), 97 deletions(-) delete mode 100644 bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoverySearch.java diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java index e5cbd94b32276..ffae01ac97045 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.smarthome.config.discovery.DiscoveryServiceRegistry; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; @@ -43,17 +44,19 @@ public class LGWebOSHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(LGWebOSHandlerFactory.class); private final WebSocketClient webSocketClient; - + private final DiscoveryServiceRegistry discoveryServiceRegistry; private final LGWebOSStateDescriptionOptionProvider stateDescriptionProvider; @Activate public LGWebOSHandlerFactory(final @Reference WebSocketFactory webSocketFactory, + final @Reference DiscoveryServiceRegistry discoveryServiceRegistry, final @Reference LGWebOSStateDescriptionOptionProvider stateDescriptionProvider) { /* * Cannot use openHAB's shared web socket client (webSocketFactory.getCommonWebSocketClient()) as we have to * change client settings. */ this.webSocketClient = webSocketFactory.createWebSocketClient("lgwebos"); + this.discoveryServiceRegistry = discoveryServiceRegistry; this.stateDescriptionProvider = stateDescriptionProvider; } @@ -66,7 +69,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(THING_TYPE_WEBOSTV)) { - return new LGWebOSHandler(thing, webSocketClient, stateDescriptionProvider); + return new LGWebOSHandler(thing, webSocketClient, discoveryServiceRegistry, stateDescriptionProvider); } return null; } diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoveryParticipant.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoveryParticipant.java index bd09c08f0204d..5b2807be63293 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoveryParticipant.java @@ -24,6 +24,7 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.jupnp.model.meta.RemoteDevice; +import org.jupnp.model.types.DeviceType; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +42,9 @@ public class LGWebOSUpnpDiscoveryParticipant implements UpnpDiscoveryParticipant private final Logger logger = LoggerFactory.getLogger(LGWebOSUpnpDiscoveryParticipant.class); + private static final DeviceType UPNP_MEDIARENDERER_DEVICE_TYPE = new DeviceType("schemas-upnp-org", "MediaRenderer", + 1); + @Override public Set getSupportedThingTypeUIDs() { return SUPPORTED_THING_TYPES_UIDS; @@ -58,7 +62,7 @@ public Set getSupportedThingTypeUIDs() { .withProperty(PROPERTY_DEVICE_ID, device.getIdentity().getUdn().getIdentifierString()) .withProperty(CONFIG_HOST, device.getIdentity().getDescriptorURL().getHost()) .withLabel(device.getDetails().getFriendlyName()) - .withProperty(PROPERTY_MODEL_NAME, device.getDetails().getModelDetails().getModelName()) + .withProperty(PROPERTY_MODEL_NAME, device.getDetails().getFriendlyName()) .withProperty(PROPERTY_MANUFACTURER, device.getDetails().getManufacturerDetails().getManufacturer()) .withRepresentationProperty(PROPERTY_DEVICE_ID).withThingType(THING_TYPE_WEBOSTV).build(); } @@ -66,7 +70,12 @@ public Set getSupportedThingTypeUIDs() { @Override public @Nullable ThingUID getThingUID(RemoteDevice device) { logger.trace("Discovered remote device {}", device); - if (device.findService(UPNP_SERVICE_TYPE) != null) { + if (UPNP_MEDIARENDERER_DEVICE_TYPE.equals(device.getType()) + && device.getDetails().getManufacturerDetails().getManufacturer() != null + && device.getDetails().getManufacturerDetails().getManufacturer().toUpperCase() + .contains("LG ELECTRONICS") + && device.getDetails().getModelDetails().getModelDescription() != null + && device.getDetails().getModelDetails().getModelDescription().toUpperCase().contains("WEBOSTV")) { logger.debug("Found LG WebOS TV: {}", device); return new ThingUID(THING_TYPE_WEBOSTV, device.getIdentity().getUdn().getIdentifierString()); } diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoverySearch.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoverySearch.java deleted file mode 100644 index 8defd27d3f54b..0000000000000 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSUpnpDiscoverySearch.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * 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.lgwebos.internal.discovery; - -import static org.openhab.binding.lgwebos.internal.LGWebOSBindingConstants.*; - -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; -import org.eclipse.smarthome.config.discovery.DiscoveryService; -import org.jupnp.UpnpService; -import org.jupnp.model.message.header.ServiceTypeHeader; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * WebOS TV does not automatically show up by the normal background scans, as it does not respond to openHABs generic - * search request. A special ssdp search request for lge-com:service:webos-second-screen:1 is required. - * This component sends this ssdp search request. - * - * @author Sebastian Prehn - Initial contribution - * - */ -@NonNullByDefault -@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.lgwebos") -public class LGWebOSUpnpDiscoverySearch extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(LGWebOSUpnpDiscoverySearch.class); - - private static final int DISCOVERY_TIMEOUT_SECONDS = 10; - private static final int SEARCH_INTERVAL_SECONDS = 10; - private static final int SEARCH_START_UP_DELAY_SECONDS = 0; - - private final UpnpService upnpService; - - private @Nullable ScheduledFuture searchJob; - - @Activate - public LGWebOSUpnpDiscoverySearch(final @Reference UpnpService upnpService) { - super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS, true); - this.upnpService = upnpService; - } - - private void search() { - logger.trace("Sending Upnp Search Request for {}", UPNP_SERVICE_TYPE); - upnpService.getControlPoint().search(new ServiceTypeHeader(UPNP_SERVICE_TYPE)); - } - - @Override - protected void startBackgroundDiscovery() { - logger.debug("Start LGWebOS device background discovery"); - ScheduledFuture job = searchJob; - if (job == null || job.isCancelled()) { - searchJob = scheduler.scheduleWithFixedDelay(() -> search(), SEARCH_START_UP_DELAY_SECONDS, - SEARCH_INTERVAL_SECONDS, TimeUnit.SECONDS); - } - } - - @Override - protected void stopBackgroundDiscovery() { - logger.debug("Stop LGWebOS device background discovery"); - ScheduledFuture job = searchJob; - if (job != null && !job.isCancelled()) { - job.cancel(false); - searchJob = null; - } - } - - @Override - protected void startScan() { - search(); - } -} diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSConfiguration.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSConfiguration.java index 536e2e1d78ce4..ad982a1396022 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSConfiguration.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSConfiguration.java @@ -45,7 +45,8 @@ public int getPort() { @Override public String toString() { - return "WebOSConfiguration [host=" + host + ", port=" + port + ", key.length=" + getKey().length() + "]"; + return "WebOSConfiguration [host=" + getHost() + ", port=" + getPort() + ", key.length=" + getKey().length() + + "]"; } } diff --git a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java index 91051087151a1..780cb1255b52d 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java @@ -26,10 +26,16 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.config.discovery.DiscoveryListener; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryServiceRegistry; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.types.Command; @@ -61,7 +67,8 @@ * @author Sebastian Prehn - initial contribution */ @NonNullByDefault -public class LGWebOSHandler extends BaseThingHandler implements LGWebOSTVSocket.ConfigProvider, WebOSTVSocketListener { +public class LGWebOSHandler extends BaseThingHandler + implements LGWebOSTVSocket.ConfigProvider, WebOSTVSocketListener, DiscoveryListener { /* * constants for device polling @@ -82,10 +89,9 @@ public class LGWebOSHandler extends BaseThingHandler implements LGWebOSTVSocket. private final Map channelHandlers; private final LauncherApplication appLauncher = new LauncherApplication(); - private @Nullable LGWebOSTVSocket socket; private final WebSocketClient webSocketClient; - + private final DiscoveryServiceRegistry discoveryServiceRegistry; private final LGWebOSStateDescriptionOptionProvider stateDescriptionProvider; private @Nullable ScheduledFuture reconnectJob; @@ -93,10 +99,14 @@ public class LGWebOSHandler extends BaseThingHandler implements LGWebOSTVSocket. private @Nullable LGWebOSConfiguration config; + private String udn = ""; + public LGWebOSHandler(Thing thing, WebSocketClient webSocketClient, + DiscoveryServiceRegistry discoveryServiceRegistry, LGWebOSStateDescriptionOptionProvider stateDescriptionProvider) { super(thing); this.webSocketClient = webSocketClient; + this.discoveryServiceRegistry = discoveryServiceRegistry; this.stateDescriptionProvider = stateDescriptionProvider; Map handlers = new HashMap<>(); @@ -138,6 +148,8 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "TV is off"); + discoveryServiceRegistry.addDiscoveryListener(this); + startReconnectJob(); } @@ -147,6 +159,8 @@ public void dispose() { stopKeepAliveJob(); stopReconnectJob(); + discoveryServiceRegistry.removeDiscoveryListener(this); + LGWebOSTVSocket s = socket; if (s != null) { s.setListener(null); @@ -322,6 +336,7 @@ public void setOptions(String channelId, List options) { } public void postUpdate(String channelId, State state) { + logger.debug("postUpdate channelId {} state {}", channelId, state); if (isLinked(channelId)) { updateState(channelId, state); } @@ -336,4 +351,52 @@ public void postUpdate(String channelId, State state) { public Collection> getServices() { return Collections.singleton(LGWebOSActions.class); } + + @Override + public void thingDiscovered(DiscoveryService source, DiscoveryResult result) { + if (THING_TYPE_WEBOSTV.equals(result.getThingTypeUID())) { + Map properties = result.getProperties(); + String host = (String) properties.get(CONFIG_HOST); + if (host != null && host.equals(getLGWebOSConfig().getHost())) { + // Thing matching the discovery + logger.debug("thingDiscovered: discovery with UID {} matching thing {}", result.getThingUID(), + getThing().getUID()); + + channelHandlers.get(CHANNEL_POWER).onDeviceReady(CHANNEL_POWER, this); + + // Update the thing properties from the discovery result + Map map = new HashMap<>(); + String deviceId = (String) properties.get(PROPERTY_DEVICE_ID); + if (deviceId != null && !deviceId.isEmpty()) { + udn = deviceId; + logger.debug("UDN {} considered", udn); + map.put(PROPERTY_DEVICE_ID, deviceId); + } + String manufacturer = (String) properties.get(PROPERTY_MANUFACTURER); + if (manufacturer != null && !manufacturer.isEmpty()) { + map.put(PROPERTY_MANUFACTURER, manufacturer); + } + String modelName = (String) properties.get(PROPERTY_MODEL_NAME); + if (modelName != null && !modelName.isEmpty()) { + map.put(PROPERTY_MODEL_NAME, modelName); + } + storeProperties(map); + } + } + } + + @Override + public void thingRemoved(DiscoveryService source, ThingUID thingUID) { + if (!udn.isEmpty() && thingUID.getId().equals(udn)) { + logger.debug("thingRemoved: discovery with UID {} matching thing {}", thingUID, getThing().getUID()); + channelHandlers.get(CHANNEL_POWER).onDeviceRemoved(CHANNEL_POWER, this); + } + } + + @Override + public @Nullable Collection removeOlderResults(DiscoveryService source, long timestamp, + @Nullable Collection thingTypeUIDs, @Nullable ThingUID bridgeUID) { + return null; + } + }