Skip to content

Commit

Permalink
[lgwebos] Detection of power on/off with UPnP
Browse files Browse the repository at this point in the history
Also fix openhab#7119

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo committed Mar 8, 2020
1 parent ea1e4d9 commit 2675a80
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ public Set<ThingTypeUID> 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
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 + "]";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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<String, ChannelHandler> handlers = new HashMap<>();
handlers.put(CHANNEL_VOLUME, new VolumeControlVolume());
Expand All @@ -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();
Expand All @@ -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();
}

Expand Down Expand Up @@ -328,4 +361,75 @@ private void refreshChannelSubscription(ChannelUID channelUID) {
public Collection<Class<? extends ThingHandlerService>> 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<String, Object> 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<String, String> 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<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
@Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
<label>Access Key</label>
<description>Key exchanged with TV after pairing.</description>
</parameter>
<parameter name="deviceId" type="text" required="false">
<label>UPnP Device Identifier</label>
<description>UPnP device identifier.</description>
</parameter>
</config-description>

</config-description:config-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
</channels>

<properties>
<property name="deviceId" />
<property name="lastConnected" />
<property name="modelName" />
<property name="manufacturer" />
Expand Down

0 comments on commit 2675a80

Please sign in to comment.