Python Bluetooth LE (Low Energy) and GATT Library providing cross-platform BLE operations with multiple backends
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Python Bluetooth LE (Low Energy) and GATT Library that provides a Pythonic API for reading and writing GATT descriptors on BLE devices such as fitness trackers, sensors, and standard GATT-compliant devices. The library wraps two different backends to provide cross-platform compatibility: BlueZ (Linux-based using gatttool) and Bluegiga's BGAPI (compatible with USB adapters like BLED112).
pip install pygattpip install "pygatt[GATTTOOL]" (Linux only, requires pexpect)import pygattMain components:
from pygatt import BGAPIBackend, GATTToolBackend, BLEAddressType, BLEError, BLEDevice
from pygatt.backends import Characteristicimport pygatt
# Initialize BGAPI backend for USB dongle (BLED112, etc.)
adapter = pygatt.BGAPIBackend()
try:
adapter.start()
device = adapter.connect('01:23:45:67:89:ab')
# Read a characteristic
value = device.char_read("a1e8f5b1-696b-4e4c-87c6-69dfe0b0093b")
print(f"Read value: {value.hex()}")
# Write to a characteristic
device.char_write("a1e8f5b1-696b-4e4c-87c6-69dfe0b0093b", bytearray([0x01, 0x02]))
finally:
adapter.stop()import pygatt
# Initialize GATTTool backend for BlueZ
adapter = pygatt.GATTToolBackend()
try:
adapter.start()
device = adapter.connect('01:23:45:67:89:ab')
value = device.char_read("a1e8f5b1-696b-4e4c-87c6-69dfe0b0093b")
finally:
adapter.stop()import pygatt
from binascii import hexlify
adapter = pygatt.GATTToolBackend()
def handle_data(handle, value):
"""
Callback function for notifications
handle -- integer, characteristic handle
value -- bytearray, the notification data
"""
print(f"Received data on handle {handle}: {hexlify(value)}")
try:
adapter.start()
device = adapter.connect('01:23:45:67:89:ab')
# Subscribe to notifications
device.subscribe("a1e8f5b1-696b-4e4c-87c6-69dfe0b0093b", callback=handle_data)
# Keep program running to receive notifications
import time
while True:
time.sleep(1)
finally:
adapter.stop()pygatt uses a backend abstraction pattern to support different BLE communication methods:
Core backend lifecycle and configuration operations for initializing and managing BLE adapters.
class BLEBackend:
def start(self): ...
def stop(self): ...
def supports_unbonded(self) -> bool: ...
def connect(self, address: str, timeout: float = 5.0, **kwargs) -> BLEDevice: ...
def scan(self, *args, **kwargs) -> list: ...
def filtered_scan(self, name_filter: str = "", *args, **kwargs) -> list: ...
def clear_bond(self, address: str = None): ...Core BLE device connection operations including characteristic reading, writing, and subscription management.
class BLEDevice:
def bond(self, permanent: bool = False): ...
def disconnect(self): ...
def get_rssi(self) -> int: ...
def char_read(self, uuid: str) -> bytearray: ...
def char_read_handle(self, handle: int) -> bytearray: ...
def char_read_long(self, uuid: str) -> bytearray: ...
def char_read_long_handle(self, handle: int) -> bytearray: ...
def char_write(self, uuid: str, value: bytearray, wait_for_response: bool = True): ...
def char_write_handle(self, handle: int, value: bytearray, wait_for_response: bool = True): ...
def char_write_long(self, uuid: str, value: bytearray, wait_for_response: bool = False): ...
def char_write_long_handle(self, handle: int, value: bytearray, wait_for_response: bool = False): ...
def subscribe(self, uuid: str, callback=None, indication: bool = False, wait_for_response: bool = True): ...
def unsubscribe(self, uuid: str, wait_for_response: bool = True): ...
def subscribe_handle(self, handle: int, callback=None, indication: bool = False, wait_for_response: bool = True): ...
def unsubscribe_handle(self, handle: int, wait_for_response: bool = True): ...
def get_handle(self, char_uuid: str) -> int: ...
def discover_characteristics(self) -> dict: ...
def exchange_mtu(self, mtu: int) -> int: ...
def resubscribe_all(self): ...BGAPI-specific backend implementation for USB dongles with advanced configuration options and low-level protocol access.
class BGAPIBackend(BLEBackend):
def __init__(self, serial_port: str = None, receive_queue_timeout: float = 0.1): ...
def start(self, reset: bool = True, delay_after_reset_s: float = 1): ...
def get_mac(self) -> str: ...
def scan(self, timeout: int = 10, scan_interval: int = 75, scan_window: int = 50, active: bool = True, **kwargs): ...
def set_bondable(self, bondable: bool): ...
def discover_characteristics(self, connection_handle: int, timeout: int = 30) -> dict: ...GATTTool-specific backend implementation for Linux BlueZ with CLI integration and advanced debugging capabilities.
class GATTToolBackend(BLEBackend):
def __init__(self, hci_device: str = "hci0", gatttool_logfile: str = None, cli_options: list = None, search_window_size: int = None, max_read: int = None): ...
def start(self, reset_on_start: bool = True, initialization_timeout: int = 3): ...
def scan(self, timeout: int = 10, run_as_root: bool = False) -> list: ...
def reset(self): ...
def sendline(self, command: str): ...Exception classes and error management for comprehensive BLE error handling and debugging.
class BLEError(Exception): ...
class NotConnectedError(BLEError): ...
class NotificationTimeout(BLEError):
def __init__(self, msg: str = None, gatttool_output: str = None): ...
class BGAPIError(Exception): ...
class ExpectedResponseTimeout(BGAPIError):
def __init__(self, expected_packets, timeout): ...Utility functions and classes for UUID conversion, device discovery, and USB serial device management.
def uuid16_to_uuid(uuid16: int) -> UUID: ...
def find_usb_serial_devices(vendor_id: int = None, product_id: int = None) -> list: ...
def extract_vid_pid(info_string: str) -> tuple: ...from enum import Enum
from uuid import UUID
from collections import defaultdict
class BLEAddressType(Enum):
public = "public"
random = "random"
class Characteristic:
def __init__(self, uuid: UUID, handle: int):
self.uuid = uuid
self.handle = handle
self.descriptors = {}
def add_descriptor(self, uuid: str, handle: int): ...
class USBSerialDeviceInfo:
def __init__(self, device_name: str, port_name: str, vendor_id: int, product_id: int):
self.device_name = device_name
self.port_name = port_name
self.vendor_id = vendor_id
self.product_id = product_id