MCPcopy Index your code
hub / github.com/HackingDave/btrpa-scan

github.com/HackingDave/btrpa-scan @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
151 symbols 327 edges 5 files 61 documented · 40%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

btrpa-scan

A Bluetooth Low Energy (BLE) scanner with advanced Resolvable Private Address (RPA) resolution. Discover nearby BLE devices, track a specific device by MAC address, or resolve privacy-randomized addresses using an Identity Resolving Key (IRK).

Written by: David Kennedy (@HackingDave) Company: TrustedSec

Features

  • Discover All Devices - Scan for all broadcasting BLE devices in range with signal strength, estimated distance, manufacturer data, and service UUIDs
  • Targeted Search - Search for a specific device by MAC address and monitor every detection
  • IRK Resolution - Resolve Resolvable Private Addresses against one or more Identity Resolving Keys to identify devices using randomized addresses for privacy
  • Multiple IRKs - Load multiple IRKs from a file to resolve addresses for several devices in a single scan
  • RSSI Filtering - Filter out weak signals with a minimum RSSI threshold
  • RSSI Averaging - Sliding window average smooths noisy BLE RSSI readings for more stable distance estimates
  • Name Filtering - Filter devices by name with case-insensitive substring matching
  • Active Scanning - Send SCAN_REQ to get SCAN_RSP with additional service UUIDs and device names that passive scanning misses
  • Environment Presets - Indoor, outdoor, and free-space path-loss models for more accurate distance estimation
  • Proximity Alerts - Audible/visual alert when a device is estimated within a configurable distance
  • Web GUI - Browser-based radar interface with animated sweep, distance visualization, GPS map, signal-strength device list with pin/unpin, and hover tooltips
  • Live TUI - Curses-based live-updating table sorted by signal strength
  • Real-Time CSV Log - Stream each detection to a CSV file as it happens
  • Batch Export - Export results to CSV, JSON, or JSONL at end of scan (supports stdout with -o -)
  • GPS Location Stamping - Tag each detection with GPS coordinates via gpsd; tracks per-device best location (strongest RSSI = closest proximity). On by default, degrades gracefully if gpsd is unavailable
  • Multi-Adapter - Scan with multiple Bluetooth adapters simultaneously (Linux)
  • Verbose/Quiet Modes - Verbose mode shows additional details (e.g. non-matching RPAs in IRK mode); quiet mode suppresses per-device output and shows only the summary

Requirements

  • Python 3.9+
  • Bluetooth hardware
  • OS support: macOS, Linux, Windows
  • gpsd (optional) — required for GPS location stamping

Installing gpsd

GPS location stamping requires the gpsd daemon running with a connected GPS receiver. If gpsd is not running, btrpa-scan continues normally without GPS.

Platform Install Start
macOS brew install gpsd gpsd -n /dev/tty.usbserial-*
Debian/Ubuntu sudo apt install gpsd gpsd-clients sudo systemctl start gpsd
Fedora/RHEL sudo dnf install gpsd gpsd-clients sudo systemctl start gpsd
Arch sudo pacman -S gpsd sudo systemctl start gpsd
Windows Use gpsd via WSL or MSYS2 See WSL instructions above

To verify gpsd is working:

# Check that gpsd is listening
gpspipe -w -n 5
# Or use the curses monitor
cgps

Platform Notes

Platform Notes
macOS Uses CoreBluetooth. IRK mode leverages an undocumented API to retrieve real Bluetooth addresses instead of UUIDs. --active has no effect — CoreBluetooth always scans actively.
Linux May require root or CAP_NET_ADMIN capability for scanning.
Windows Native WinRT Bluetooth API — real MAC addresses available natively. TUI requires pip install windows-curses.

Installation

This project uses pyproject.toml (PEP 621), the modern Python packaging standard. It defines the project as an installable package with a registered CLI command — no need to run .py files directly.

Quick Run (no install)

uvx btrpa-scan --all

Run from GitHub (no install)

uvx --from git+https://github.com/hackingdave/btrpa-scan.git btrpa-scan --all

Install as a Tool

uv tool install btrpa-scan

Or directly from GitHub:

uv tool install git+https://github.com/hackingdave/btrpa-scan.git

Install via pip

pip install btrpa-scan

For GUI support (Flask-based radar interface):

pip install btrpa-scan[gui]

From Source

git clone https://github.com/hackingdave/btrpa-scan.git
cd btrpa-scan
pip install .

Usage

usage: btrpa-scan [-h] [-a] [--irk HEX] [--irk-file PATH] [-t TIMEOUT]
                     [--output {csv,json,jsonl}] [-o FILE] [--log FILE]
                     [-v | -q] [--min-rssi DBM] [--rssi-window N] [--active]
                     [--environment {free_space,indoor,outdoor}]
                     [--ref-rssi DBM] [--name-filter PATTERN]
                     [--alert-within METERS] [--tui] [--gui] [--gui-port PORT]
                     [--no-gps] [--adapters LIST] [mac]

BLE Scanner — discover all devices or hunt for a specific one

positional arguments:
  mac                   Target MAC address to search for (omit to scan all)

optional arguments:
  -h, --help            show this help message and exit
  -a, --all             Scan for all broadcasting devices
  --irk HEX             Resolve RPAs using this Identity Resolving Key (32 hex chars)
  --irk-file PATH       Read IRK(s) from a file (one per line, hex format)
  -t, --timeout TIMEOUT Scan timeout in seconds (default: 30, or infinite for --irk)
  --output {csv,json,jsonl}
                        Batch output format written at end of scan
  -o, --output-file FILE
                        Output file path (default: btrpa-scan-results.<format>;
                        use - for stdout)
  --log FILE            Stream detections to a CSV file in real time
  -v, --verbose         Verbose mode — show additional details
  -q, --quiet           Quiet mode — suppress per-device output, show summary only
  --min-rssi DBM        Minimum RSSI threshold (e.g. -70) — ignore weaker signals
  --rssi-window N       RSSI sliding window size for averaging (default: 1 = no averaging)
  --active              Use active scanning (sends SCAN_REQ for additional data)
  --environment {free_space,indoor,outdoor}
                        Distance estimation path-loss model (default: free_space)
  --ref-rssi DBM        Calibrated RSSI at 1 metre for distance estimation
  --name-filter PATTERN Filter devices by name (case-insensitive substring match)
  --alert-within METERS Proximity alert when device is within this distance
  --tui                 Live-updating terminal table instead of scrolling output
  --gui                 Launch web-based radar interface in the browser
  --gui-port PORT       Port for GUI web server (default: 5000)
  --no-gps              Disable GPS location stamping (GPS is on by default via gpsd)
  --adapters LIST       Comma-separated Bluetooth adapter names (e.g. hci0,hci1)

Mode 1: Discover All Devices

Scan for all broadcasting BLE devices (default 30-second timeout):

btrpa-scan --all

With a custom timeout:

btrpa-scan --all -t 60

Mode 2: Targeted Search

Search for a specific device by MAC address:

btrpa-scan AA:BB:CC:DD:EE:FF

Mode 3: IRK Resolution

Resolve Resolvable Private Addresses using an Identity Resolving Key. This mode runs indefinitely by default until stopped with Ctrl+C:

btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF

The IRK can be provided in several formats:

Format Example
Plain hex 0123456789ABCDEF0123456789ABCDEF
Colon-separated 01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF
Dash-separated 01-23-45-67-89-AB-CD-EF-01-23-45-67-89-AB-CD-EF
0x-prefixed 0x0123456789ABCDEF0123456789ABCDEF

IRK from a File

Load one or more IRKs from a file. Each line should contain one IRK in any supported hex format. Lines starting with # are treated as comments:

btrpa-scan --irk-file keys.txt

Example keys.txt:

# Alice's phone
0123456789ABCDEF0123456789ABCDEF
# Bob's watch
FEDCBA9876543210FEDCBA9876543210

When multiple IRKs are loaded, each detected RPA is checked against all keys. The summary shows total matches across all keys.

IRK from Environment Variable

Set the BTRPA_IRK environment variable to avoid passing the key on the command line:

export BTRPA_IRK=0123456789ABCDEF0123456789ABCDEF
btrpa-scan

Priority: --irk > --irk-file > BTRPA_IRK

RSSI Filtering

Only show devices with signal strength above a threshold:

btrpa-scan --all --min-rssi -70

RSSI Averaging

BLE RSSI is inherently noisy. Use a sliding window average for more stable distance estimates and to filter out spurious weak detections:

btrpa-scan --all --rssi-window 5

When windowing is active, the display shows both raw and averaged RSSI (e.g. RSSI: -65 dBm (avg: -62 dBm over 5 readings)), and distance estimation uses the averaged value. The --min-rssi filter also applies to the averaged RSSI, preventing a single noisy spike from dropping a device.

Name Filtering

Filter devices by name using a case-insensitive substring match:

btrpa-scan --all --name-filter "AirPods"

Only devices whose advertised name contains the given pattern will be shown. Devices with no name are excluded when a name filter is active.

Active Scanning

Passive scanning (the default) only sees advertisements. Active scanning sends SCAN_REQ and gets SCAN_RSP, which can reveal additional service UUIDs and device names:

btrpa-scan --all --active

Note: On macOS, CoreBluetooth always scans actively regardless of this flag. On Linux/BlueZ, active scanning may require root or CAP_NET_ADMIN.

Environment Presets

Distance estimation uses a path-loss exponent that varies by environment. The default (free_space, n=2.0) assumes no obstructions. For more realistic estimates indoors:

btrpa-scan --all --environment indoor
Preset Path-Loss Exponent (n) Use Case
free_space 2.0 Open air, line of sight
outdoor 2.2 Parks, parking lots
indoor 3.0 Offices, homes, buildings

Higher n values produce larger distance estimates for the same RSSI, reflecting signal attenuation from walls and obstacles.

Reference RSSI Calibration

By default btrpa-scan derives the expected RSSI at 1 metre from the advertised TX Power using an empirically validated 59 dB offset (the iBeacon standard). For even better accuracy you can supply a calibrated value measured in your own environment:

  1. Place the target device exactly 1 metre from the scanner.
  2. Run a short scan and note the average RSSI.
  3. Pass that value with --ref-rssi:
btrpa-scan --all --ref-rssi -55

When --ref-rssi is set, TX Power is ignored entirely. This also enables distance estimates for devices that don't advertise TX Power.

Proximity Alerts

Trigger an audible bell and visual alert when a device is estimated within a given distance. Requires that the target device advertise TX Power:

btrpa-scan AA:BB:CC:DD:EE:FF --alert-within 5.0

Works in all modes including IRK resolution:

btrpa-scan --irk <key> --alert-within 3.0

Live TUI

Replace scrolling output with a live-updating terminal table, sorted by signal strength:

btrpa-scan --all --tui

The TUI shows all detected devices in a compact table with address, name, RSSI, averaged RSSI, estimated distance, detection count, and last-seen time. Resolved IRK matches are shown in bold, and devices within the --alert-within threshold are highlighted.

Combine with other flags:

btrpa-scan --irk <key> --tui --rssi-window 5 --environment indoor --alert-within 5.0

Web GUI

Launch a browser-based radar interface with an animated sweep, real-time device tracking, and GPS map:

btrpa-scan --all --gui

The GUI features:

  • Radar display — animated sweep line with concentric distance rings (1m, 5m, 10m, 20m). Devices appear as color-coded dots positioned by estimated distance (green = close, yellow = medium, red = far). Full-height layout with matrix data rain background and ghost MAC flicker effects
  • Device list — right-side panel listing all detected devices, color-coded by signal strength. Click any device to pin it for tracking
  • Pinned devices — left-side panel showing pinned MAC addresses with live RSSI and distance updates. Pin multiple devices simultaneously; click × to unpin
  • GPS map — Leaflet.js map with OpenStreetMap tiles showing scanner position and device locations. Automatically hidden if no GPS is available
  • Hover tooltips — hover over any device (radar dot, list entry, or pinned entry) to see full device details (address, name, RSSI, TX power, distance, GPS, manufacturer data, services)
  • Dark theme — matrix-green hacker aesthetic designed for field work

GUI mode scans continuously by default (no 30-second timeout). Press Ctrl+C to stop. Use -t to set a specific scan duration:

# Scan for 60 seconds with indoor path-loss model
btrpa-scan --all --gui -t 60 --environment indoor

# Custom port
btrpa-scan --all --gui --gui-port 8080

# Combine with RSSI averaging and proximity alerts
btrpa-scan --all --gui --rssi-window 5 --alert-within 5.0

Note: --gui requires Flask and flask-socketio (pip install btrpa-scan[gui]). Cannot be combined with --tui or --quiet.

Real-Time CSV Log

Stream each detection to a CSV file as it happens (useful for long-running scans where you want incremental data):

```bash btrpa-scan --all --l

Core symbols most depended-on inside this repo

_avg_rssi
called by 12
btrpa-scan.py
start
called by 4
btrpa-scan.py
_print_device
called by 4
btrpa-scan.py
start
called by 4
btrpa_scan/cli.py
_print_device
called by 4
btrpa_scan/cli.py
stop
called by 3
btrpa-scan.py
_parse_irk
called by 3
btrpa-scan.py
stop
called by 3
btrpa_scan/cli.py

Shape

Method 117
Function 16
Class 14
Route 4

Languages

Python100%

Modules by API surface

test_btrpa_scan.py55 symbols
btrpa_scan/cli.py48 symbols
btrpa-scan.py48 symbols

For agents

$ claude mcp add btrpa-scan \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact