]> git.basschouten.com Git - openhab-addons.git/commitdiff
[bluetooth.generic] Enable BLE notification for linked channels (#10122)
authorPete <8108165+PRosenb@users.noreply.github.com>
Tue, 16 Feb 2021 20:26:34 +0000 (21:26 +0100)
committerGitHub <noreply@github.com>
Tue, 16 Feb 2021 20:26:34 +0000 (12:26 -0800)
* [bluetooth] Add BluetoothDevice.isNotifying()
* [bluetooth] Improve Characteristic properties support
* [bluez] Improve Characteristic properties support
* [bluetooth] Add BluetoothDevice.canNotify()
* [bluez] Also catch DBusExecutionException on read value
* [bluetooth.generic] Activate notifications for linked channels where characteristics are able to notify
* [bluez] Adjust javadoc
* [bluegiga] Add BluetoothDevice.isNotifying() support
* [bluegiga] Fix notification enabled check
* [bluetooth] move canNotify() to Characteristic
* [bluegiga] rename notificationEnabled to notifying
* [bluetooth.generic] use handlerToChannels to subscribe to notifications
* [bluetooth.generic] implement TODOs of canRead()/canWrite()
* [bluetooth.generic] optimize ChannelUID
* [bluetooth.generic] use channelUids for link check

Signed-off-by: Peter Rosenberg <prosenb.dev@gmail.com>
bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothCharacteristic.java
bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java
bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZBluetoothDevice.java
bundles/org.openhab.binding.bluetooth.generic/src/main/java/org/openhab/binding/bluetooth/generic/internal/GenericBluetoothHandler.java
bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java
bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java
bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/DelegateBluetoothDevice.java
bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java [new file with mode: 0644]
bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/MockBluetoothDevice.java

index afa8ba4843b835ebd5aba7c10c9849549bc1e774..43206c9b1c37f925a49388ad95bdbececd4837e1 100644 (file)
@@ -27,7 +27,7 @@ import org.openhab.binding.bluetooth.BluetoothCharacteristic;
  */
 public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic {
 
-    private boolean notificationEnabled;
+    private boolean notifying;
 
     public BlueGigaBluetoothCharacteristic(int handle) {
         super(null, handle);
@@ -45,11 +45,11 @@ public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic {
         this.uuid = uuid;
     }
 
-    public boolean isNotificationEnabled() {
-        return notificationEnabled;
+    public boolean isNotifying() {
+        return notifying;
     }
 
-    public void setNotificationEnabled(boolean enable) {
-        this.notificationEnabled = enable;
+    public void setNotifying(boolean enable) {
+        this.notifying = enable;
     }
 }
index c2cb969e8fe4efc4ec0ea0cd76ca53b49ba2c4da..ccff259a0b8785213c8bafa566e5b49c3f4e8b8d 100644 (file)
@@ -196,7 +196,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
         }
 
         BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
-        if (ch.isNotificationEnabled()) {
+        if (ch.isNotifying()) {
             return true;
         }
 
@@ -241,12 +241,12 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
     @Override
     public boolean disableNotifications(BluetoothCharacteristic characteristic) {
         if (connection == -1) {
-            logger.debug("Cannot enable notifications, device not connected {}", this);
+            logger.debug("Cannot disable notifications, device not connected {}", this);
             return false;
         }
 
         BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
-        if (ch.isNotificationEnabled()) {
+        if (!ch.isNotifying()) {
             return true;
         }
 
@@ -288,6 +288,12 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
         return true;
     }
 
+    @Override
+    public boolean isNotifying(BluetoothCharacteristic characteristic) {
+        BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
+        return ch.isNotifying();
+    }
+
     @Override
     public boolean enableNotifications(BluetoothDescriptor descriptor) {
         // TODO will be implemented in a followup PR
@@ -613,7 +619,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
                 if (!success) {
                     logger.debug("write to descriptor failed");
                 }
-                ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(success);
+                ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(success);
                 procedureProgress = BlueGigaProcedure.NONE;
                 procedureCharacteristic = null;
                 break;
@@ -622,7 +628,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
                 if (!success) {
                     logger.debug("write to descriptor failed");
                 }
-                ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(!success);
+                ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(!success);
                 procedureProgress = BlueGigaProcedure.NONE;
                 procedureCharacteristic = null;
                 break;
@@ -656,7 +662,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
         }
 
         for (BlueGigaBluetoothCharacteristic ch : handleToCharacteristic.values()) {
-            ch.setNotificationEnabled(false);
+            ch.setNotifying(false);
         }
 
         cancelTimer(procedureTimer);
index f4d78ab591e644538b8b03d189ae98cf472d244c..509f21fa429a1939d28fc8a5b4c1812e1cec3576 100644 (file)
@@ -57,6 +57,7 @@ import com.github.hypfvieh.bluetooth.wrapper.BluetoothGattService;
  *
  * @author Kai Kreuzer - Initial contribution and API
  * @author Benjamin Lafois - Replaced tinyB with bluezDbus
+ * @author Peter Rosenberg - Improve notifications and properties support
  *
  */
 @NonNullByDefault
@@ -399,6 +400,7 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
                 for (BluetoothGattCharacteristic dBusBlueZCharacteristic : dBusBlueZService.getGattCharacteristics()) {
                     BluetoothCharacteristic characteristic = new BluetoothCharacteristic(
                             UUID.fromString(dBusBlueZCharacteristic.getUuid()), 0);
+                    convertCharacteristicProperties(dBusBlueZCharacteristic, characteristic);
 
                     for (BluetoothGattDescriptor dBusBlueZDescriptor : dBusBlueZCharacteristic.getGattDescriptors()) {
                         BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic,
@@ -414,6 +416,42 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
         return true;
     }
 
+    /**
+     * Convert the flags of BluetoothGattCharacteristic to the int bitset used by BluetoothCharacteristic.
+     *
+     * @param dBusBlueZCharacteristic source characteristic to read the flags from
+     * @param characteristic destination characteristic to write to properties to
+     */
+    private void convertCharacteristicProperties(BluetoothGattCharacteristic dBusBlueZCharacteristic,
+            BluetoothCharacteristic characteristic) {
+        int properties = 0;
+
+        for (String property : dBusBlueZCharacteristic.getFlags()) {
+            switch (property) {
+                case "broadcast":
+                    properties |= BluetoothCharacteristic.PROPERTY_BROADCAST;
+                    break;
+                case "read":
+                    properties |= BluetoothCharacteristic.PROPERTY_READ;
+                    break;
+                case "write-without-response":
+                    properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
+                    break;
+                case "write":
+                    properties |= BluetoothCharacteristic.PROPERTY_WRITE;
+                    break;
+                case "notify":
+                    properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
+                    break;
+                case "indicate":
+                    properties |= BluetoothCharacteristic.PROPERTY_INDICATE;
+                    break;
+            }
+        }
+
+        characteristic.setProperties(properties);
+    }
+
     @Override
     public boolean readCharacteristic(BluetoothCharacteristic characteristic) {
         BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
@@ -428,7 +466,8 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
                 characteristic.setValue(value);
                 notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic,
                         BluetoothCompletionStatus.SUCCESS);
-            } catch (DBusException e) {
+            } 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,
@@ -438,6 +477,18 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
         return true;
     }
 
+    @Override
+    public boolean isNotifying(BluetoothCharacteristic characteristic) {
+        BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
+        if (c != null) {
+            Boolean isNotifying = c.isNotifying();
+            return Objects.requireNonNullElse(isNotifying, false);
+        } else {
+            logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
+            return false;
+        }
+    }
+
     @Override
     public boolean disableNotifications(BluetoothCharacteristic characteristic) {
         BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
index 5717b082107298f4f5ac14565fe3b187b340c4d5..633170f4284e4e2a133f7cc478fb3888a5fe3523 100644 (file)
@@ -58,6 +58,7 @@ import org.sputnikdev.bluetooth.gattparser.spec.Field;
  * channels based off of a bluetooth device's GATT characteristics.
  *
  * @author Connor Petty - Initial contribution
+ * @author Peter Rosenberg - Use notifications
  *
  */
 @NonNullByDefault
@@ -68,6 +69,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
     private final Map<ChannelUID, CharacteristicHandler> channelHandlers = new ConcurrentHashMap<>();
     private final BluetoothGattParser gattParser = BluetoothGattParserFactory.getDefault();
     private final CharacteristicChannelTypeProvider channelTypeProvider;
+    private final Map<CharacteristicHandler, List<ChannelUID>> handlerToChannels = new ConcurrentHashMap<>();
 
     private @Nullable ScheduledFuture<?> readCharacteristicJob = null;
 
@@ -84,12 +86,14 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
         readCharacteristicJob = scheduler.scheduleWithFixedDelay(() -> {
             if (device.getConnectionState() == ConnectionState.CONNECTED) {
                 if (resolved) {
-                    for (CharacteristicHandler charHandler : charHandlers.values()) {
-                        if (charHandler.canRead()) {
+                    handlerToChannels.forEach((charHandler, channelUids) -> {
+                        // Only read the value manually if notification is not on.
+                        // Also read it the first time before we activate notifications below.
+                        if (!device.isNotifying(charHandler.characteristic) && charHandler.canRead()) {
                             device.readCharacteristic(charHandler.characteristic);
                             try {
                                 // TODO the ideal solution would be to use locks/conditions and timeouts
-                                // between this code and `onCharacteristicReadComplete` but
+                                // Kbetween this code and `onCharacteristicReadComplete` but
                                 // that would overcomplicate the code a bit and I plan
                                 // on implementing a better more generalized solution later
                                 Thread.sleep(50);
@@ -97,7 +101,20 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
                                 return;
                             }
                         }
-                    }
+                        if (charHandler.characteristic.canNotify()) {
+                            // Enabled/Disable notifications dependent on if the channel is linked.
+                            // TODO check why isLinked() is true for not linked channels
+                            if (channelUids.stream().anyMatch(this::isLinked)) {
+                                if (!device.isNotifying(charHandler.characteristic)) {
+                                    device.enableNotifications(charHandler.characteristic);
+                                }
+                            } else {
+                                if (device.isNotifying(charHandler.characteristic)) {
+                                    device.disableNotifications(charHandler.characteristic);
+                                }
+                            }
+                        }
+                    });
                 } else {
                     // if we are connected and still haven't been able to resolve the services, try disconnecting and
                     // then connecting again
@@ -117,6 +134,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
 
         charHandlers.clear();
         channelHandlers.clear();
+        handlerToChannels.clear();
     }
 
     @Override
@@ -161,9 +179,11 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
                     logger.trace("{} processing characteristic {}", address, characteristic.getUuid());
                     CharacteristicHandler handler = getCharacteristicHandler(characteristic);
                     List<Channel> chans = handler.buildChannels();
-                    for (Channel channel : chans) {
-                        channelHandlers.put(channel.getUID(), handler);
+                    List<ChannelUID> chanUids = chans.stream().map(Channel::getUID).collect(Collectors.toList());
+                    for (ChannelUID channel : chanUids) {
+                        channelHandlers.put(channel, handler);
                     }
+                    handlerToChannels.put(handler, chanUids);
                     return chans.stream();
                 })//
                 .collect(Collectors.toList());
@@ -341,8 +361,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
             if (gattParser.isKnownCharacteristic(charUUID)) {
                 return gattParser.isValidForRead(charUUID);
             }
-            // TODO: need to evaluate this from characteristic properties, but such properties aren't support yet
-            return true;
+            return characteristic.canRead();
         }
 
         public boolean canWrite() {
@@ -350,8 +369,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
             if (gattParser.isKnownCharacteristic(charUUID)) {
                 return gattParser.isValidForWrite(charUUID);
             }
-            // TODO: need to evaluate this from characteristic properties, but such properties aren't support yet
-            return true;
+            return characteristic.canWrite();
         }
 
         private boolean isAdvanced() {
index 0a014ecb36e93e29a393c612b18c7416832c325d..dd591acc723f65058572c72fd283caac64d1a2c6 100644 (file)
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Chris Jackson - Initial contribution
  * @author Kai Kreuzer - Cleaned up code
+ * @author Peter Rosenberg - Improve properties support
  */
 public class BluetoothCharacteristic {
     public static final int FORMAT_UINT8 = 0x11;
@@ -142,6 +143,16 @@ public class BluetoothCharacteristic {
         return instance;
     }
 
+    /**
+     * Set the raw properties. The individual properties are represented as bits inside
+     * of this int value.
+     *
+     * @param properties of this Characteristic
+     */
+    public void setProperties(int properties) {
+        this.properties = properties;
+    }
+
     /**
      * Returns the properties of this characteristic.
      *
@@ -152,6 +163,46 @@ public class BluetoothCharacteristic {
         return properties;
     }
 
+    /**
+     * Returns if the given characteristics property is enabled or not.
+     *
+     * @param property one of the Constants BluetoothCharacteristic.PROPERTY_XX
+     * @return true if this characteristic has the given property enabled, false if properties not set or
+     *         the given property is not enabled.
+     */
+    public boolean hasPropertyEnabled(int property) {
+        return (properties & property) != 0;
+    }
+
+    /**
+     * Returns if notifications can be enabled on this characteristic.
+     *
+     * @return true if notifications can be enabled, false if notifications are not supported, characteristic is missing
+     *         on device or notifications are not supported.
+     */
+    public boolean canNotify() {
+        return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY);
+    }
+
+    /**
+     * Returns if the value can be read on this characteristic.
+     *
+     * @return true if the value can be read, false otherwise.
+     */
+    public boolean canRead() {
+        return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ);
+    }
+
+    /**
+     * Returns if the value can be written on this characteristic.
+     *
+     * @return true if the value can be written with of without a response, false otherwise.
+     */
+    public boolean canWrite() {
+        return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE)
+                || hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
+    }
+
     /**
      * Returns the permissions for this characteristic.
      */
index f9dfde0862b5dd85bd8469afcc657a954bbef0cb..ba1186829f12bf3749b57367ee2861db78bdc7d5 100644 (file)
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
  * @author Chris Jackson - Initial contribution
  * @author Kai Kreuzer - Refactored class to use Integer instead of int, fixed bugs, diverse improvements
  * @author Connor Petty - Made most of the methods abstract
+ * @author Peter Rosenberg - Improve notifications
  */
 @NonNullByDefault
 public abstract class BluetoothDevice {
@@ -249,6 +250,15 @@ public abstract class BluetoothDevice {
      */
     public abstract boolean writeCharacteristic(BluetoothCharacteristic characteristic);
 
+    /**
+     * Returns if notification is enabled for the given characteristic.
+     *
+     * @param characteristic the {@link BluetoothCharacteristic} to check if notifications are enabled.
+     * @return true if notification is enabled, false if notification is disabled, characteristic is missing on device
+     *         or notifications are not supported.
+     */
+    public abstract boolean isNotifying(BluetoothCharacteristic characteristic);
+
     /**
      * Enables notifications for a characteristic. Only a single read or write operation can be requested at once.
      * Attempting to perform an operation when one is already in progress will result in subsequent calls returning
index bb4c37029bf1b59cd45eae9ebfb3e3f1fa677c46..074d9a8f39e7ee74cbbda3252f3cfbfe7a581555 100644 (file)
@@ -112,6 +112,12 @@ public abstract class DelegateBluetoothDevice extends BluetoothDevice {
         return delegate != null && delegate.writeCharacteristic(characteristic);
     }
 
+    @Override
+    public boolean isNotifying(BluetoothCharacteristic characteristic) {
+        BluetoothDevice delegate = getDelegate();
+        return delegate != null ? delegate.isNotifying(characteristic) : false;
+    }
+
     @Override
     public boolean enableNotifications(BluetoothCharacteristic characteristic) {
         BluetoothDevice delegate = getDelegate();
diff --git a/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/CharacteristicPropertiesTest.java
new file mode 100644 (file)
index 0000000..3d40537
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2010-2021 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 static org.junit.jupiter.api.Assertions.*;
+
+import java.util.UUID;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link BluetoothCharacteristic}.
+ *
+ * @author Peter Rosenberg - Initial contribution
+ */
+public class CharacteristicPropertiesTest {
+    private BluetoothCharacteristic characteristic = new BluetoothCharacteristic(UUID.randomUUID(), 0);
+
+    @Test
+    public void testAllSupportedProperties() {
+        // given
+        // when
+        int properties = 0;
+        properties |= BluetoothCharacteristic.PROPERTY_BROADCAST;
+        properties |= BluetoothCharacteristic.PROPERTY_READ;
+        properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
+        properties |= BluetoothCharacteristic.PROPERTY_WRITE;
+        properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
+        properties |= BluetoothCharacteristic.PROPERTY_INDICATE;
+        characteristic.setProperties(properties);
+
+        // then
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST), "Broastcast not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
+                "Write not response not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
+                "Signed write set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
+                "Extended props set");
+    }
+
+    @Test
+    public void testNoProperties() {
+        // given
+        // when
+        int properties = 0;
+        characteristic.setProperties(properties);
+
+        // then
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST),
+                "Broastcast not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
+                "Write not response not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
+                "Signed write set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
+                "Extended props set");
+    }
+
+    @Test
+    public void testSomeSupportedProperties() {
+        // given
+        // when
+        int properties = 0;
+        properties |= BluetoothCharacteristic.PROPERTY_READ;
+        properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
+        characteristic.setProperties(properties);
+
+        // then
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST),
+                "Broastcast not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
+                "Write not response not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
+        assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
+                "Signed write set");
+        assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
+                "Extended props set");
+    }
+}
index d5170cd071df5a7451b23a42fb6d88de5a13d7b1..892362f86c1d296b73c5e74a7c0ea13c6a7d0db2 100644 (file)
@@ -102,6 +102,11 @@ public class MockBluetoothDevice extends BaseBluetoothDevice {
         return false;
     }
 
+    @Override
+    public boolean isNotifying(BluetoothCharacteristic characteristic) {
+        return false;
+    }
+
     @Override
     public boolean disableNotifications(BluetoothCharacteristic characteristic) {
         return false;