Skip to content

Commit

Permalink
connect only when configuration exits and added on off detection
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Prehn committed Jan 9, 2016
1 parent 433879e commit 333e4c1
Show file tree
Hide file tree
Showing 23 changed files with 274 additions and 448 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,10 @@
*/
package org.openhab.binding.connectsdk.internal;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Base64;
import java.util.List;

import javax.imageio.ImageIO;

import org.openhab.binding.connectsdk.ConnectSDKBindingProvider;
import org.openhab.binding.connectsdk.internal.bridges.ExternalInputControlInput;
import org.openhab.binding.connectsdk.internal.bridges.MediaControlForward;
Expand All @@ -41,6 +34,7 @@
import org.openhab.binding.connectsdk.internal.bridges.VolumeControlUpDown;
import org.openhab.binding.connectsdk.internal.bridges.VolumeControlVolume;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
Expand All @@ -52,8 +46,6 @@
import com.connectsdk.discovery.DiscoveryManagerListener;
import com.connectsdk.service.DeviceService;
import com.connectsdk.service.DeviceService.PairingType;
import com.connectsdk.service.capability.ToastControl;
import com.connectsdk.service.capability.listeners.ResponseListener;
import com.connectsdk.service.command.ServiceCommandError;

/**
Expand All @@ -68,11 +60,12 @@ public class ConnectSDKBinding extends AbstractBinding<ConnectSDKBindingProvider

private static OpenhabConnectSDKPropertyBridge[] bridges = new OpenhabConnectSDKPropertyBridge[] {
new VolumeControlVolume(), new VolumeControlMute(), new VolumeControlUp(), new VolumeControlDown(),
new TVControlChannel(), new TVControlUp(), new TVControlDown(), new VolumeControlUpDown(), new TVControlChannelName(), new TVControlProgram(),
new PowerControlPower(), new ExternalInputControlInput(),
new MediaControlForward(), new MediaControlPause(), new MediaControlPlay(), new MediaControlRewind(), new MediaControlStop(), new MediaControlPlayState(),
new TVControlChannel(), new TVControlUp(), new TVControlDown(), new VolumeControlUpDown(),
new TVControlChannelName(), new TVControlProgram(), new PowerControlPower(),
new ExternalInputControlInput(), new MediaControlForward(), new MediaControlPause(),
new MediaControlPlay(), new MediaControlRewind(), new MediaControlStop(), new MediaControlPlayState(),
new ToastControlToast()

};

@Override
Expand Down Expand Up @@ -180,11 +173,10 @@ public void onPairingRequired(ConnectableDevice device, DeviceService service, P
@Override
public void onDeviceReady(ConnectableDevice device) {
logger.info("Device ready: {}", device);
refreshSubscriptions(device);
for (OpenhabConnectSDKPropertyBridge b : bridges) {
b.removeAnySubscription(device); // ensure all old subscriptions are cleaned out
b.addSubscription(device, providers, eventPublisher);
b.onDeviceReady(device, providers, eventPublisher);
}
sendHelloWorld(device);
}

@Override
Expand All @@ -202,31 +194,64 @@ public void onConnectionFailed(ConnectableDevice device, ServiceCommandError err
@Override
public void onCapabilityUpdated(ConnectableDevice device, List<String> added, List<String> removed) {
logger.debug("Capabilities updated: {} - added: {} - removed: {}", device, added, removed);
device.connect(); // ensure all services are connected, can be called even if some of the services are already connected
for (OpenhabConnectSDKPropertyBridge b : bridges) {
b.removeAnySubscription(device); // ensure all old subscriptions are cleaned out
b.addSubscription(device, providers, eventPublisher);
}
refreshSubscriptions(device);
}
});
device.connect(); // ensure all services are connected, can be called even if some of the services are already connected
//logger.debug("Capabilities: " + device.getFriendlyName() + " : " + device.getCapabilities().toString());

connectIfAnyItemIsConfiguredFor(device);
// logger.debug("Capabilities: " + device.getFriendlyName() + " : " + device.getCapabilities().toString());
}

private void connectIfAnyItemIsConfiguredFor(final ConnectableDevice device) {
for (ConnectSDKBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
try {
if (device.getIpAddress().equals(
InetAddress.getByName(provider.getDeviceForItem(itemName)).getHostAddress())) {
device.connect();
return;
}
} catch (UnknownHostException e) {
logger.error("Failed to resolve {} to IP address. Skipping update on item {}.", device, itemName);
}
}
}
}

@Override
public void onDeviceUpdated(DiscoveryManager manager, ConnectableDevice device) {
logger.info("Device updated: {}", device);
public void bindingChanged(BindingProvider provider, String itemName) {
super.bindingChanged(provider, itemName);
logger.debug("Item {} binding changed, refresh all listeners", itemName);
/*
* Every time the item model reloads this method will be called once for every item bound to this binding.
* Recreating all subscriptions each time is not elegant, but there is no callback that gets called once after a
* model change. Tested allBindingsChanged, but that does not get called after editing the items file.
* Performance impact seems acceptable, as model reloads do not happen all that often. If we find a better way,
* let's improve it.
*/
for (ConnectableDevice device : discoveryManager.getCompatibleDevices().values()) {
refreshSubscriptions(device);
}

}

private void refreshSubscriptions(ConnectableDevice device) {
for (OpenhabConnectSDKPropertyBridge b : bridges) {
b.removeAnySubscription(device);
b.addSubscription(device, providers, eventPublisher);
b.refreshSubscription(device, providers, eventPublisher);
}
device.connect(); // ensure all services are connected, can be called even if some of the services are already connected
}

@Override
public void onDeviceUpdated(DiscoveryManager manager, ConnectableDevice device) {
logger.info("Device updated: {}", device);
refreshSubscriptions(device);
}

@Override
public void onDeviceRemoved(DiscoveryManager manager, ConnectableDevice device) {
logger.info("Device removed: {}", device);
for (OpenhabConnectSDKPropertyBridge b : bridges) {
b.onDeviceRemoved(device, providers, eventPublisher);
b.removeAnySubscription(device);
}
// no need to call device.disconnect this is done by connect sdk framework for us
Expand All @@ -237,34 +262,4 @@ public void onDiscoveryFailed(DiscoveryManager manager, ServiceCommandError erro
logger.warn("Discovery failed: {}", error.getMessage());
}

private void sendHelloWorld(ConnectableDevice device) {
if (device.hasCapability(ToastControl.Show_Toast)) {
try {
BufferedImage bi = ImageIO.read(getClass().getResource("/openhab-logo-square.png"));

ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStream b64 = Base64.getEncoder().wrap(os);
ImageIO.write(bi, "png", b64);
String result = os.toString("UTF-8");

device.getCapability(ToastControl.class).showToast("Welcome to Openhab!", result, "png",
new ResponseListener<Object>() {

@Override
public void onSuccess(Object object) {
logger.debug("toast: {}", object);
}

@Override
public void onError(ServiceCommandError error) {
logger.error(error.getMessage());
}
});

} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
package org.openhab.binding.connectsdk.internal;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import android.content.BroadcastReceiver;
//import android.content.ComponentName;
//import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
//import android.content.ComponentName;
//import android.content.ContentResolver;
//import android.content.IntentSender;
//import android.content.ServiceConnection;
//import android.content.SharedPreferences;
//import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
//import android.content.pm.FeatureInfo;
//import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
//import android.content.pm.PermissionGroupInfo;
//import android.content.pm.PermissionInfo;
//import android.content.pm.ProviderInfo;
Expand All @@ -26,24 +37,13 @@
//import android.database.DatabaseErrorHandler;
//import android.database.sqlite.SQLiteDatabase;
//import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
//import android.net.Uri;
import android.net.wifi.WifiManager;
//import android.os.Bundle;
//import android.os.Handler;
//import android.os.Looper;
import android.view.Display;
//import android.view.View;
//import android.view.ViewGroup;
//import android.view.WindowManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.openhab.binding.connectsdk.internal.bridges;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -26,47 +29,77 @@ private synchronized Map<String, ServiceSubscription<T>> getSubscriptions() {
}
return subscriptions;
}

@Override
public void onDeviceReady(final ConnectableDevice device, final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher) {

}

@Override
public void onDeviceRemoved(final ConnectableDevice device, final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher) {
}

@Override
public final void addSubscription(final ConnectableDevice device,
final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher) {
ServiceSubscription<T> listener = getSubscription(device, providers, eventPublisher);
if (listener != null) {
logger.debug("Subscribed {}:{} listener on IP: {}", getItemClass(), getItemProperty(),
device.getIpAddress());

// not sure if this is required, trying to find performance leak
if(subscriptions != null && subscriptions.containsKey(device.getIpAddress())){
throw new IllegalStateException("A listener for device "+device.getIpAddress()+" already exists for this Bridge: "+ this.getClass().getName()+ " Call removeAnySubscription first.");
public final synchronized void refreshSubscription(final ConnectableDevice device,
final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher) {
removeAnySubscription(device); // ensure all old subscriptions are cleaned out
Collection<String> matchingItemNames = findMatchingItemNames(device, providers);
if (!matchingItemNames.isEmpty()) { // only listen if least one item is configured
ServiceSubscription<T> listener = getSubscription(device, matchingItemNames, eventPublisher);
if (listener != null) {
logger.debug("Subscribed {}:{} listener on IP: {}", getItemClass(), getItemProperty(),
device.getIpAddress());
getSubscriptions().put(device.getIpAddress(), listener);
}
}
}

protected Collection<String> findMatchingItemNames(ConnectableDevice device,
Collection<ConnectSDKBindingProvider> providers) {
Collection<String> matchingItemNames = new ArrayList<String>();

for (ConnectSDKBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
try {
if (matchClassAndProperty(provider.getClassForItem(itemName), provider.getPropertyForItem(itemName))
&& device.getIpAddress().equals(
InetAddress.getByName(provider.getDeviceForItem(itemName)).getHostAddress())) {
matchingItemNames.add(itemName);
}
} catch (UnknownHostException e) {
logger.error("Failed to resolve {} to IP address. Skipping update on item {}.", device, itemName);
}
}

getSubscriptions().put(device.getIpAddress(), listener);

}
return matchingItemNames;
}

/**
* Creates a subscription instance for this device. This may return <code>null</code> if no subscription is possible or required.
* Creates a subscription instance for this device. This may return <code>null</code> if no subscription is possible
* or required.
*
* @param device
* @param providers
* @param eventPublisher
* @param device device to which state changes to subscribe to
* @param itemNames item's names that shall be update on device status change. Only items that match the device ip, item property and item class must be provided.
* @param eventPublisher
* @return instance or <code>null</code> if no subscription is possible or required
*/
protected abstract ServiceSubscription<T> getSubscription(final ConnectableDevice device,
final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher);
protected ServiceSubscription<T> getSubscription(final ConnectableDevice device, final Collection<String> itemNames, final EventPublisher eventPublisher) {
return null;
}

@Override
public final synchronized void removeAnySubscription(final ConnectableDevice device) { // here
if (subscriptions != null) { // only if subscriptions was initialized (lazy loading)
ServiceSubscription<T> l = subscriptions.remove(device.getIpAddress());
if (l != null) {
l.unsubscribe();
// not sure if this is required, trying to find performance leak
if(l instanceof URLServiceSubscription) {
((URLServiceSubscription<?>)l).removeListeners();

// not sure if this is required, trying to find performance leak
if (l instanceof URLServiceSubscription) {
((URLServiceSubscription<?>) l).removeListeners();
}

logger.debug("Unsubscribed {}:{} listener on IP: {}", getItemClass(), getItemProperty(),
device.getIpAddress());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.openhab.binding.connectsdk.internal.bridges;

import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;

import org.openhab.binding.connectsdk.ConnectSDKBindingProvider;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -15,7 +12,6 @@
import com.connectsdk.service.capability.ExternalInputControl;
import com.connectsdk.service.capability.ExternalInputControl.ExternalInputListListener;
import com.connectsdk.service.command.ServiceCommandError;
import com.connectsdk.service.command.ServiceSubscription;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;

Expand All @@ -40,7 +36,6 @@ private ExternalInputControl getControl(final ConnectableDevice device) {
public void onReceiveCommand(final ConnectableDevice d, final String clazz, final String property, Command command) {
if (matchClassAndProperty(clazz, property)
&& d.hasCapabilities(ExternalInputControl.List, ExternalInputControl.Set)) {

final String value = command.toString();
final ExternalInputControl control = getControl(d);
control.getExternalInputList(new ExternalInputListListener() {
Expand Down Expand Up @@ -74,10 +69,4 @@ public boolean apply(ExternalInputInfo c) {

}

@Override
protected ServiceSubscription<ExternalInputListListener> getSubscription(final ConnectableDevice device,
final Collection<ConnectSDKBindingProvider> providers, final EventPublisher eventPublisher) {
return null;
}

}
Loading

0 comments on commit 333e4c1

Please sign in to comment.