Python Bluetooth LE (Low Energy) and GATT Library providing cross-platform BLE operations with multiple backends
npx @tessl/cli install tessl/pypi-pygatt@5.0.0Python 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