From c791a4c8e31e4c2ade4348ae1b9cd4f3838dedb0 Mon Sep 17 00:00:00 2001 From: Connor Petty Date: Sun, 8 Mar 2020 13:58:47 -0700 Subject: [PATCH 1/2] Refactor and unify BluetoothAdapter implementation logic Signed-off-by: Connor Petty --- .../bluegiga/BlueGigaBluetoothDevice.java | 17 +- .../handler/BlueGigaBridgeHandler.java | 137 ++--------- .../internal/BlueGigaConfiguration.java | 8 +- .../main/resources/ESH-INF/thing/bluegiga.xml | 18 +- .../bluetooth/bluez/BlueZBluetoothDevice.java | 11 +- .../handler/BlueZAdapterConfiguration.java | 5 +- .../bluez/handler/BlueZBridgeHandler.java | 151 +----------- .../main/resources/ESH-INF/thing/bluez.xml | 18 +- .../AbstractBluetoothBridgeHandler.java | 223 ++++++++++++++++++ ...seBluetoothBridgeHandlerConfiguration.java | 29 +++ .../bluetooth/BeaconBluetoothHandler.java | 3 - .../binding/bluetooth/BluetoothAdapter.java | 2 +- .../binding/bluetooth/BluetoothDevice.java | 36 ++- .../internal/BluetoothAddressLocker.java | 55 ----- .../internal/BluetoothDiscoveryProcess.java | 31 +-- .../bluetooth/MockBluetoothAdapter.java | 4 +- 16 files changed, 368 insertions(+), 380 deletions(-) create mode 100644 bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java create mode 100644 bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java delete mode 100644 bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothAddressLocker.java diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java index 9e1996268c8e9..cf9e268122153 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java @@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.common.ThreadPoolManager; -import org.joda.time.DateTime; import org.openhab.binding.bluetooth.BluetoothAddress; import org.openhab.binding.bluetooth.BluetoothCharacteristic; import org.openhab.binding.bluetooth.BluetoothCompletionStatus; @@ -81,8 +80,6 @@ private enum BlueGigaProcedure { // The connection handle if the device is connected private int connection = -1; - private DateTime lastSeenTime = DateTime.now(); - private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth"); private @Nullable ScheduledFuture connectTimer; @@ -524,6 +521,7 @@ public void bluegigaEventReceived(BlueGigaResponse 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 DateTime getLastSeenTime() { - return lastSeenTime; - } - - private void updateLastSeenTime() { - this.lastSeenTime = DateTime.now(); - } - private void cancelTimer(@Nullable ScheduledFuture task) { if (task != null) { task.cancel(true); diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java index 7f1b9fc36876a..d34446fe5cc51 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java @@ -19,10 +19,8 @@ import java.io.OutputStream; 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; @@ -33,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; @@ -116,8 +106,8 @@ * @author Pauli Anttila - Many improvements */ @NonNullByDefault -public class BlueGigaBridgeHandler extends BaseBridgeHandler - implements BluetoothAdapter, BlueGigaEventListener, BlueGigaHandlerListener { +public class BlueGigaBridgeHandler extends AbstractBluetoothBridgeHandler + implements BlueGigaEventListener, BlueGigaHandlerListener { private final Logger logger = LoggerFactory.getLogger(BlueGigaBridgeHandler.class); @@ -158,20 +148,12 @@ public class BlueGigaBridgeHandler extends BaseBridgeHandler // Map of open connections private final Map connections = new ConcurrentHashMap<>(); - // Set of discovery listeners - protected final Set discoveryListeners = new CopyOnWriteArraySet<>(); - - // List of device listeners - protected final ConcurrentHashMap 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) { @@ -179,19 +161,9 @@ public BlueGigaBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) 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 cfg = Optional.of(getConfigAs(BlueGigaConfiguration.class)); if (cfg.isPresent()) { configuration = cfg.get(); @@ -203,7 +175,12 @@ public void initialize() { @Override public void dispose() { - stop(true); + stop(); + stopScheduledTasks(); + if (initTask != null) { + initTask.cancel(true); + } + super.dispose(); } private void start() { @@ -211,7 +188,7 @@ private void start() { 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)); @@ -257,7 +234,7 @@ private void start() { } } - private void stop(boolean exit) { + private void stop() { if (transactionManager.isPresent()) { transactionManager.get().removeEventListener(this); transactionManager.get().close(); @@ -272,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() { @@ -307,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); } @@ -324,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).isBeforeNow(); - } - private BlueGigaGetConnectionsResponse readMaxConnections() throws BlueGigaException { return sendCommandWithoutChecks(new BlueGigaGetConnectionsCommand(), BlueGigaGetConnectionsResponse.class); } @@ -447,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(); @@ -483,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); } /** @@ -565,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} * @@ -794,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) { diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaConfiguration.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaConfiguration.java index 418820a7699dd..3fa5a1f2c72c2 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaConfiguration.java +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaConfiguration.java @@ -13,6 +13,7 @@ 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. @@ -20,8 +21,7 @@ * @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); } } diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml index f208b32e4186a..185e1ce22b69a 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml @@ -14,12 +14,24 @@ serial-port Serial Port - - - Whether this adapter actively participates in Bluetooth device discovery + + + Whether this adapter performs background discovery of Bluetooth devices true false + + + How often device cleanup is performed in seconds + true + 60 + + + + The number of seconds a device can remain radio silent before it is eligible for cleanup + true + 300 + Passive scan idle time defines the time how long to wait in milliseconds before start passive scan. diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java index 9e4a0a513d435..dd63b4cd21d04 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java @@ -51,8 +51,6 @@ public class BlueZBluetoothDevice extends BluetoothDevice { private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("bluetooth"); - private long lastSeenTime; - /** * Constructor * @@ -376,17 +374,10 @@ private BluetoothGattDescriptor getTinybDescriptorByUUID(String uuid) { return null; } - private void updateLastSeenTime() { - this.lastSeenTime = System.currentTimeMillis(); - } - - public long getTimeSinceSeen(TimeUnit unit) { - return unit.convert(System.currentTimeMillis() - this.lastSeenTime, TimeUnit.MILLISECONDS); - } - /** * Clean up and release memory. */ + @Override public void dispose() { if (device == null) { return; diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java index 43e874c230a18..b85ce17496e9e 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java @@ -12,14 +12,15 @@ */ package org.openhab.binding.bluetooth.bluez.handler; +import org.openhab.binding.bluetooth.BaseBluetoothBridgeHandlerConfiguration; + /** * Configuration properties class. * * @author Hilbrand Bouwkamp - Initial contribution */ -public class BlueZAdapterConfiguration { +public class BlueZAdapterConfiguration extends BaseBluetoothBridgeHandlerConfiguration { public String address; - public Boolean discovery; } diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java index 60d32287446d0..e35427e2a637e 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java @@ -13,31 +13,16 @@ package org.openhab.binding.bluetooth.bluez.handler; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.eclipse.jdt.annotation.DefaultLocation; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.ChannelUID; 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.openhab.binding.bluetooth.BluetoothAdapter; +import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler; import org.openhab.binding.bluetooth.BluetoothAddress; -import org.openhab.binding.bluetooth.BluetoothDevice; -import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState; -import org.openhab.binding.bluetooth.BluetoothDiscoveryListener; import org.openhab.binding.bluetooth.bluez.BlueZBluetoothDevice; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import tinyb.BluetoothException; import tinyb.BluetoothManager; @@ -52,30 +37,13 @@ * @author Connor Petty - Simplified device scan logic */ @NonNullByDefault -public class BlueZBridgeHandler extends BaseBridgeHandler implements BluetoothAdapter { - - private final Logger logger = LoggerFactory.getLogger(BlueZBridgeHandler.class); +public class BlueZBridgeHandler extends AbstractBluetoothBridgeHandler { private @NonNullByDefault({}) tinyb.BluetoothAdapter adapter; - private final Map tinybDeviceCache = new ConcurrentHashMap<>(); - // Our BT address private @NonNullByDefault({}) BluetoothAddress adapterAddress; - // Internal flag for the discovery configuration - private boolean discoveryConfigActive = true; - // Actual discovery status. - private boolean discoveryActive = true; - - // Map of Bluetooth devices known to this bridge. - // This contains the devices from the most recent scan - private final @NonNullByDefault({ - DefaultLocation.RETURN_TYPE }) Map devices = new ConcurrentHashMap<>(); - - // Set of discovery listeners - protected final Set discoveryListeners = new CopyOnWriteArraySet<>(); - private @NonNullByDefault({}) ScheduledFuture discoveryJob; /** @@ -89,6 +57,7 @@ public BlueZBridgeHandler(Bridge bridge) { @Override public void initialize() { + super.initialize(); BluetoothManager manager; try { manager = BluetoothManager.getBluetoothManager(); @@ -115,11 +84,6 @@ public void initialize() { return; } - discoveryActive = discoveryConfigActive = Boolean.TRUE.equals(configuration.discovery); - if (discoveryConfigActive) { - logger.debug("Deactivated discovery participation."); - } - logger.debug("Creating BlueZ adapter with address '{}'", adapterAddress); for (tinyb.BluetoothAdapter adapter : manager.getAdapters()) { @@ -136,25 +100,6 @@ public void initialize() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No adapter for this address found."); } - @Override - public ThingUID getUID() { - return getThing().getUID(); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - } - - @Override - public void addDiscoveryListener(BluetoothDiscoveryListener listener) { - discoveryListeners.add(listener); - } - - @Override - public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) { - discoveryListeners.remove(listener); - } - private void startDiscovery() { // we need to make sure the adapter is powered first if (!adapter.getPowered()) { @@ -167,36 +112,23 @@ private void startDiscovery() { } private void refreshDevices() { - try { + refreshTry: try { logger.debug("Refreshing Bluetooth device list..."); List tinybDevices = adapter.getDevices(); logger.debug("Found {} Bluetooth devices.", tinybDevices.size()); for (tinyb.BluetoothDevice tinybDevice : tinybDevices) { BlueZBluetoothDevice device = getDevice(new BluetoothAddress(tinybDevice.getAddress())); device.updateTinybDevice(tinybDevice); - notifyDiscoveryListeners(device); - } - // clean up orphaned entries - synchronized (devices) { - for (BlueZBluetoothDevice device : devices.values()) { - if (shouldRemove(device)) { - logger.debug("Removing device '{}' due to inactivity", device.getAddress()); - device.dispose(); - devices.remove(device.getAddress()); - discoveryListeners.forEach(listener -> listener.deviceRemoved(device)); - } - } + deviceDiscovered(device); } // For whatever reason, bluez will sometimes turn off scanning. So we just make sure it keeps running. startDiscovery(); - // everything went fine, so lets switch to online - updateStatus(ThingStatus.ONLINE); } catch (BluetoothException ex) { String message = ex.getMessage(); if (message != null) { if (message.contains("Operation already in progress")) { // we shouldn't go offline in this case - return; + break refreshTry; } int idx = message.lastIndexOf(':'); if (idx != -1) { @@ -204,33 +136,12 @@ private void refreshDevices() { } } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message); + return; } - } - - private boolean shouldRemove(BlueZBluetoothDevice 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; + // everything went fine, so lets switch to online if not already + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); } - // we remove devices we haven't seen in a while - return device.getTimeSinceSeen(TimeUnit.MINUTES) > 5; - } - - @Override - public void scanStart() { - // Enable scanning even while discovery is disabled in config. This allows manual starting discovery. - discoveryActive = true; - } - - @Override - public void scanStop() { - // Set active discovery state back to the configured discovery state. - discoveryActive = discoveryConfigActive; - // We need to keep the adapter in discovery mode as we otherwise won't get any RSSI updates either } @Override @@ -239,21 +150,8 @@ public BluetoothAddress getAddress() { } @Override - public BlueZBluetoothDevice getDevice(BluetoothAddress bluetoothAddress) { - synchronized (devices) { - BlueZBluetoothDevice device = devices.get(bluetoothAddress); - if (device == null) { - device = new BlueZBluetoothDevice(this, bluetoothAddress); - device.initialize(); - devices.put(bluetoothAddress, device); - } - return device; - } - } - - @Override - public boolean hasDevice(BluetoothAddress address) { - return devices.containsKey(address); + protected BlueZBluetoothDevice createDevice(BluetoothAddress address) { + return new BlueZBluetoothDevice(this, address); } @Override @@ -265,30 +163,7 @@ public void dispose() { if (adapter != null && adapter.getDiscovering()) { adapter.stopDiscovery(); } - synchronized (devices) { - for (BlueZBluetoothDevice device : devices.values()) { - device.dispose(); - } - devices.clear(); - } - } - - private void notifyDiscoveryListeners(BluetoothDevice device) { - if (discoveryActive) { - if (deviceReachable(device)) { - for (BluetoothDiscoveryListener listener : discoveryListeners) { - listener.deviceDiscovered(device); - } - } else { - logger.trace("Not notifying listeners for device '{}', because it is not reachable.", - device.getAddress()); - } - } - } - - private boolean deviceReachable(BluetoothDevice device) { - Integer rssi = device.getRssi(); - return rssi != null && rssi != 0; + super.dispose(); } } diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml index 2236bbe2b12ae..aea40961a98c9 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml @@ -14,12 +14,24 @@ The Bluetooth address of the adapter in format XX:XX:XX:XX:XX:XX - - - Whether Bluetooth device discovery is always enabled for this adapter + + + Whether this adapter performs background discovery of Bluetooth devices true false + + + How often device cleanup is performed in seconds + true + 60 + + + + The number of seconds a device can remain radio silent before it is eligible for cleanup + true + 300 + diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java new file mode 100644 index 0000000000000..909c157e46541 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java @@ -0,0 +1,223 @@ +/** + * 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.bluetooth; + +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +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.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a abstract superclass for BluetoothAdapter implementations. This class takes care of inactive device cleanup + * as well as handling background and active discovery logic. + * + * Subclasses will primarily be responsible for device discovery + * + * @author Connor Petty - Initial contribution from refactored code + */ +@NonNullByDefault +public abstract class AbstractBluetoothBridgeHandler extends BaseBridgeHandler + implements BluetoothAdapter { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + // Set of discovery listeners + private final Set discoveryListeners = new CopyOnWriteArraySet<>(); + + // Map of Bluetooth devices known to this bridge. + // This contains the devices from the most recent scan + private final Map devices = new ConcurrentHashMap<>(); + + // Actual discovery status. + protected volatile boolean activeScanEnabled = false; + + private BaseBluetoothBridgeHandlerConfiguration config = new BaseBluetoothBridgeHandlerConfiguration(); + + private @Nullable ScheduledFuture inactiveRemovalJob; + + /** + * Constructor + * + * @param bridge the bridge definition for this handler + */ + public AbstractBluetoothBridgeHandler(Bridge bridge) { + super(bridge); + } + + @Override + public ThingUID getUID() { + return getThing().getUID(); + } + + @Override + public void initialize() { + config = getConfigAs(BaseBluetoothBridgeHandlerConfiguration.class); + + int intervalSecs = config.inactiveDeviceCleanupIntervalSecs; + inactiveRemovalJob = scheduler.scheduleWithFixedDelay(this::removeInactiveDevices, intervalSecs, intervalSecs, + TimeUnit.SECONDS); + } + + @Override + public void dispose() { + ScheduledFuture inactiveRemovalJob = this.inactiveRemovalJob; + if (inactiveRemovalJob != null) { + inactiveRemovalJob.cancel(true); + } + this.inactiveRemovalJob = null; + + synchronized (devices) { + for (BD device : devices.values()) { + removeDevice(device); + } + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + private void removeInactiveDevices() { + // clean up orphaned entries + synchronized (devices) { + for (BD device : devices.values()) { + if (shouldRemove(device)) { + logger.debug("Removing device '{}' due to inactivity", device.getAddress()); + removeDevice(device); + } + } + } + } + + protected void removeDevice(BluetoothDevice device) { + device.dispose(); + synchronized (devices) { + devices.remove(device.getAddress()); + } + discoveryListeners.forEach(listener -> listener.deviceRemoved(device)); + } + + private boolean shouldRemove(BluetoothDevice 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; + } + + // we remove devices we haven't seen in a while + return ZonedDateTime.now().minusSeconds(config.inactiveDeviceCleanupThresholdSecs) + .isAfter(device.getLastSeenTime()); + } + + @Override + public void addDiscoveryListener(BluetoothDiscoveryListener listener) { + discoveryListeners.add(listener); + } + + @Override + public void removeDiscoveryListener(@Nullable BluetoothDiscoveryListener listener) { + discoveryListeners.remove(listener); + } + + @Override + public void scanStart() { + // Enable scanning even while discovery is disabled in config. This allows manual starting discovery. + activeScanEnabled = true; + refreshDiscoveredDevices(); + } + + protected void refreshDiscoveredDevices() { + logger.debug("Refreshing Bluetooth device list..."); + synchronized (devices) { + devices.values().forEach(this::deviceDiscovered); + } + } + + @Override + public void scanStop() { + // Set active discovery state back to the configured discovery state. + activeScanEnabled = false; + // We need to keep the adapter in discovery mode as we otherwise won't get any RSSI updates either + } + + @Override + public BD getDevice(BluetoothAddress address) { + synchronized (devices) { + if (devices.containsKey(address)) { + return devices.get(address); + } + BD device = createDevice(address); + device.updateLastSeenTime(); + devices.put(address, device); + return device; + } + } + + protected abstract BD createDevice(BluetoothAddress address); + + @Override + public boolean hasHandlerForDevice(BluetoothAddress address) { + String addrStr = address.toString(); + /* + * This type of search is inefficient and won't scale as the number of bluetooth Thing children increases on + * this bridge. But implementing a more efficient search would require a bit more overhead. + * Luckily though, it is reasonable to assume that the number of Thing children will remain small. + */ + for (Thing childThing : getThing().getThings()) { + Object childAddr = childThing.getConfiguration().get(BluetoothBindingConstants.CONFIGURATION_ADDRESS); + if (addrStr.equals(childAddr)) { + return childThing.getHandler() != null; + } + } + return false; + } + + public void deviceDiscovered(BluetoothDevice device) { + if (hasHandlerForDevice(device.getAddress())) { + // no point in discovering a device that already has a handler + return; + } + if (config.backgroundDiscovery || activeScanEnabled) { + if (deviceReachable(device)) { + discoveryListeners.forEach(listener -> listener.deviceDiscovered(device)); + } else { + logger.trace("Not notifying listeners for device '{}', because it is not reachable.", + device.getAddress()); + } + } + } + + private boolean deviceReachable(BluetoothDevice device) { + Integer rssi = device.getRssi(); + return rssi != null && rssi != 0; + } + +} diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java new file mode 100644 index 0000000000000..16468ccb49057 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java @@ -0,0 +1,29 @@ +/** + * 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.bluetooth; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This is the base configuration that all bluetooth bridge implementations will use. + * Bridges may choose to use a subclass of this class as their configuration in order to + * support more options. + * + * @author Connor Petty - Initial contribution from refactored code + */ +@NonNullByDefault +public class BaseBluetoothBridgeHandlerConfiguration { + public boolean backgroundDiscovery = false; + public int inactiveDeviceCleanupIntervalSecs = 60; + public int inactiveDeviceCleanupThresholdSecs = 300; +} diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java index f08714d33f516..3149716c0a52c 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java @@ -26,7 +26,6 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.UnDefType; -import org.openhab.binding.bluetooth.discovery.internal.BluetoothAddressLocker; import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification; import org.openhab.binding.bluetooth.notification.BluetoothScanNotification; @@ -81,11 +80,9 @@ public void initialize() { try { deviceLock.lock(); - BluetoothAddressLocker.lock(address); device = adapter.getDevice(address); device.addListener(this); } finally { - BluetoothAddressLocker.unlock(address); deviceLock.unlock(); } diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java index fabdb2286555b..da3ff8fe92b6f 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java @@ -81,6 +81,6 @@ public interface BluetoothAdapter extends Identifiable { * @param address the {@link BluetoothAddress} to check for * @return true if this adapter has a {@link BluetoothDevice} with that address */ - boolean hasDevice(BluetoothAddress address); + boolean hasHandlerForDevice(BluetoothAddress address); } diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java index 774357ba8db86..20f00ea01e30c 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.bluetooth; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -117,6 +118,11 @@ protected enum BluetoothEventType { */ protected Integer txPower = null; + /** + * Last time when activity occurred on this device. + */ + protected ZonedDateTime lastSeenTime; + /** * The event listeners will be notified of device updates */ @@ -134,7 +140,25 @@ public BluetoothDevice(BluetoothAdapter adapter, BluetoothAddress address) { } /** - * Returns the the name of the Bluetooth device. + * Returns the last time this device was active + * + * @return The last time this device was active + */ + public ZonedDateTime getLastSeenTime() { + return lastSeenTime; + } + + /** + * Updates the last activity timestamp for this device. + * Should be called whenever activity occurs on this device. + * + */ + public void updateLastSeenTime() { + lastSeenTime = ZonedDateTime.now(); + } + + /** + * Returns the name of the Bluetooth device. * * @return The devices name */ @@ -491,13 +515,21 @@ public void removeListener(BluetoothDeviceListener listener) { /** * Checks if this device has any listeners - * + * * @return true if this device has listeners */ public boolean hasListeners() { return !eventListeners.isEmpty(); } + /** + * Releases resources that this device is using. + * + */ + protected void dispose() { + + } + /** * Notify the listeners of an event * diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothAddressLocker.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothAddressLocker.java deleted file mode 100644 index 0800722572e54..0000000000000 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothAddressLocker.java +++ /dev/null @@ -1,55 +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.bluetooth.discovery.internal; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.openhab.binding.bluetooth.BluetoothAddress; - -/** - * The {@link BluetoothAddressLocker} handles global locking of BluetoothAddress. - * This is used to make sure that devices with handlers are not connected to during discovery. - * - * @author Connor Petty - Initial Contribution - */ -public class BluetoothAddressLocker { - - private static Map locks = new ConcurrentHashMap<>(); - - public static void lock(BluetoothAddress address) { - locks.compute(address, (addr, oldRef) -> { - LockReference ref = oldRef; - if (ref == null) { - ref = new LockReference(); - } - ref.count++; - return ref; - }).lock.lock(); - } - - public static void unlock(BluetoothAddress address) { - locks.computeIfPresent(address, (addr, ref) -> { - ref.count--; - ref.lock.unlock(); - return ref.count <= 0 ? null : ref; - }); - } - - private static class LockReference { - private int count = 0; - private Lock lock = new ReentrantLock(); - } -} diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryProcess.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryProcess.java index 95b56fb152fa8..3ee3f665d61c3 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryProcess.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryProcess.java @@ -101,24 +101,19 @@ public DiscoveryResult get() { DiscoveryResult result = null; if (!connectionParticipants.isEmpty()) { BluetoothAddress address = device.getAddress(); - try { - BluetoothAddressLocker.lock(address); - if (isAddressAvailable(address)) { - result = findConnectionResult(connectionParticipants); - // make sure to disconnect before letting go of the device - if (device.getConnectionState() == ConnectionState.CONNECTED) { - try { - if (!device.disconnect()) { - logger.debug("Failed to disconnect from device {}", address); - } - } catch (RuntimeException ex) { - logger.warn("Error occurred during bluetooth discovery for device {} on adapter {}", - address, device.getAdapter().getAddress(), ex); + if (isAddressAvailable(address)) { + result = findConnectionResult(connectionParticipants); + // make sure to disconnect before letting go of the device + if (device.getConnectionState() == ConnectionState.CONNECTED) { + try { + if (!device.disconnect()) { + logger.debug("Failed to disconnect from device {}", address); } + } catch (RuntimeException ex) { + logger.warn("Error occurred during bluetooth discovery for device {} on adapter {}", address, + device.getAdapter().getAddress(), ex); } } - } finally { - BluetoothAddressLocker.unlock(address); } } if (result == null) { @@ -129,11 +124,7 @@ public DiscoveryResult get() { private boolean isAddressAvailable(BluetoothAddress address) { // if a device with this address has a handler on any of the adapters, we abandon discovery - return adapters.stream().filter(adapter -> adapter.hasDevice(address)) - // get adapter's corresponding device - .map(adapter -> adapter.getDevice(address)) - // make sure nothing is listening to any of them - .noneMatch(d -> d.hasListeners()); + return adapters.stream().noneMatch(adapter -> adapter.hasHandlerForDevice(address)); } private DiscoveryResult createDefaultResult(BluetoothDevice device) { diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothAdapter.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothAdapter.java index bd831142415a5..d5ace31518b34 100644 --- a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothAdapter.java +++ b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothAdapter.java @@ -63,8 +63,8 @@ public MockBluetoothDevice getDevice(BluetoothAddress address) { } @Override - public boolean hasDevice(BluetoothAddress address) { - return devices.containsKey(address); + public boolean hasHandlerForDevice(BluetoothAddress address) { + return false; } } From 05c648de62fdf832dd392624b4ad947d6372f93d Mon Sep 17 00:00:00 2001 From: Connor Petty Date: Wed, 1 Apr 2020 14:19:41 -0700 Subject: [PATCH 2/2] Review changes. Signed-off-by: Connor Petty --- .../main/resources/ESH-INF/thing/bluegiga.xml | 8 +++---- .../bluez/handler/BlueZBridgeHandler.java | 9 +++---- .../main/resources/ESH-INF/thing/bluez.xml | 24 +++++++++---------- .../AbstractBluetoothBridgeHandler.java | 10 ++++---- ...seBluetoothBridgeHandlerConfiguration.java | 4 ++-- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml index 185e1ce22b69a..ddef05a56a1f3 100644 --- a/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml @@ -20,15 +20,15 @@ true false - + - How often device cleanup is performed in seconds + How often device cleanup is performed true 60 - + - The number of seconds a device can remain radio silent before it is eligible for cleanup + Timespan a device can remain radio silent before it is eligible for cleanup true 300 diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java index e35427e2a637e..ef0b12d886324 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java @@ -23,6 +23,8 @@ import org.openhab.binding.bluetooth.AbstractBluetoothBridgeHandler; import org.openhab.binding.bluetooth.BluetoothAddress; import org.openhab.binding.bluetooth.bluez.BlueZBluetoothDevice; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tinyb.BluetoothException; import tinyb.BluetoothManager; @@ -39,6 +41,8 @@ @NonNullByDefault public class BlueZBridgeHandler extends AbstractBluetoothBridgeHandler { + private final Logger logger = LoggerFactory.getLogger(BlueZBridgeHandler.class); + private @NonNullByDefault({}) tinyb.BluetoothAdapter adapter; // Our BT address @@ -138,10 +142,7 @@ private void refreshDevices() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message); return; } - // everything went fine, so lets switch to online if not already - if (getThing().getStatus() != ThingStatus.ONLINE) { - updateStatus(ThingStatus.ONLINE); - } + updateStatus(ThingStatus.ONLINE); } @Override diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml index aea40961a98c9..3a5eb86d5fbdd 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml @@ -20,18 +20,18 @@ true false - - - How often device cleanup is performed in seconds - true - 60 - - - - The number of seconds a device can remain radio silent before it is eligible for cleanup - true - 300 - + + + How often device cleanup is performed + true + 60 + + + + Timespan a device can remain radio silent before it is eligible for cleanup + true + 300 + diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java index 909c157e46541..5ba2eb780ce32 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/AbstractBluetoothBridgeHandler.java @@ -13,9 +13,9 @@ package org.openhab.binding.bluetooth; import java.time.ZonedDateTime; +import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -44,14 +44,14 @@ public abstract class AbstractBluetoothBridgeHandler extends BaseBridgeHandler implements BluetoothAdapter { - protected final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(AbstractBluetoothBridgeHandler.class); // Set of discovery listeners private final Set discoveryListeners = new CopyOnWriteArraySet<>(); // Map of Bluetooth devices known to this bridge. // This contains the devices from the most recent scan - private final Map devices = new ConcurrentHashMap<>(); + private final Map devices = new HashMap<>(); // Actual discovery status. protected volatile boolean activeScanEnabled = false; @@ -78,7 +78,7 @@ public ThingUID getUID() { public void initialize() { config = getConfigAs(BaseBluetoothBridgeHandlerConfiguration.class); - int intervalSecs = config.inactiveDeviceCleanupIntervalSecs; + int intervalSecs = config.inactiveDeviceCleanupInterval; inactiveRemovalJob = scheduler.scheduleWithFixedDelay(this::removeInactiveDevices, intervalSecs, intervalSecs, TimeUnit.SECONDS); } @@ -133,7 +133,7 @@ private boolean shouldRemove(BluetoothDevice device) { } // we remove devices we haven't seen in a while - return ZonedDateTime.now().minusSeconds(config.inactiveDeviceCleanupThresholdSecs) + return ZonedDateTime.now().minusSeconds(config.inactiveDeviceCleanupThreshold) .isAfter(device.getLastSeenTime()); } diff --git a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java index 16468ccb49057..904db763c74f0 100644 --- a/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java +++ b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BaseBluetoothBridgeHandlerConfiguration.java @@ -24,6 +24,6 @@ @NonNullByDefault public class BaseBluetoothBridgeHandlerConfiguration { public boolean backgroundDiscovery = false; - public int inactiveDeviceCleanupIntervalSecs = 60; - public int inactiveDeviceCleanupThresholdSecs = 300; + public int inactiveDeviceCleanupInterval = 60; + public int inactiveDeviceCleanupThreshold = 300; }