Switch to bleak for reliable BLE scanning with RSSI

- Replace hcitool-based BT scanning with bleak Python library
- Bleak provides reliable RSSI values via D-Bus/BlueZ
- BLE scan now finds devices that hcitool missed
- Update project docs to reflect resolved BT RSSI blocker
- Add bleak>=0.21.0 to dependencies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
User
2026-02-01 00:17:47 +01:00
parent 52df6421be
commit 0e99232582
6 changed files with 35 additions and 61 deletions

View File

@@ -59,8 +59,8 @@ Understanding the RF environment around you is useful for:
┌─────────────────────────┴───────────────────────────────────┐ ┌─────────────────────────┴───────────────────────────────────┐
│ System Tools │ │ System Tools │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ iw │ │ hcitool │ │bluetoothctl│ │ │ iw │ │ bleak │ │ SQLite │
│ │ (WiFi) │ │ (BT) │ │ (BLE) │ │ │ │ (WiFi) │ │ (BLE) │ │(History) │ │
│ └──────────┘ └──────────┘ └──────────┘ │ │ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
``` ```
@@ -78,6 +78,7 @@ Understanding the RF environment around you is useful for:
- Flask - Web framework - Flask - Web framework
- PyYAML - Configuration - PyYAML - Configuration
- dataclasses - Data structures - dataclasses - Data structures
- bleak - BLE scanning with RSSI
### Frontend ### Frontend
- Leaflet.js - 2D maps - Leaflet.js - 2D maps

View File

@@ -41,7 +41,7 @@
- [x] Live BT tracking mode - [x] Live BT tracking mode
- [x] Moving device detection (purple markers) - [x] Moving device detection (purple markers)
- [x] Filter-aware scanning (WiFi/BT toggle) - [x] Filter-aware scanning (WiFi/BT toggle)
- [ ] Reliable RSSI acquisition for movement tracking - [x] Reliable RSSI acquisition for movement tracking (bleak)
- [ ] Position smoothing/averaging - [ ] Position smoothing/averaging
- [ ] Device trails/history visualization - [ ] Device trails/history visualization

View File

@@ -23,7 +23,7 @@
| Status | Task | Notes | | Status | Task | Notes |
|--------|------|-------| |--------|------|-------|
| [-] | Fix Bluetooth RSSI acquisition | `hcitool rssi` only works for connected devices; `bluetoothctl` doesn't expose RSSI for cached devices | | [x] | Fix Bluetooth RSSI acquisition | Switched to `bleak` Python library for reliable BLE scanning with RSSI |
--- ---
@@ -38,7 +38,7 @@
| [x] | Live BT tracking mode | 4-second scan interval | | [x] | Live BT tracking mode | 4-second scan interval |
| [x] | Moving device detection | Purple markers for RSSI changes | | [x] | Moving device detection | Purple markers for RSSI changes |
| [x] | Filter-aware scanning | Skip WiFi/BT based on toggle | | [x] | Filter-aware scanning | Skip WiFi/BT based on toggle |
| [~] | Improve BT discovery reliability | Try alternative scanning methods | | [x] | Improve BT discovery reliability | Using bleak library for BLE scanning |
| [ ] | Document API endpoints | docs/API.md | | [ ] | Document API endpoints | docs/API.md |
| [ ] | Create CHEATSHEET.md | Quick reference guide | | [ ] | Create CHEATSHEET.md | Quick reference guide |
@@ -78,28 +78,28 @@
| Live tracking button | 2026-01-31 | | Live tracking button | 2026-01-31 |
| Purple moving indicators | 2026-01-31 | | Purple moving indicators | 2026-01-31 |
| Smart scanning (filter-aware) | 2026-01-31 | | Smart scanning (filter-aware) | 2026-01-31 |
| SQLite historical database | 2026-02-01 |
| Bleak BLE scanning (reliable RSSI) | 2026-02-01 |
| Auto-start live BT tracking | 2026-02-01 |
--- ---
## Blockers ## Blockers
### BT RSSI Acquisition *No current blockers*
### ~~BT RSSI Acquisition~~ (RESOLVED)
**Problem:** Cannot get reliable RSSI values for Bluetooth devices **Problem:** Cannot get reliable RSSI values for Bluetooth devices
- `hcitool rssi <addr>` - Only works for connected devices - `hcitool rssi <addr>` - Only works for connected devices
- `bluetoothctl info` - No RSSI for cached devices - `bluetoothctl info` - No RSSI for cached devices
- `btmgmt find` - Not providing output
- BLE scan (`hcitool lescan`) - I/O errors on this adapter
**Potential Solutions:** **Solution:** Switched to `bleak` Python BLE library which provides reliable RSSI via D-Bus/BlueZ.
1. Use D-Bus BlueZ API directly (needs dbus-python)
2. Keep devices paired/connected for RSSI polling
3. Focus on WiFi-based tracking instead
4. Use dedicated BLE beacon hardware
--- ---
## Notes ## Notes
- Bluetooth scanning is unreliable on Raspberry Pi 5 with built-in adapter - BLE scanning now uses `bleak` Python library (reliable RSSI via D-Bus)
- WiFi scanning works well with `iw` command - WiFi scanning works well with `iw` command
- Consider external USB Bluetooth adapter for better BLE support - Live BT tracking auto-starts on page load (3-second scan interval)
- Historical data stored in SQLite database with auto-cleanup

View File

@@ -214,6 +214,7 @@
- [x] Device labeling and favorites - [x] Device labeling and favorites
- [x] New device alerts - [x] New device alerts
- [x] Automatic data retention/cleanup - [x] Automatic data retention/cleanup
- [x] Reliable BLE scanning with bleak library
--- ---

View File

@@ -32,6 +32,7 @@ classifiers = [
dependencies = [ dependencies = [
"flask>=3.0.0", "flask>=3.0.0",
"pyyaml>=6.0", "pyyaml>=6.0",
"bleak>=0.21.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]

View File

@@ -814,61 +814,32 @@ def create_app(config: Config | None = None) -> Flask:
@app.route("/api/scan/bt", methods=["POST"]) @app.route("/api/scan/bt", methods=["POST"])
def api_scan_bt(): def api_scan_bt():
"""Quick Bluetooth-only scan for real-time tracking using hcitool""" """Quick Bluetooth-only scan for real-time tracking using bleak (BLE)"""
import subprocess import asyncio
import re from bleak import BleakScanner
bt = [] bt = []
try: try:
# Use hcitool inq for classic BT (more reliable) async def do_scan():
result = subprocess.run( devices = await BleakScanner.discover(timeout=3.0, return_adv=True)
['sudo', 'hcitool', 'inq', '--flush'], results = []
capture_output=True, for addr, (device, adv) in devices.items():
text=True, rssi = adv.rssi if adv else -70
timeout=12 name = device.name or '<unknown>'
) results.append({
for line in result.stdout.split('\n'):
# Parse: " E0:03:6B:FE:24:A1 clock offset: 0x15c7 class: 0x08043c"
m = re.match(r'\s*([0-9A-Fa-f:]+)\s+clock offset.*class:\s*0x([0-9A-Fa-f]+)', line)
if m:
addr = m.group(1)
device_class = m.group(2)
# Get device name
name = '<unknown>'
try:
name_result = subprocess.run(
['sudo', 'hcitool', 'name', addr],
capture_output=True, text=True, timeout=5
)
if name_result.stdout.strip():
name = name_result.stdout.strip()
except:
pass
# Get RSSI (only works if device is connectable)
rssi = -70 # Default
try:
rssi_result = subprocess.run(
['sudo', 'hcitool', 'rssi', addr],
capture_output=True, text=True, timeout=3
)
rssi_match = re.search(r'RSSI return value:\s*(-?\d+)', rssi_result.stdout)
if rssi_match:
rssi = int(rssi_match.group(1))
except:
pass
bt.append({
'address': addr, 'address': addr,
'name': name, 'name': name,
'rssi': rssi, 'rssi': rssi,
'device_class': device_class 'device_class': 'BLE'
}) })
return results
# Run async scan in sync context
bt = asyncio.run(do_scan())
print(f"[BT] Bleak scan found {len(bt)} devices")
except Exception as e: except Exception as e:
print(f"[BT] hcitool scan error: {e}") print(f"[BT] Bleak scan error: {e}")
bt = [] bt = []
# The scanner already does auto-identification if enabled # The scanner already does auto-identification if enabled