Skip to content

Commit

Permalink
Added a backend for python-for-android
Browse files Browse the repository at this point in the history
and an example for kivy buildozer on android
  • Loading branch information
xloem committed Jan 15, 2021
1 parent e0a8a95 commit 7a0ed93
Show file tree
Hide file tree
Showing 17 changed files with 1,913 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Fixed
* Fixed ``BaseBleakClient.services_resolved`` not reset on disconnect on BlueZ
backend. Merged #401.
* Fixed RSSI missing in discovered devices on macOS backend. Merged #400.
* Added python-for-android backend.


`0.10.0`_ (2020-12-11)
Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Features
* Supports Windows 10, version 16299 (Fall Creators Update) or greater
* Supports Linux distributions with BlueZ >= 5.43
* OS X/macOS support via Core Bluetooth API, from at least OS X version 10.11
* Android backend compatible with python-for-android

Bleak supports reading, writing and getting notifications from
GATT servers, as well as a function for discovering BLE devices.
Expand Down
20 changes: 16 additions & 4 deletions bleak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
from bleak.__version__ import __version__ # noqa
from bleak.exc import BleakError


_on_rtd = os.environ.get("READTHEDOCS") == "True"
if os.environ.get("P4A_BOOTSTRAP") is not None:
_system = "Python4Android"
else:
_system = platform.system()

_logger = logging.getLogger(__name__)
_logger.addHandler(logging.NullHandler())
Expand All @@ -28,7 +33,7 @@
_logger.addHandler(handler)
_logger.setLevel(logging.DEBUG)

if platform.system() == "Linux":
if _system == "Linux":
if not _on_rtd:
# TODO: Check if BlueZ version 5.43 is sufficient.
p = subprocess.Popen(["bluetoothctl", "--version"], stdout=subprocess.PIPE)
Expand All @@ -49,7 +54,14 @@
from bleak.backends.bluezdbus.client import (
BleakClientBlueZDBus as BleakClient,
) # noqa: F401
elif platform.system() == "Darwin":
elif _system == "Python4Android":
from bleak.backends.p4android.scanner import (
BleakScannerP4Android as BleakScanner,
) # noqa: F401
from bleak.backends.p4android.client import (
BleakClientP4Android as BleakClient,
) # noqa: F401
elif _system == "Darwin":
try:
from CoreBluetooth import CBPeripheral # noqa: F401
except Exception as ex:
Expand All @@ -62,7 +74,7 @@
BleakClientCoreBluetooth as BleakClient,
) # noqa: F401

elif platform.system() == "Windows":
elif _system == "Windows":
# Requires Windows 10 Creators update at least, i.e. Window 10.0.16299
_vtup = platform.win32_ver()[1].split(".")
if int(_vtup[0]) != 10:
Expand All @@ -80,7 +92,7 @@
from bleak.backends.dotnet.scanner import BleakScannerDotNet as BleakScanner # noqa
from bleak.backends.dotnet.client import BleakClientDotNet as BleakClient # noqa
else:
raise BleakError(f"Unsupported platform: {platform.system()}")
raise BleakError(f"Unsupported platform: {_system}")

# for backward compatibility
discover = BleakScanner.discover
Expand Down
Empty file.
102 changes: 102 additions & 0 deletions bleak/backends/p4android/characteristic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from uuid import UUID
from typing import Union, List

from bleak.backends.characteristic import BleakGATTCharacteristic
from bleak.backends.descriptor import BleakGATTDescriptor
from bleak.exc import BleakError

from jnius import autoclass


class _java:
BluetoothGattCharacteristic = autoclass(
"android.bluetooth.BluetoothGattCharacteristic"
)

CHARACTERISTIC_PROPERTY_DBUS_NAMES = {
BluetoothGattCharacteristic.PROPERTY_BROADCAST: "broadcast",
BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS: "extended-properties",
BluetoothGattCharacteristic.PROPERTY_INDICATE: "indicate",
BluetoothGattCharacteristic.PROPERTY_NOTIFY: "notify",
BluetoothGattCharacteristic.PROPERTY_READ: "read",
BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE: "authenticated-signed-writes",
BluetoothGattCharacteristic.PROPERTY_WRITE: "write",
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE: "write-without-response",
}

NOTIFICATION_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb"


class BleakGATTCharacteristicP4Android(BleakGATTCharacteristic):
"""GATT Characteristic implementation for the python-for-android backend"""

def __init__(self, java, service_uuid: str):
super(BleakGATTCharacteristicP4Android, self).__init__(java)
self.__uuid = self.obj.getUuid().toString()
self.__handle = self.obj.getInstanceId()
self.__service_uuid = service_uuid
self.__descriptors = []
self.__notification_descriptor = None

self.__properties = [
name
for flag, name in _java.CHARACTERISTIC_PROPERTY_DBUS_NAMES.items()
if flag & self.obj.getProperties()
]

@property
def service_uuid(self) -> str:
"""The uuid of the Service containing this characteristic"""
return self.__service_uuid

@property
def handle(self) -> int:
"""The handle of this characteristic"""
return self.__handle

@property
def uuid(self) -> str:
"""The uuid of this characteristic"""
return self.__uuid

@property
def properties(self) -> List:
"""Properties of this characteristic"""
return self.__properties

@property
def descriptors(self) -> List:
"""List of descriptors for this service"""
return self.__descriptors

def get_descriptor(
self, specifier: Union[str, UUID]
) -> Union[BleakGATTDescriptor, None]:
"""Get a descriptor by UUID (str or uuid.UUID)"""
if isinstance(specifier, int):
raise BleakError(
"The Android Bluetooth API does not provide access to descriptor handles."
)

matches = [
descriptor
for descriptor in self.descriptors
if descriptor.uuid == str(specifier)
]
if len(matches) == 0:
return None
return matches[0]

def add_descriptor(self, descriptor: BleakGATTDescriptor):
"""Add a :py:class:`~BleakGATTDescriptor` to the characteristic.
Should not be used by end user, but rather by `bleak` itself.
"""
self.__descriptors.append(descriptor)
if descriptor.uuid == _java.NOTIFICATION_DESCRIPTOR_UUID:
self.__notification_descriptor = descriptor

@property
def notification_descriptor(self) -> BleakGATTDescriptor:
"""The notification descriptor. Mostly needed by `bleak`, not by end user"""
return self.__notification_descriptor
Loading

0 comments on commit 7a0ed93

Please sign in to comment.