0
# GATTTool Backend
1
2
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.
3
4
## Capabilities
5
6
### Initialization and Configuration
7
8
Initialize GATTTool backend with BlueZ adapter configuration and CLI parameters.
9
10
```python { .api }
11
def __init__(self, hci_device: str = "hci0", gatttool_logfile: str = None,
12
cli_options: list = None, search_window_size: int = None, max_read: int = None):
13
"""
14
Initialize GATTTool backend.
15
16
Args:
17
hci_device: Bluetooth adapter identifier (e.g., 'hci0', 'hci1')
18
gatttool_logfile: Path to log gatttool CLI interactions
19
cli_options: Additional gatttool command-line options
20
search_window_size: Pexpect search buffer size for characteristic discovery
21
max_read: Maximum bytes to read from gatttool output
22
"""
23
24
def start(self, reset_on_start: bool = True, initialization_timeout: int = 3):
25
"""
26
Start GATTTool backend and initialize CLI session.
27
28
Args:
29
reset_on_start: Reset Bluetooth adapter on startup
30
initialization_timeout: CLI initialization timeout in seconds
31
32
Raises:
33
BLEError: gatttool not found or initialization failed
34
"""
35
```
36
37
**Usage Example:**
38
39
```python
40
import pygatt
41
42
# Basic initialization
43
adapter = pygatt.GATTToolBackend()
44
45
# Advanced configuration
46
adapter = pygatt.GATTToolBackend(
47
hci_device="hci1", # Use second Bluetooth adapter
48
gatttool_logfile="/tmp/gatttool.log", # Enable CLI logging
49
search_window_size=2048, # Larger buffer for many characteristics
50
max_read=4096 # Increase read buffer
51
)
52
53
adapter.start(reset_on_start=True, initialization_timeout=5)
54
```
55
56
### System Integration
57
58
Direct integration with Linux BlueZ stack and system-level Bluetooth management.
59
60
```python { .api }
61
def scan(self, timeout: int = 10, run_as_root: bool = False) -> list:
62
"""
63
Perform BLE device scan using BlueZ HCI commands.
64
65
Args:
66
timeout: Scan duration in seconds
67
run_as_root: Run scan with sudo privileges (may be required)
68
69
Returns:
70
list: Discovered devices with address, name, and RSSI
71
72
Raises:
73
BLEError: Scan failed, possibly due to permissions
74
75
Note:
76
May require root privileges depending on system configuration
77
"""
78
79
def reset(self):
80
"""
81
Reset the Bluetooth adapter using HCI commands.
82
83
Useful for recovering from stuck states or connection issues.
84
85
Raises:
86
BLEError: Reset failed or adapter not available
87
"""
88
89
def clear_bond(self, address: str = None):
90
"""
91
Clear stored bonding information using bluetoothctl.
92
93
Args:
94
address: Specific device address, or None for all bonds
95
96
Note:
97
Uses system-level bluetoothctl command to manage bonds
98
"""
99
```
100
101
**Usage Example:**
102
103
```python
104
import pygatt
105
106
adapter = pygatt.GATTToolBackend()
107
adapter.start()
108
109
# Scan with elevated privileges if needed
110
devices = adapter.scan(timeout=15, run_as_root=True)
111
for device in devices:
112
print(f"Found: {device['address']} - {device.get('name', 'Unknown')}")
113
114
# Reset adapter if having connection issues
115
adapter.reset()
116
```
117
118
### Low-Level CLI Access
119
120
Direct access to gatttool command-line interface for advanced operations and debugging.
121
122
```python { .api }
123
def sendline(self, command: str):
124
"""
125
Send raw command to gatttool CLI.
126
127
Args:
128
command: gatttool command string
129
130
Returns:
131
Command response output
132
133
Raises:
134
BLEError: Command failed or CLI not available
135
"""
136
137
def char_read(self, uuid: str, timeout: int = 1) -> bytearray:
138
"""
139
Read characteristic using gatttool CLI.
140
141
Args:
142
uuid: Characteristic UUID
143
timeout: Read timeout in seconds
144
145
Returns:
146
bytearray: Characteristic value
147
"""
148
149
def char_read_handle(self, handle: int, timeout: int = 4) -> bytearray:
150
"""
151
Read characteristic by handle using gatttool.
152
153
Args:
154
handle: Characteristic handle
155
timeout: Read timeout in seconds
156
157
Returns:
158
bytearray: Characteristic value
159
"""
160
161
def char_write_handle(self, handle: int, value: bytearray,
162
wait_for_response: bool = True, timeout: int = 30):
163
"""
164
Write to characteristic using gatttool CLI.
165
166
Args:
167
handle: Characteristic handle
168
value: Data to write
169
wait_for_response: Wait for write confirmation
170
timeout: Write timeout in seconds
171
172
Raises:
173
BLEError: Write failed or timed out
174
"""
175
```
176
177
**Usage Example:**
178
179
```python
180
import pygatt
181
182
adapter = pygatt.GATTToolBackend()
183
adapter.start()
184
device = adapter.connect('01:23:45:67:89:ab')
185
186
# Send raw gatttool command
187
adapter.sendline('char-desc') # List characteristic descriptors
188
189
# Direct characteristic operations
190
value = adapter.char_read('00002a19-0000-1000-8000-00805f9b34fb', timeout=2)
191
adapter.char_write_handle(42, bytearray([0x01, 0x00]), timeout=5)
192
```
193
194
### Service Discovery
195
196
Comprehensive GATT service and characteristic discovery with BlueZ integration.
197
198
```python { .api }
199
def discover_characteristics(self, timeout: int = 5) -> dict:
200
"""
201
Discover characteristics using gatttool CLI.
202
203
Args:
204
timeout: Discovery timeout in seconds
205
206
Returns:
207
dict: Mapping of UUID to Characteristic objects
208
209
Note:
210
Uses gatttool's 'char-desc' command for comprehensive discovery
211
"""
212
213
def exchange_mtu(self, mtu: int, timeout: int = 1) -> int:
214
"""
215
Exchange MTU using gatttool CLI.
216
217
Args:
218
mtu: Requested MTU size
219
timeout: Exchange timeout in seconds
220
221
Returns:
222
int: Negotiated MTU size
223
"""
224
```
225
226
### Connection Management
227
228
Advanced connection handling with auto-reconnect and disconnect callback support.
229
230
```python { .api }
231
def connect(self, address: str, timeout: float = DEFAULT_CONNECT_TIMEOUT_S,
232
address_type=BLEAddressType.public, auto_reconnect: bool = False) -> GATTToolBLEDevice:
233
"""
234
Connect to device with GATTTool-specific options.
235
236
Args:
237
address: Device MAC address
238
timeout: Connection timeout in seconds
239
address_type: BLEAddressType.public or BLEAddressType.random
240
auto_reconnect: Automatically reconnect on connection loss
241
242
Returns:
243
GATTToolBLEDevice: Connected device with GATTTool features
244
245
Raises:
246
NotConnectedError: Connection failed
247
"""
248
249
def kill(self):
250
"""
251
Terminate any running scan processes cleanly.
252
253
Useful for stopping scans that may interfere with connections.
254
"""
255
```
256
257
**Usage Example:**
258
259
```python
260
# Connect with auto-reconnect for robust applications
261
device = adapter.connect('01:23:45:67:89:ab',
262
auto_reconnect=True,
263
timeout=10)
264
265
# Stop any background scans
266
adapter.kill()
267
```
268
269
## GATTTool Device Features
270
271
The GATTToolBLEDevice class extends BLEDevice with GATTTool-specific enhancements:
272
273
### Disconnect Callbacks
274
275
```python { .api }
276
def register_disconnect_callback(self, callback):
277
"""
278
Register callback for disconnect events.
279
280
Args:
281
callback: Function called on disconnect: callback(device)
282
283
Useful for implementing reconnection logic or cleanup operations.
284
"""
285
286
def remove_disconnect_callback(self, callback):
287
"""
288
Remove previously registered disconnect callback.
289
290
Args:
291
callback: Callback function to remove
292
"""
293
```
294
295
**Usage Example:**
296
297
```python
298
def on_disconnect(device):
299
print(f"Device {device._address} disconnected!")
300
# Implement reconnection logic here
301
302
device = adapter.connect('01:23:45:67:89:ab')
303
device.register_disconnect_callback(on_disconnect)
304
```
305
306
## System Requirements
307
308
### Linux Dependencies
309
310
- **BlueZ 5.18+**: Bluetooth stack (tested on 5.18, 5.21, 5.35, 5.43)
311
- **gatttool**: Command-line GATT utility (part of BlueZ)
312
- **pexpect**: Python process interaction library
313
- **sudo access**: Required for some operations (scanning, reset)
314
315
### Installation
316
317
```bash
318
# Install with GATTTool support
319
pip install "pygatt[GATTTOOL]"
320
321
# Verify gatttool is available
322
which gatttool
323
324
# Check BlueZ version
325
bluetoothctl --version
326
```
327
328
### Permissions Configuration
329
330
#### udev Rules (Recommended)
331
332
Create `/etc/udev/rules.d/99-ble.rules`:
333
334
```
335
# Allow users in 'bluetooth' group to access BLE without sudo
336
KERNEL=="hci[0-9]*", GROUP="bluetooth", MODE="0664"
337
SUBSYSTEM=="bluetooth", GROUP="bluetooth", MODE="0664"
338
```
339
340
Add user to bluetooth group:
341
342
```bash
343
sudo usermod -a -G bluetooth $USER
344
```
345
346
#### Capabilities (Alternative)
347
348
Grant specific capabilities to Python interpreter:
349
350
```bash
351
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(which python3)
352
```
353
354
### Troubleshooting
355
356
#### Scan Permissions
357
358
```python
359
# If regular scan fails, try with sudo
360
try:
361
devices = adapter.scan(timeout=10)
362
except pygatt.BLEError:
363
devices = adapter.scan(timeout=10, run_as_root=True)
364
```
365
366
#### Supervision Timeout Issues
367
368
```bash
369
# Increase supervision timeout for unstable connections
370
echo 1000 > /sys/kernel/debug/bluetooth/hci0/supervision_timeout
371
```
372
373
#### CLI Buffer Size
374
375
```python
376
# For devices with many characteristics
377
adapter = pygatt.GATTToolBackend(search_window_size=2048)
378
```
379
380
## Advanced Configuration
381
382
### Custom CLI Options
383
384
```python
385
# Add custom gatttool parameters
386
adapter = pygatt.GATTToolBackend(
387
cli_options=['--sec-level=medium', '--connect-timeout=30']
388
)
389
```
390
391
### Multiple Adapters
392
393
```python
394
# Use specific Bluetooth adapter
395
adapter1 = pygatt.GATTToolBackend(hci_device="hci0")
396
adapter2 = pygatt.GATTToolBackend(hci_device="hci1")
397
```
398
399
### Debug Logging
400
401
```python
402
# Enable comprehensive logging
403
import logging
404
logging.basicConfig(level=logging.DEBUG)
405
logging.getLogger('pygatt').setLevel(logging.DEBUG)
406
407
adapter = pygatt.GATTToolBackend(gatttool_logfile="/tmp/debug.log")
408
```
409
410
## Error Handling
411
412
Common GATTTool backend errors and solutions:
413
414
### Permission Errors
415
416
```python
417
try:
418
devices = adapter.scan()
419
except pygatt.BLEError as e:
420
if "permission" in str(e).lower():
421
print("Try: sudo python script.py")
422
print("Or configure udev rules for bluetooth group")
423
```
424
425
### CLI Timeout Issues
426
427
```python
428
# Increase timeouts for slow systems
429
adapter = pygatt.GATTToolBackend()
430
adapter.start(initialization_timeout=10)
431
432
# Longer read timeouts
433
value = adapter.char_read(uuid, timeout=5)
434
```
435
436
### Connection Stability
437
438
```python
439
# Enable auto-reconnect for unreliable connections
440
device = adapter.connect(address, auto_reconnect=True)
441
442
# Register disconnect handler
443
def reconnect_handler(device):
444
time.sleep(1)
445
new_device = adapter.connect(device._address, auto_reconnect=True)
446
447
device.register_disconnect_callback(reconnect_handler)
448
```