[bluetooth.generic] Enable BLE notification for linked channels (#10122)

* [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>
This commit is contained in:
Pete
2021-02-16 21:26:34 +01:00
committed by GitHub
parent 596b261d47
commit 7abeb97396
9 changed files with 270 additions and 22 deletions

View 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.
*/

View 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

View 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();

View File

@@ -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");
}
}

View 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;