Package org.openhab.binding.bluetooth.internal

Source Code of org.openhab.binding.bluetooth.internal.BTDeviceDiscoveryService

/**
* Copyright (c) 2010-2014, openHAB.org and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.bluetooth.internal;

import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Set;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;

import org.openhab.binding.bluetooth.BluetoothEventHandler;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;

/**
* This class is registered as a service and depends on {@link BluetoothEventHandler}s to process the results
* of the device discovery.
*
* When this service is activated, the bluetooth discovery runs as a separate thread and retriggers a device discovery
* in a configurable frequency.
*
* The device discovery is not done, if there are no active event handlers registered.
* @author Kai Kreuzer
* @since 0.3.0
*
*/
public class BTDeviceDiscoveryService extends Thread implements ManagedService {
 
  private static final Logger logger = LoggerFactory.getLogger(BTDeviceDiscoveryService.class);

  // the namespace for configuration entries
  private static final String SERVICE_PID = "bluetooth";
 
  // allows the user to define a refresh rate
  private static final String REFRESH_RATE = "refresh";

  /** the frequency of how often a new Bluetooth device scan is triggered in seconds */
  public int refreshRate = 30;
 
  // a set to collect all devices currently in range
  private Set<RemoteDevice> newDevices = new HashSet<RemoteDevice>();
 
  // a copy of the last discovery result to be able to see when devices disappear
  private Set<RemoteDevice> oldDevices = newDevices;
 
  final private Set<BluetoothEventHandler> eventHandler = Collections.synchronizedSet(new HashSet<BluetoothEventHandler>());
 
  // for interrupting this thread
  private boolean interrupted = false;

  public BTDeviceDiscoveryService() {
    super("Bluetooth Discovery Service");
  }
 
  public void activate() {
    // start the separate thread for listening to the bluetooth device
    start();
  }
 
  public void deactivate() {
    // try to end the thread smoothly
    setInterrupted(true);
  }
 
  public void addBluetoothEventHandler(BluetoothEventHandler handler) {
    eventHandler.add(handler);
  }

  public void removeBluetoothEventHandler(BluetoothEventHandler handler) {
    eventHandler.remove(handler);
  }

  @SuppressWarnings("rawtypes")
  public void updated(Dictionary config) throws ConfigurationException {
    if(config!=null) {
      String value = (String) config.get(REFRESH_RATE);
      try {
        int newRefreshRate = Integer.parseInt(value);
        refreshRate = newRefreshRate;
      } catch(NumberFormatException e) {
        logger.warn("Invalid configuration value '{}' for parameter '" + SERVICE_PID + ":" + REFRESH_RATE+ "'", value);
      }
    }
  }

  public void setInterrupted(boolean interrupted) {
    this.interrupted = interrupted;
  }

  @Override
  public void run() {
    try {
      LocalDevice localDevice = LocalDevice.getLocalDevice();
      logger.debug("Initializing local bluetooth device ({}, {})", localDevice.getBluetoothAddress(), localDevice.getFriendlyName());
      DiscoveryAgent agent = localDevice.getDiscoveryAgent();
      final Object inquiryCompletedEvent = new Object();

      // this is the call back for the bluetooth driver
      DiscoveryListener listener = new DiscoveryListener() {
       
        public void deviceDiscovered(RemoteDevice btDevice,
            DeviceClass cod) {
          if (!newDevices.contains(btDevice)) {
            newDevices.add(btDevice);
            if (!oldDevices.contains(btDevice)) {             
              BluetoothDevice device = toBluetoothDevice(btDevice);
              logger.debug("Device discovered: {}", device.toString());
              for(BluetoothEventHandler handler : eventHandler) {
                handler.handleDeviceInRange(device);
              }
            }
          }
        }

        public void inquiryCompleted(int discType) {
          // check if any device has disappeared
          for (RemoteDevice btDevice : oldDevices) {
            if (newDevices.contains(btDevice))
              continue;
            BluetoothDevice device = toBluetoothDevice(btDevice);
            logger.debug("Device out of range: {}", device.toString());
            for(BluetoothEventHandler handler : eventHandler) {
              handler.handleDeviceOutOfRange(device);
            }
          }

          oldDevices = new HashSet<RemoteDevice>(newDevices);
         
          // we now pass the list of all devices in range to the event handlers
          Iterable<BluetoothDevice> devices = Iterables.transform(newDevices, new Function<RemoteDevice, BluetoothDevice>() {
            public BluetoothDevice apply(RemoteDevice from) {
              return toBluetoothDevice(from);
            }
          });
          for(BluetoothEventHandler handler : eventHandler) {
            handler.handleAllDevicesInRange(devices);
          }
         
          newDevices.clear();
          synchronized (inquiryCompletedEvent) {
            // tell the main thread that we are done
            inquiryCompletedEvent.notifyAll();
          }
        }

        public void serviceSearchCompleted(int transID, int respCode) {
        }

        public void servicesDiscovered(int transID,
            ServiceRecord[] servRecord) {
        }
      };

      // this is the main loop, which will run as long as the thread is not marked as interrupted
      while (!interrupted) {
        long starttime = new Date().getTime();
       
        // check if we at all need to run the bluetooth discovery
        boolean runDiscovery = false;
        for(BluetoothEventHandler handler : eventHandler) {
          if(handler.isActive()) {
            runDiscovery = true;
            break;
          }
        }
       
        if(runDiscovery) {
          synchronized (inquiryCompletedEvent) {
            try {
              logger.debug("Launching bluetooth device discovery...");
              boolean started = agent.startInquiry(
                  DiscoveryAgent.GIAC, listener);
              if (started) {
                inquiryCompletedEvent.wait();
              }
            } catch (BluetoothStateException e) {
              logger.error("Error while starting the bluetooth agent", e);
            } catch (InterruptedException e) {
              interrupted = true;
            }
          }
        }
        if(!interrupted) {
          // let this thread sleep until the next discovery should be done
          long sleeptime = refreshRate * 1000L - (new Date().getTime() - starttime);
          if(sleeptime>0) {
            logger.debug("Sleeping for {} s...", sleeptime/1000.0);
            try {
              Thread.sleep(sleeptime);
            } catch(InterruptedException e) {
              interrupted = true;
            }
          }
        }
      }
    } catch (BluetoothStateException e) {
      logger.error("Error while initializing local bluetooth device.", e);
    }
  }
 
  /**
   * transforms a {@link RemoteDevice} object from the bluecove API to our own datastructure for bluetooth devices
   *
   * @param btDevice the device coming from the bluecove API
   * @return an instance of our own bluetooth device data structure
   */
  private static BluetoothDevice toBluetoothDevice(RemoteDevice btDevice) {
    String address = btDevice.getBluetoothAddress();
    String friendlyName = "";
    try {
      friendlyName = btDevice.getFriendlyName(false);
    } catch (IOException e) {
      // no friendly name accessible, let's ignore that
    }
    boolean paired = btDevice.isTrustedDevice();
    return new BluetoothDevice(address, friendlyName, paired);
  }
}
TOP

Related Classes of org.openhab.binding.bluetooth.internal.BTDeviceDiscoveryService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.