Python Bluetooth LE (Low Energy) and GATT Library providing cross-platform BLE operations with multiple backends
—
GATTTool-specific backend implementation for Linux BlueZ with CLI integration and advanced debugging capabilities. The GATTToolBackend provides native Linux BLE connectivity using the system's BlueZ stack and gatttool command-line utility.
Initialize GATTTool backend with BlueZ adapter configuration and CLI parameters.
def __init__(self, hci_device: str = "hci0", gatttool_logfile: str = None,
cli_options: list = None, search_window_size: int = None, max_read: int = None):
"""
Initialize GATTTool backend.
Args:
hci_device: Bluetooth adapter identifier (e.g., 'hci0', 'hci1')
gatttool_logfile: Path to log gatttool CLI interactions
cli_options: Additional gatttool command-line options
search_window_size: Pexpect search buffer size for characteristic discovery
max_read: Maximum bytes to read from gatttool output
"""
def start(self, reset_on_start: bool = True, initialization_timeout: int = 3):
"""
Start GATTTool backend and initialize CLI session.
Args:
reset_on_start: Reset Bluetooth adapter on startup
initialization_timeout: CLI initialization timeout in seconds
Raises:
BLEError: gatttool not found or initialization failed
"""Usage Example:
import pygatt
# Basic initialization
adapter = pygatt.GATTToolBackend()
# Advanced configuration
adapter = pygatt.GATTToolBackend(
hci_device="hci1", # Use second Bluetooth adapter
gatttool_logfile="/tmp/gatttool.log", # Enable CLI logging
search_window_size=2048, # Larger buffer for many characteristics
max_read=4096 # Increase read buffer
)
adapter.start(reset_on_start=True, initialization_timeout=5)Direct integration with Linux BlueZ stack and system-level Bluetooth management.
def scan(self, timeout: int = 10, run_as_root: bool = False) -> list:
"""
Perform BLE device scan using BlueZ HCI commands.
Args:
timeout: Scan duration in seconds
run_as_root: Run scan with sudo privileges (may be required)
Returns:
list: Discovered devices with address, name, and RSSI
Raises:
BLEError: Scan failed, possibly due to permissions
Note:
May require root privileges depending on system configuration
"""
def reset(self):
"""
Reset the Bluetooth adapter using HCI commands.
Useful for recovering from stuck states or connection issues.
Raises:
BLEError: Reset failed or adapter not available
"""
def clear_bond(self, address: str = None):
"""
Clear stored bonding information using bluetoothctl.
Args:
address: Specific device address, or None for all bonds
Note:
Uses system-level bluetoothctl command to manage bonds
"""Usage Example:
import pygatt
adapter = pygatt.GATTToolBackend()
adapter.start()
# Scan with elevated privileges if needed
devices = adapter.scan(timeout=15, run_as_root=True)
for device in devices:
print(f"Found: {device['address']} - {device.get('name', 'Unknown')}")
# Reset adapter if having connection issues
adapter.reset()Direct access to gatttool command-line interface for advanced operations and debugging.
def sendline(self, command: str):
"""
Send raw command to gatttool CLI.
Args:
command: gatttool command string
Returns:
Command response output
Raises:
BLEError: Command failed or CLI not available
"""
def char_read(self, uuid: str, timeout: int = 1) -> bytearray:
"""
Read characteristic using gatttool CLI.
Args:
uuid: Characteristic UUID
timeout: Read timeout in seconds
Returns:
bytearray: Characteristic value
"""
def char_read_handle(self, handle: int, timeout: int = 4) -> bytearray:
"""
Read characteristic by handle using gatttool.
Args:
handle: Characteristic handle
timeout: Read timeout in seconds
Returns:
bytearray: Characteristic value
"""
def char_write_handle(self, handle: int, value: bytearray,
wait_for_response: bool = True, timeout: int = 30):
"""
Write to characteristic using gatttool CLI.
Args:
handle: Characteristic handle
value: Data to write
wait_for_response: Wait for write confirmation
timeout: Write timeout in seconds
Raises:
BLEError: Write failed or timed out
"""Usage Example:
import pygatt
adapter = pygatt.GATTToolBackend()
adapter.start()
device = adapter.connect('01:23:45:67:89:ab')
# Send raw gatttool command
adapter.sendline('char-desc') # List characteristic descriptors
# Direct characteristic operations
value = adapter.char_read('00002a19-0000-1000-8000-00805f9b34fb', timeout=2)
adapter.char_write_handle(42, bytearray([0x01, 0x00]), timeout=5)Comprehensive GATT service and characteristic discovery with BlueZ integration.
def discover_characteristics(self, timeout: int = 5) -> dict:
"""
Discover characteristics using gatttool CLI.
Args:
timeout: Discovery timeout in seconds
Returns:
dict: Mapping of UUID to Characteristic objects
Note:
Uses gatttool's 'char-desc' command for comprehensive discovery
"""
def exchange_mtu(self, mtu: int, timeout: int = 1) -> int:
"""
Exchange MTU using gatttool CLI.
Args:
mtu: Requested MTU size
timeout: Exchange timeout in seconds
Returns:
int: Negotiated MTU size
"""Advanced connection handling with auto-reconnect and disconnect callback support.
def connect(self, address: str, timeout: float = DEFAULT_CONNECT_TIMEOUT_S,
address_type=BLEAddressType.public, auto_reconnect: bool = False) -> GATTToolBLEDevice:
"""
Connect to device with GATTTool-specific options.
Args:
address: Device MAC address
timeout: Connection timeout in seconds
address_type: BLEAddressType.public or BLEAddressType.random
auto_reconnect: Automatically reconnect on connection loss
Returns:
GATTToolBLEDevice: Connected device with GATTTool features
Raises:
NotConnectedError: Connection failed
"""
def kill(self):
"""
Terminate any running scan processes cleanly.
Useful for stopping scans that may interfere with connections.
"""Usage Example:
# Connect with auto-reconnect for robust applications
device = adapter.connect('01:23:45:67:89:ab',
auto_reconnect=True,
timeout=10)
# Stop any background scans
adapter.kill()The GATTToolBLEDevice class extends BLEDevice with GATTTool-specific enhancements:
def register_disconnect_callback(self, callback):
"""
Register callback for disconnect events.
Args:
callback: Function called on disconnect: callback(device)
Useful for implementing reconnection logic or cleanup operations.
"""
def remove_disconnect_callback(self, callback):
"""
Remove previously registered disconnect callback.
Args:
callback: Callback function to remove
"""Usage Example:
def on_disconnect(device):
print(f"Device {device._address} disconnected!")
# Implement reconnection logic here
device = adapter.connect('01:23:45:67:89:ab')
device.register_disconnect_callback(on_disconnect)# Install with GATTTool support
pip install "pygatt[GATTTOOL]"
# Verify gatttool is available
which gatttool
# Check BlueZ version
bluetoothctl --versionCreate /etc/udev/rules.d/99-ble.rules:
# Allow users in 'bluetooth' group to access BLE without sudo
KERNEL=="hci[0-9]*", GROUP="bluetooth", MODE="0664"
SUBSYSTEM=="bluetooth", GROUP="bluetooth", MODE="0664"Add user to bluetooth group:
sudo usermod -a -G bluetooth $USERGrant specific capabilities to Python interpreter:
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(which python3)# If regular scan fails, try with sudo
try:
devices = adapter.scan(timeout=10)
except pygatt.BLEError:
devices = adapter.scan(timeout=10, run_as_root=True)# Increase supervision timeout for unstable connections
echo 1000 > /sys/kernel/debug/bluetooth/hci0/supervision_timeout# For devices with many characteristics
adapter = pygatt.GATTToolBackend(search_window_size=2048)# Add custom gatttool parameters
adapter = pygatt.GATTToolBackend(
cli_options=['--sec-level=medium', '--connect-timeout=30']
)# Use specific Bluetooth adapter
adapter1 = pygatt.GATTToolBackend(hci_device="hci0")
adapter2 = pygatt.GATTToolBackend(hci_device="hci1")# Enable comprehensive logging
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('pygatt').setLevel(logging.DEBUG)
adapter = pygatt.GATTToolBackend(gatttool_logfile="/tmp/debug.log")Common GATTTool backend errors and solutions:
try:
devices = adapter.scan()
except pygatt.BLEError as e:
if "permission" in str(e).lower():
print("Try: sudo python script.py")
print("Or configure udev rules for bluetooth group")# Increase timeouts for slow systems
adapter = pygatt.GATTToolBackend()
adapter.start(initialization_timeout=10)
# Longer read timeouts
value = adapter.char_read(uuid, timeout=5)# Enable auto-reconnect for unreliable connections
device = adapter.connect(address, auto_reconnect=True)
# Register disconnect handler
def reconnect_handler(device):
time.sleep(1)
new_device = adapter.connect(device._address, auto_reconnect=True)
device.register_disconnect_callback(reconnect_handler)Install with Tessl CLI
npx tessl i tessl/pypi-pygatt