diff --git a/PROJECT.md b/PROJECT.md index f85dcba..efb1242 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -59,8 +59,8 @@ Understanding the RF environment around you is useful for: ┌─────────────────────────┴───────────────────────────────────┐ │ System Tools │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ iw │ │ hcitool │ │bluetoothctl│ │ -│ │ (WiFi) │ │ (BT) │ │ (BLE) │ │ +│ │ iw │ │ bleak │ │ SQLite │ │ +│ │ (WiFi) │ │ (BLE) │ │(History) │ │ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` @@ -78,6 +78,7 @@ Understanding the RF environment around you is useful for: - Flask - Web framework - PyYAML - Configuration - dataclasses - Data structures +- bleak - BLE scanning with RSSI ### Frontend - Leaflet.js - 2D maps diff --git a/ROADMAP.md b/ROADMAP.md index 9faab4b..702524a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -41,7 +41,7 @@ - [x] Live BT tracking mode - [x] Moving device detection (purple markers) - [x] Filter-aware scanning (WiFi/BT toggle) -- [ ] Reliable RSSI acquisition for movement tracking +- [x] Reliable RSSI acquisition for movement tracking (bleak) - [ ] Position smoothing/averaging - [ ] Device trails/history visualization diff --git a/TASKS.md b/TASKS.md index 5111873..d03cd4a 100644 --- a/TASKS.md +++ b/TASKS.md @@ -23,7 +23,7 @@ | 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] | Moving device detection | Purple markers for RSSI changes | | [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 | | [ ] | Create CHEATSHEET.md | Quick reference guide | @@ -78,28 +78,28 @@ | Live tracking button | 2026-01-31 | | Purple moving indicators | 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 -### BT RSSI Acquisition +*No current blockers* + +### ~~BT RSSI Acquisition~~ (RESOLVED) **Problem:** Cannot get reliable RSSI values for Bluetooth devices - `hcitool rssi ` - Only works for connected 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:** -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 +**Solution:** Switched to `bleak` Python BLE library which provides reliable RSSI via D-Bus/BlueZ. --- ## 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 -- 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 diff --git a/TODO.md b/TODO.md index d9b9a20..ac019cb 100644 --- a/TODO.md +++ b/TODO.md @@ -214,6 +214,7 @@ - [x] Device labeling and favorites - [x] New device alerts - [x] Automatic data retention/cleanup +- [x] Reliable BLE scanning with bleak library --- diff --git a/pyproject.toml b/pyproject.toml index 94999c9..d4b0d48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ dependencies = [ "flask>=3.0.0", "pyyaml>=6.0", + "bleak>=0.21.0", ] [project.optional-dependencies] diff --git a/src/rf_mapper/web/app.py b/src/rf_mapper/web/app.py index db99263..3e653e0 100644 --- a/src/rf_mapper/web/app.py +++ b/src/rf_mapper/web/app.py @@ -814,61 +814,32 @@ def create_app(config: Config | None = None) -> Flask: @app.route("/api/scan/bt", methods=["POST"]) def api_scan_bt(): - """Quick Bluetooth-only scan for real-time tracking using hcitool""" - import subprocess - import re + """Quick Bluetooth-only scan for real-time tracking using bleak (BLE)""" + import asyncio + from bleak import BleakScanner bt = [] try: - # Use hcitool inq for classic BT (more reliable) - result = subprocess.run( - ['sudo', 'hcitool', 'inq', '--flush'], - capture_output=True, - text=True, - timeout=12 - ) - - 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 = '' - 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({ + async def do_scan(): + devices = await BleakScanner.discover(timeout=3.0, return_adv=True) + results = [] + for addr, (device, adv) in devices.items(): + rssi = adv.rssi if adv else -70 + name = device.name or '' + results.append({ 'address': addr, 'name': name, '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: - print(f"[BT] hcitool scan error: {e}") + print(f"[BT] Bleak scan error: {e}") bt = [] # The scanner already does auto-identification if enabled