CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pygatt

Python Bluetooth LE (Low Energy) and GATT Library providing cross-platform BLE operations with multiple backends

Pending
Overview
Eval results
Files

gatttool-backend.mddocs/

GATTTool Backend

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.

Capabilities

Initialization and Configuration

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)

System Integration

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

Low-Level CLI Access

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)

Service Discovery

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
    """

Connection Management

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

GATTTool Device Features

The GATTToolBLEDevice class extends BLEDevice with GATTTool-specific enhancements:

Disconnect Callbacks

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)

System Requirements

Linux Dependencies

  • BlueZ 5.18+: Bluetooth stack (tested on 5.18, 5.21, 5.35, 5.43)
  • gatttool: Command-line GATT utility (part of BlueZ)
  • pexpect: Python process interaction library
  • sudo access: Required for some operations (scanning, reset)

Installation

# Install with GATTTool support
pip install "pygatt[GATTTOOL]"

# Verify gatttool is available
which gatttool

# Check BlueZ version
bluetoothctl --version

Permissions Configuration

udev Rules (Recommended)

Create /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 $USER

Capabilities (Alternative)

Grant specific capabilities to Python interpreter:

sudo setcap 'cap_net_raw,cap_net_admin+eip' $(which python3)

Troubleshooting

Scan Permissions

# 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)

Supervision Timeout Issues

# Increase supervision timeout for unstable connections
echo 1000 > /sys/kernel/debug/bluetooth/hci0/supervision_timeout

CLI Buffer Size

# For devices with many characteristics
adapter = pygatt.GATTToolBackend(search_window_size=2048)

Advanced Configuration

Custom CLI Options

# Add custom gatttool parameters
adapter = pygatt.GATTToolBackend(
    cli_options=['--sec-level=medium', '--connect-timeout=30']
)

Multiple Adapters

# Use specific Bluetooth adapter
adapter1 = pygatt.GATTToolBackend(hci_device="hci0")
adapter2 = pygatt.GATTToolBackend(hci_device="hci1")

Debug Logging

# Enable comprehensive logging
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('pygatt').setLevel(logging.DEBUG)

adapter = pygatt.GATTToolBackend(gatttool_logfile="/tmp/debug.log")

Error Handling

Common GATTTool backend errors and solutions:

Permission Errors

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")

CLI Timeout Issues

# Increase timeouts for slow systems
adapter = pygatt.GATTToolBackend()
adapter.start(initialization_timeout=10)

# Longer read timeouts
value = adapter.char_read(uuid, timeout=5)

Connection Stability

# 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

docs

backend-management.md

bgapi-backend.md

device-operations.md

error-handling.md

gatttool-backend.md

index.md

utilities.md

tile.json