From 2675a80a5a3a9ff7c5f58c3c6385a1b7eafef00f Mon Sep 17 00:00:00 2001 From: lolodomo Date: Sat, 7 Mar 2020 08:19:28 +0100 Subject: [PATCH] [lgwebos] Detection of power on/off with UPnP Also fix #7119 Signed-off-by: Laurent Garnier --- .../internal/LGWebOSBindingConstants.java | 2 +- .../internal/LGWebOSHandlerFactory.java | 12 +- .../LGWebOSUpnpDiscoveryParticipant.java | 8 +- .../discovery/LGWebOSUpnpDiscoverySearch.java | 89 --------------- .../handler/LGWebOSConfiguration.java | 10 +- .../internal/handler/LGWebOSHandler.java | 108 +++++++++++++++++- .../main/resources/ESH-INF/config/config.xml | 4 + .../resources/ESH-INF/thing/thing-types.xml | 1 - 8 files changed, 135 insertions(+), 99 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/LGWebOSBindingConstants.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java index d00ae5bdf2078..f7b6fd686a236 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java +++ b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java @@ -43,12 +43,12 @@ public class LGWebOSBindingConstants { */ public static final String CONFIG_HOST = "host"; public static final String CONFIG_KEY = "key"; + public static final String CONFIG_DEVICE_ID = "deviceId"; /* * Property names must match property names in * - property names in ESH-INF/thing/thing-types.xml */ - public static final String PROPERTY_DEVICE_ID = "deviceId"; public static final String PROPERTY_DEVICE_OS = "deviceOS"; public static final String PROPERTY_DEVICE_OS_VERSION = "deviceOSVersion"; public static final String PROPERTY_DEVICE_OS_RELEASE_VERSION = "deviceOSReleaseVersion"; 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 cf76158c1868a..df9cb08958aac 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,12 +17,14 @@ 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; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; import org.eclipse.smarthome.io.net.http.WebSocketFactory; +import org.eclipse.smarthome.io.transport.upnp.UpnpIOService; import org.openhab.binding.lgwebos.internal.handler.LGWebOSHandler; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; @@ -43,14 +45,20 @@ public class LGWebOSHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(LGWebOSHandlerFactory.class); private final WebSocketClient webSocketClient; + private final UpnpIOService upnpIOService; + private final DiscoveryServiceRegistry discoveryServiceRegistry; @Activate - public LGWebOSHandlerFactory(final @Reference WebSocketFactory webSocketFactory) { + public LGWebOSHandlerFactory(final @Reference WebSocketFactory webSocketFactory, + final @Reference UpnpIOService upnpIOService, + final @Reference DiscoveryServiceRegistry discoveryServiceRegistry) { /* * Cannot use openHAB's shared web socket client (webSocketFactory.getCommonWebSocketClient()) as we have to * change client settings. */ this.webSocketClient = webSocketFactory.createWebSocketClient("lgwebos"); + this.upnpIOService = upnpIOService; + this.discoveryServiceRegistry = discoveryServiceRegistry; } @Override @@ -62,7 +70,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); + return new LGWebOSHandler(thing, webSocketClient, upnpIOService, discoveryServiceRegistry); } 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..3f5b0bb77a2b6 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 @@ -55,12 +55,14 @@ public Set getSupportedThingTypeUIDs() { } return DiscoveryResultBuilder.create(thingUID).withLabel(device.getDetails().getFriendlyName()) - .withProperty(PROPERTY_DEVICE_ID, device.getIdentity().getUdn().getIdentifierString()) + .withProperty(CONFIG_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().getModelDetails().getModelName() + " " + + device.getDetails().getModelDetails().getModelNumber()) .withProperty(PROPERTY_MANUFACTURER, device.getDetails().getManufacturerDetails().getManufacturer()) - .withRepresentationProperty(PROPERTY_DEVICE_ID).withThingType(THING_TYPE_WEBOSTV).build(); + .withRepresentationProperty(CONFIG_DEVICE_ID).withThingType(THING_TYPE_WEBOSTV).build(); } @Override 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..d84ff1773a5c3 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 @@ -28,6 +28,8 @@ public class LGWebOSConfiguration { int port = 3000; // 3001 for TLS @Nullable String key; // name has to match LGWebOSBindingConstants.CONFIG_KEY + @Nullable + String deviceId; // name has to match LGWebOSBindingConstants.CONFIG_DEVICE_ID public String getHost() { String h = host; @@ -43,9 +45,15 @@ public int getPort() { return port; } + public String getDeviceId() { + String id = deviceId; + return id == null ? "" : id; + } + @Override public String toString() { - return "WebOSConfiguration [host=" + host + ", port=" + port + ", key.length=" + getKey().length() + "]"; + return "WebOSConfiguration [host=" + host + ", port=" + port + ", key.length=" + getKey().length() + + ", deviceId=" + deviceId + "]"; } } 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 a8a1dfed63c2d..00063af866056 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 @@ -25,14 +25,23 @@ 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.library.types.OnOffType; 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; import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.io.transport.upnp.UpnpIOParticipant; +import org.eclipse.smarthome.io.transport.upnp.UpnpIOService; import org.openhab.binding.lgwebos.action.LGWebOSActions; import org.openhab.binding.lgwebos.internal.ChannelHandler; import org.openhab.binding.lgwebos.internal.LGWebOSBindingConstants; @@ -59,7 +68,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, UpnpIOParticipant, DiscoveryListener { /* * constants for device polling @@ -81,14 +91,21 @@ public class LGWebOSHandler extends BaseThingHandler implements LGWebOSTVSocket. private @Nullable LGWebOSTVSocket socket; private final WebSocketClient webSocketClient; + private final UpnpIOService upnpIOService; + + private final DiscoveryServiceRegistry discoveryServiceRegistry; + private @Nullable ScheduledFuture reconnectJob; private @Nullable ScheduledFuture keepAliveJob; private @Nullable LGWebOSConfiguration config; - public LGWebOSHandler(Thing thing, WebSocketClient webSocketClient) { + public LGWebOSHandler(Thing thing, WebSocketClient webSocketClient, UpnpIOService upnpIOService, + final DiscoveryServiceRegistry discoveryServiceRegistry) { super(thing); this.webSocketClient = webSocketClient; + this.upnpIOService = upnpIOService; + this.discoveryServiceRegistry = discoveryServiceRegistry; Map handlers = new HashMap<>(); handlers.put(CHANNEL_VOLUME, new VolumeControlVolume()); @@ -115,6 +132,7 @@ private LGWebOSConfiguration getLGWebOSConfig() { @Override public void initialize() { + logger.debug("Initializing handler for thing {}", getThing().getUID()); LGWebOSConfiguration c = getLGWebOSConfig(); logger.trace("Handler initialized with config {}", c); String host = c.getHost(); @@ -127,20 +145,35 @@ public void initialize() { s.setListener(this); socket = s; + if (!c.getDeviceId().isEmpty()) { + logger.debug("UPnP registerParticipant (UDN={})", getUDN()); + upnpIOService.registerParticipant(this); + } + + discoveryServiceRegistry.addDiscoveryListener(this); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "TV is off"); + startReconnectJob(); } @Override public void dispose() { + logger.debug("Disposing handler for thing {}", getThing().getUID()); stopKeepAliveJob(); stopReconnectJob(); + discoveryServiceRegistry.removeDiscoveryListener(this); + + upnpIOService.unregisterParticipant(this); + LGWebOSTVSocket s = socket; if (s != null) { s.setListener(null); scheduler.execute(() -> s.disconnect()); // dispose should be none-blocking } socket = null; + config = null; // ensure config gets actually refreshed during re-initialization super.dispose(); } @@ -328,4 +361,75 @@ private void refreshChannelSubscription(ChannelUID channelUID) { public Collection> getServices() { return Collections.singleton(LGWebOSActions.class); } + + @Override + public String getUDN() { + return getLGWebOSConfig().getDeviceId(); + } + + @Override + public void onStatusChanged(boolean status) { + logger.debug("onStatusChanged status={}", status); + postUpdate(CHANNEL_POWER, status ? OnOffType.ON : OnOffType.OFF); + } + + @Override + public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) { + } + + @Override + public void onServiceSubscribed(@Nullable String service, boolean succeeded) { + } + + @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 + + // Update the thing properties from the discovery result + Map map = editProperties(); + String manufacturer = (String) properties.get(PROPERTY_MANUFACTURER); + if (manufacturer != null && !manufacturer.isEmpty() + && !manufacturer.equals(map.get(PROPERTY_MANUFACTURER))) { + logger.debug("Update property manufacturer with {}", manufacturer); + map.put(PROPERTY_MANUFACTURER, manufacturer); + } + String modelName = (String) properties.get(PROPERTY_MODEL_NAME); + if (modelName != null && !modelName.isEmpty() && !modelName.equals(map.get(PROPERTY_MODEL_NAME))) { + logger.debug("Update property modelName with {}", modelName); + map.put(PROPERTY_MODEL_NAME, modelName); + } + updateProperties(map); + + String deviceId = (String) properties.get(CONFIG_DEVICE_ID); + if (deviceId != null && !deviceId.isEmpty() && !deviceId.equals(getLGWebOSConfig().getDeviceId())) { + logger.debug("Use UDN {} from discovery", deviceId); + getLGWebOSConfig().deviceId = deviceId; + + // persist the configuration change + Configuration configuration = editConfiguration(); + configuration.put(LGWebOSBindingConstants.CONFIG_DEVICE_ID, deviceId); + updateConfiguration(configuration); + + logger.debug("UPnP registerParticipant (UDN={})", getUDN()); + upnpIOService.unregisterParticipant(this); + upnpIOService.registerParticipant(this); + } + } + } + } + + @Override + public void thingRemoved(DiscoveryService source, ThingUID thingUID) { + } + + @Override + public @Nullable Collection removeOlderResults(DiscoveryService source, long timestamp, + @Nullable Collection thingTypeUIDs, @Nullable ThingUID bridgeUID) { + return null; + } + } diff --git a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml index d24e98399def6..6655abc0da7d1 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml @@ -15,6 +15,10 @@ Key exchanged with TV after pairing. + + + UPnP device identifier. + diff --git a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml index 3fd07c311839e..965c56a713a9b 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml @@ -22,7 +22,6 @@ -