Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[bluetooth] Refactor and unify BluetoothAdapter implementation logic (o…
Browse files Browse the repository at this point in the history
…penhab#7129)

* Refactor and unify BluetoothAdapter implementation logic

Signed-off-by: Connor Petty <mistercpp2000+gitsignoff@gmail.com>
Signed-off-by: Hans-Reiner Hoffmann <hans-reiner.hoffmann@gmx.de>
cpmeister authored and Hans-Reiner committed Apr 11, 2020
1 parent 2b02038 commit f14dd2a
Showing 16 changed files with 366 additions and 377 deletions.
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@
*/
package org.openhab.binding.bluetooth.bluegiga;

import java.time.ZonedDateTime;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -81,8 +80,6 @@ private enum BlueGigaProcedure {
// The connection handle if the device is connected
private int connection = -1;

private ZonedDateTime lastSeenTime = ZonedDateTime.now();

private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth");

private @Nullable ScheduledFuture<?> connectTimer;
@@ -524,6 +521,7 @@ private void handleAttributeValueEvent(BlueGigaAttributeValueEvent event) {
/**
* Clean up and release memory.
*/
@Override
public void dispose() {
if (connectionState == ConnectionState.CONNECTED) {
disconnect();
@@ -536,19 +534,6 @@ public void dispose() {
connection = -1;
}

/**
* Return last seen Time
*
* @return last seen Time
*/
public ZonedDateTime getLastSeenTime() {
return lastSeenTime;
}

private void updateLastSeenTime() {
this.lastSeenTime = ZonedDateTime.now();
}

private void cancelTimer(@Nullable ScheduledFuture<?> task) {
if (task != null) {
task.cancel(true);
Original file line number Diff line number Diff line change
@@ -17,13 +17,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -34,25 +31,17 @@
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.common.ThreadPoolManager;
import org.eclipse.smarthome.core.thing.Bridge;
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.ThingUID;
import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.io.transport.serial.PortInUseException;
import org.eclipse.smarthome.io.transport.serial.SerialPort;
import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier;
import org.eclipse.smarthome.io.transport.serial.SerialPortManager;
import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException;
import org.openhab.binding.bluetooth.BluetoothAdapter;
import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler;
import org.openhab.binding.bluetooth.BluetoothAddress;
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
import org.openhab.binding.bluetooth.BluetoothDevice;
import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
import org.openhab.binding.bluetooth.BluetoothDeviceListener;
import org.openhab.binding.bluetooth.BluetoothDiscoveryListener;
import org.openhab.binding.bluetooth.bluegiga.BlueGigaAdapterConstants;
import org.openhab.binding.bluetooth.bluegiga.BlueGigaBluetoothDevice;
import org.openhab.binding.bluetooth.bluegiga.internal.BlueGigaCommand;
@@ -117,8 +106,8 @@
* @author Pauli Anttila - Many improvements
*/
@NonNullByDefault
public class BlueGigaBridgeHandler extends BaseBridgeHandler
implements BluetoothAdapter, BlueGigaEventListener, BlueGigaHandlerListener {
public class BlueGigaBridgeHandler extends AbstractBluetoothBridgeHandler<BlueGigaBluetoothDevice>
implements BlueGigaEventListener, BlueGigaHandlerListener {

private final Logger logger = LoggerFactory.getLogger(BlueGigaBridgeHandler.class);

@@ -159,40 +148,22 @@ public class BlueGigaBridgeHandler extends BaseBridgeHandler
// Map of open connections
private final Map<Integer, BluetoothAddress> connections = new ConcurrentHashMap<>();

// Set of discovery listeners
protected final Set<BluetoothDiscoveryListener> discoveryListeners = new CopyOnWriteArraySet<>();

// List of device listeners
protected final ConcurrentHashMap<BluetoothAddress, BluetoothDeviceListener> deviceListeners = new ConcurrentHashMap<>();

private volatile boolean initComplete = false;

private @Nullable ScheduledFuture<?> initTask;
private @Nullable ScheduledFuture<?> removeInactiveDevicesTask;
private @Nullable ScheduledFuture<?> discoveryTask;

private volatile boolean activeScanEnabled = false;

private @Nullable Future<?> passiveScanIdleTimer;

public BlueGigaBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) {
super(bridge);
this.serialPortManager = serialPortManager;
}

@Override
public ThingUID getUID() {
// being a BluetoothAdapter, we use the UID of our bridge
return getThing().getUID();
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// No commands supported for the bridge
}

@Override
public void initialize() {
super.initialize();
Optional<BlueGigaConfiguration> cfg = Optional.of(getConfigAs(BlueGigaConfiguration.class));
if (cfg.isPresent()) {
configuration = cfg.get();
@@ -204,15 +175,20 @@ public void initialize() {

@Override
public void dispose() {
stop(true);
stop();
stopScheduledTasks();
if (initTask != null) {
initTask.cancel(true);
}
super.dispose();
}

private void start() {
try {
if (!initComplete) {
logger.debug("Initialize BlueGiga");
logger.debug("Using configuration: {}", configuration);
stop(false);
stop();
if (openSerialPort(configuration.port, 115200)) {
serialHandler = Optional.of(new BlueGigaSerialHandler(inputStream.get(), outputStream.get()));
transactionManager = Optional.of(new BlueGigaTransactionManager(serialHandler.get(), executor));
@@ -258,7 +234,7 @@ private void start() {
}
}

private void stop(boolean exit) {
private void stop() {
if (transactionManager.isPresent()) {
transactionManager.get().removeEventListener(this);
transactionManager.get().close();
@@ -273,17 +249,6 @@ private void stop(boolean exit) {
initComplete = false;
connections.clear();
closeSerialPort();

if (exit) {
stopScheduledTasks();
if (initTask != null) {
initTask.cancel(true);
}
devices.forEach((address, device) -> {
device.dispose();
});
devices.clear();
}
}

private void schedulePassiveScan() {
@@ -308,8 +273,6 @@ private void cancelScheduledPassiveScan() {
private void startScheduledTasks() {
schedulePassiveScan();
logger.debug("Start scheduled task to remove inactive devices");
removeInactiveDevicesTask = scheduler.scheduleWithFixedDelay(this::removeInactiveDevices, 1, 1,
TimeUnit.MINUTES);
discoveryTask = scheduler.scheduleWithFixedDelay(this::refreshDiscoveredDevices, 0, 10, TimeUnit.SECONDS);
}

@@ -325,38 +288,6 @@ private void stopScheduledTasks() {
}
}

private void removeInactiveDevices() {
logger.debug("Check inactive devices, count {}", devices.size());
devices.forEach((address, device) -> {
if (shouldRemove(device)) {
logger.debug("Removing device '{}' due to inactivity, last seen: {}", address,
device.getLastSeenTime());
device.dispose();
devices.remove(address);
}
});
}

private void refreshDiscoveredDevices() {
logger.debug("Refreshing Bluetooth device list...");
devices.forEach((address, device) -> {
deviceDiscovered(device);
});
}

private boolean shouldRemove(BlueGigaBluetoothDevice device) {
// we can't remove devices with listeners since that means they have a handler.
if (device.hasListeners()) {
return false;
}
// devices that are connected won't receive any scan notifications so we can't remove them for being idle
if (device.getConnectionState() == ConnectionState.CONNECTED) {
return false;
}

return device.getLastSeenTime().plusMinutes(5).isBefore(ZonedDateTime.now());
}

private BlueGigaGetConnectionsResponse readMaxConnections() throws BlueGigaException {
return sendCommandWithoutChecks(new BlueGigaGetConnectionsCommand(), BlueGigaGetConnectionsResponse.class);
}
@@ -448,24 +379,20 @@ private void closeSerialPort() {

@Override
public void scanStart() {
super.scanStart();
logger.debug("Start active scan");
activeScanEnabled = true;
// Stop the passive scan
cancelScheduledPassiveScan();
bgEndProcedure();

// Start a active scan
bgStartScanning(true, configuration.activeScanInterval, configuration.activeScanWindow);

for (BluetoothDevice device : devices.values()) {
deviceDiscovered(device);
}
}

@Override
public void scanStop() {
super.scanStop();
logger.debug("Stop active scan");
activeScanEnabled = false;

// Stop the active scan
bgEndProcedure();
@@ -484,21 +411,9 @@ public BluetoothAddress getAddress() {
}
}

@SuppressWarnings({ "null", "unused" })
@Override
public BluetoothDevice getDevice(BluetoothAddress address) {
BlueGigaBluetoothDevice device = devices.get(address);
if (device == null) {
// This method always needs to return a device, even if we don't currently know about it.
device = new BlueGigaBluetoothDevice(this, address, BluetoothAddressType.UNKNOWN);
devices.put(address, device);
}
return device;
}

@Override
public boolean hasDevice(BluetoothAddress address) {
return devices.containsKey(address);
protected BlueGigaBluetoothDevice createDevice(BluetoothAddress address) {
return new BlueGigaBluetoothDevice(this, address, BluetoothAddressType.UNKNOWN);
}

/**
@@ -566,17 +481,6 @@ public boolean bgDisconnect(int connectionHandle) {
}
}

/**
* Device discovered. This simply passes the discover information to the discovery service for processing.
*/
public void deviceDiscovered(BluetoothDevice device) {
if (configuration.discovery || activeScanEnabled) {
for (BluetoothDiscoveryListener listener : discoveryListeners) {
listener.deviceDiscovered(device);
}
}
}

/**
* Start a read of all primary services using {@link BlueGigaReadByGroupTypeCommand}
*
@@ -795,16 +699,6 @@ public void removeEventListener(BlueGigaEventListener listener) {
});
}

@Override
public void addDiscoveryListener(BluetoothDiscoveryListener listener) {
discoveryListeners.add(listener);
}

@Override
public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) {
discoveryListeners.remove(listener);
}

@Override
public void bluegigaEventReceived(@Nullable BlueGigaResponse event) {
if (event instanceof BlueGigaScanResponseEvent) {
Original file line number Diff line number Diff line change
@@ -13,15 +13,15 @@
package org.openhab.binding.bluetooth.bluegiga.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.bluetooth.BaseBluetoothBridgeHandlerConfiguration;

/**
* Configuration class for {@link BlueGigaConfiguration} device.
*
* @author Pauli Anttila - Initial contribution
*/
@NonNullByDefault
public class BlueGigaConfiguration {
public boolean discovery;
public class BlueGigaConfiguration extends BaseBluetoothBridgeHandlerConfiguration {
public String port = "";
public int passiveScanIdleTime;
public int passiveScanInterval;
@@ -39,7 +39,7 @@ public String toString() {
"[discovery=%b, port=%s, passiveScanIdleTime=%d, passiveScanInterval=%d, passiveScanWindow=%d"
+ ", activeScanInterval=%d, activeScanWindow=%d, connIntervalMin=%d, connIntervalMax=%d"
+ ", connLatency=%d, connTimeout=%d]",
discovery, port, passiveScanIdleTime, passiveScanInterval, passiveScanWindow, activeScanInterval,
activeScanWindow, connIntervalMin, connIntervalMax, connLatency, connTimeout);
backgroundDiscovery, port, passiveScanIdleTime, passiveScanInterval, passiveScanWindow,
activeScanInterval, activeScanWindow, connIntervalMin, connIntervalMax, connLatency, connTimeout);
}
}
Original file line number Diff line number Diff line change
@@ -14,12 +14,24 @@
<context>serial-port</context>
<description>Serial Port</description>
</parameter>
<parameter name="discovery" type="boolean">
<label>Device Discovery</label>
<description>Whether this adapter actively participates in Bluetooth device discovery</description>
<parameter name="backgroundDiscovery" type="boolean">
<label>Background Discovery</label>
<description>Whether this adapter performs background discovery of Bluetooth devices</description>
<advanced>true</advanced>
<default>false</default>
</parameter>
<parameter name="inactiveDeviceCleanupInterval" type="integer" min="1" unit="s">
<label>Device Cleanup Interval</label>
<description>How often device cleanup is performed</description>
<advanced>true</advanced>
<default>60</default>
</parameter>
<parameter name="inactiveDeviceCleanupThreshold" type="integer" min="1" unit="s">
<label>Device Cleanup Threshold</label>
<description>Timespan a device can remain radio silent before it is eligible for cleanup</description>
<advanced>true</advanced>
<default>300</default>
</parameter>
<parameter name="passiveScanIdleTime" type="integer" min="100" max="60000">
<label>Passive Scan Idle Time</label>
<description>Passive scan idle time defines the time how long to wait in milliseconds before start passive scan.</description>
Loading

0 comments on commit f14dd2a

Please sign in to comment.