[bluetooth] Changed characteristic read/write to use CompletableFutures (#8970)
Signed-off-by: Connor Petty <mistercpp2000+gitsignoff@gmail.com>
This commit is contained in:
@@ -15,10 +15,10 @@ package org.openhab.binding.bluetooth.bluez.internal;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bluez.exceptions.BluezFailedException;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.freedesktop.dbus.errors.NoReply;
|
||||
@@ -28,7 +28,6 @@ import org.freedesktop.dbus.types.UInt16;
|
||||
import org.openhab.binding.bluetooth.BaseBluetoothDevice;
|
||||
import org.openhab.binding.bluetooth.BluetoothAddress;
|
||||
import org.openhab.binding.bluetooth.BluetoothCharacteristic;
|
||||
import org.openhab.binding.bluetooth.BluetoothCompletionStatus;
|
||||
import org.openhab.binding.bluetooth.BluetoothDescriptor;
|
||||
import org.openhab.binding.bluetooth.BluetoothService;
|
||||
import org.openhab.binding.bluetooth.bluez.internal.events.BlueZEvent;
|
||||
@@ -42,6 +41,8 @@ import org.openhab.binding.bluetooth.bluez.internal.events.ServicesResolvedEvent
|
||||
import org.openhab.binding.bluetooth.bluez.internal.events.TXPowerEvent;
|
||||
import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
|
||||
import org.openhab.binding.bluetooth.notification.BluetoothScanNotification;
|
||||
import org.openhab.binding.bluetooth.util.RetryException;
|
||||
import org.openhab.binding.bluetooth.util.RetryFuture;
|
||||
import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -257,57 +258,65 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableNotifications(BluetoothCharacteristic characteristic) {
|
||||
ensureConnected();
|
||||
|
||||
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
|
||||
if (c != null) {
|
||||
|
||||
try {
|
||||
c.startNotify();
|
||||
} catch (DBusException e) {
|
||||
if (e.getMessage().contains("Already notifying")) {
|
||||
return false;
|
||||
} else if (e.getMessage().contains("In Progress")) {
|
||||
// let's retry in 10 seconds
|
||||
scheduler.schedule(() -> enableNotifications(characteristic), 10, TimeUnit.SECONDS);
|
||||
} else {
|
||||
logger.warn("Exception occurred while activating notifications on '{}'", address, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return false;
|
||||
public CompletableFuture<@Nullable Void> enableNotifications(BluetoothCharacteristic characteristic) {
|
||||
BluetoothDevice dev = device;
|
||||
if (dev == null || !dev.isConnected()) {
|
||||
return CompletableFuture
|
||||
.failedFuture(new IllegalStateException("DBusBlueZ device is not set or not connected"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeCharacteristic(BluetoothCharacteristic characteristic) {
|
||||
logger.debug("writeCharacteristic()");
|
||||
|
||||
ensureConnected();
|
||||
|
||||
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
|
||||
if (c == null) {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return false;
|
||||
return CompletableFuture.failedFuture(
|
||||
new IllegalStateException("Characteristic " + characteristic.getUuid() + " is missing on device"));
|
||||
}
|
||||
|
||||
scheduler.submit(() -> {
|
||||
return RetryFuture.callWithRetry(() -> {
|
||||
try {
|
||||
c.writeValue(characteristic.getByteValue(), null);
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, characteristic,
|
||||
BluetoothCompletionStatus.SUCCESS);
|
||||
c.startNotify();
|
||||
} catch (DBusException e) {
|
||||
if (e.getMessage().contains("Already notifying")) {
|
||||
return null;
|
||||
} else if (e.getMessage().contains("In Progress")) {
|
||||
// let's retry in half a second
|
||||
throw new RetryException(500, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
logger.warn("Exception occurred while activating notifications on '{}'", address, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<@Nullable Void> writeCharacteristic(BluetoothCharacteristic characteristic, byte[] value) {
|
||||
logger.debug("writeCharacteristic()");
|
||||
|
||||
BluetoothDevice dev = device;
|
||||
if (dev == null || !dev.isConnected()) {
|
||||
return CompletableFuture
|
||||
.failedFuture(new IllegalStateException("DBusBlueZ device is not set or not connected"));
|
||||
}
|
||||
|
||||
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
|
||||
if (c == null) {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return CompletableFuture.failedFuture(
|
||||
new IllegalStateException("Characteristic " + characteristic.getUuid() + " is missing on device"));
|
||||
}
|
||||
|
||||
return RetryFuture.callWithRetry(() -> {
|
||||
try {
|
||||
c.writeValue(value, null);
|
||||
return null;
|
||||
} catch (DBusException e) {
|
||||
logger.debug("Exception occurred when trying to write characteristic '{}': {}",
|
||||
characteristic.getUuid(), e.getMessage());
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_WRITE_COMPLETE, characteristic,
|
||||
BluetoothCompletionStatus.ERROR);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -364,10 +373,7 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
|
||||
}
|
||||
BluetoothCharacteristic c = getCharacteristic(UUID.fromString(characteristic.getUuid()));
|
||||
if (c != null) {
|
||||
synchronized (c) {
|
||||
c.setValue(event.getData());
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_UPDATED, c, BluetoothCompletionStatus.SUCCESS);
|
||||
}
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_UPDATED, c, event.getData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,28 +459,30 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readCharacteristic(BluetoothCharacteristic characteristic) {
|
||||
public CompletableFuture<byte[]> readCharacteristic(BluetoothCharacteristic characteristic) {
|
||||
BluetoothDevice dev = device;
|
||||
if (dev == null || !dev.isConnected()) {
|
||||
return CompletableFuture
|
||||
.failedFuture(new IllegalStateException("DBusBlueZ device is not set or not connected"));
|
||||
}
|
||||
|
||||
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
|
||||
if (c == null) {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return false;
|
||||
return CompletableFuture.failedFuture(
|
||||
new IllegalStateException("Characteristic " + characteristic.getUuid() + " is missing on device"));
|
||||
}
|
||||
|
||||
scheduler.submit(() -> {
|
||||
return RetryFuture.callWithRetry(() -> {
|
||||
try {
|
||||
byte[] value = c.readValue(null);
|
||||
characteristic.setValue(value);
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic,
|
||||
BluetoothCompletionStatus.SUCCESS);
|
||||
return c.readValue(null);
|
||||
} catch (DBusException | DBusExecutionException e) {
|
||||
// DBusExecutionException is thrown if the value cannot be read
|
||||
logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(),
|
||||
e.getMessage());
|
||||
notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic,
|
||||
BluetoothCompletionStatus.ERROR);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -490,24 +498,35 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableNotifications(BluetoothCharacteristic characteristic) {
|
||||
public CompletableFuture<@Nullable Void> disableNotifications(BluetoothCharacteristic characteristic) {
|
||||
BluetoothDevice dev = device;
|
||||
if (dev == null || !dev.isConnected()) {
|
||||
return CompletableFuture
|
||||
.failedFuture(new IllegalStateException("DBusBlueZ device is not set or not connected"));
|
||||
}
|
||||
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
|
||||
if (c != null) {
|
||||
if (c == null) {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return CompletableFuture.failedFuture(
|
||||
new IllegalStateException("Characteristic " + characteristic.getUuid() + " is missing on device"));
|
||||
}
|
||||
|
||||
return RetryFuture.callWithRetry(() -> {
|
||||
try {
|
||||
c.stopNotify();
|
||||
} catch (BluezFailedException e) {
|
||||
if (e.getMessage().contains("In Progress")) {
|
||||
// let's retry in 10 seconds
|
||||
scheduler.schedule(() -> disableNotifications(characteristic), 10, TimeUnit.SECONDS);
|
||||
} catch (DBusException e) {
|
||||
if (e.getMessage().contains("Already notifying")) {
|
||||
return null;
|
||||
} else if (e.getMessage().contains("In Progress")) {
|
||||
// let's retry in half a second
|
||||
throw new RetryException(500, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
logger.warn("Exception occurred while activating notifications on '{}'", address, e);
|
||||
logger.warn("Exception occurred while deactivating notifications on '{}'", address, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user