Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

btrack

Find a Bluetooth device by ear — scan, pick it, follow the beeps

Python 3.9+ MIT License Cross-platform Status: v0

Install · Use · Limits · Troubleshooting · How it works · Contributing

Pick a device from a list, then walk around — the beeping speeds up as you get closer, like a metal detector.

Offline. No account, no cloud, no companion app on the target device. Runs on macOS, Windows and Linux.

  TRACKING  Pixel 8
  A1B2C3D4-5E6F-7890-ABCD-EF1234567890

  ███████████████████████████████░░░░░░░░░░░░░░░░░░  62%

  ▲  WARMER — keep going that way

  signal -58.3 dBm   distance ~1.8 m   update live   sound on
  history ▁▂▃▃▅▅▆▇

  [c] calibrate at 1m   [m] mute   [q] quit

What it can and cannot do

Read this before you rely on it.

It plays hotter/colder. It does not point. Bluetooth gives you signal strength, not direction. You find things by sweeping the area and following the pitch — not by following an arrow. There is no map and no bearing, and any tool claiming otherwise from a single radio is guessing.

Range is roughly 10–30 m, and walls, furniture and your own body cut it hard. Holding your laptop between yourself and the device measurably weakens the signal. Good for a house, a flat, an office floor. Marginal in a large shop.

The device has to be awake and advertising. A phone asleep in a drawer may broadcast nothing your computer can see, and a phone with a dead battery broadcasts nothing at all. This is precisely why Apple's Find My and Google's Find My Device use a mesh of other people's phones instead of your laptop — if your phone is genuinely lost outside your home, use those. btrack is for "it's in this building somewhere."

Track only devices you own or have permission to look for. Following someone else's device around is stalking, and in many places a crime.

Measured sensitivity

The "warmer/colder" call is deliberately conservative: it stays quiet unless the movement is statistically distinguishable from noise, because a detector that says "warmer" while you stand still is worse than one that says nothing.

Walking at ~1 m/s changes the signal at a rate that depends on how far away you already are:

Distance Signal change while walking
2 m ~7.5 dB/s
3 m ~4.4 dB/s
5 m ~2.4 dB/s
8 m ~1.5 dB/s
12 m ~0.9 dB/s

Which the detector picks up like this (simulated, 15 runs each). "False trend" is how often it claims movement while the device is stationary:

Noise False trend 1.5 dB/s 2.5 dB/s 4 dB/s 6 dB/s
3 dB (quiet room) 3% 82% 100% 100% 100%
5 dB (typical) 7% 32% 84% 100% 100%
8 dB (cluttered) 7% 6% 36% 84% 99%

In practice: within about 5 m it tracks you reliably. Beyond ~10 m a slow approach is genuinely below the noise floor — walk decisively, or watch the percentage and history sparkline rather than waiting for a "warmer".

Install

Python 3.9 or newer.

git clone https://github.com/tal7aouy/btrack.git
cd btrack
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

Or install it as a command:

pip install -e .

On Linux you also need PortAudio for sound (sudo apt install libportaudio2). Without it everything still works, just silently.

Use

python3 -m btrack          # scan, pick from the list, track
btrack                    # same, if you installed with pip install -e .

Scan, choose a number, then walk. Press q to stop.

  Nearby Bluetooth LE devices
    #    RSSI   SEEN  NAME / ADDRESS

    1   -47 dBm    31  Pixel 8            A1B2C3D4-5E6F-...
    2   -63 dBm    12  Galaxy Buds        7788AABB-CCDD-...
    3   -88 dBm     3  (unnamed)          11223344-5566-...

  Number to track, [r] rescan, [q] quit:

Calibrate for a much better distance readout. Hold your computer about a metre from the device and press c. That pins the top of the scale to this particular device's transmit power, which varies a lot between models.

Options

Flag Does
-a, --address ADDR skip the picker, track this address directly
-t, --scan-time N seconds to scan (default 6)
--named-only hide devices that advertise no name
--min-rssi N hide devices weaker than N dBm
--no-audio silent, meter only
--volume 0.0-1.0 beep volume (default 0.35)
--tx-power N reference RSSI at 1 m (default -59; c sets it live)
--path-loss N 2.0 open space, 2.5 default, 3.0+ cluttered
--smoothing N EMA weight; lower is steadier but slower (default 0.2)
--lost-after N seconds of silence before reporting a lost signal
--no-color plain output
--log PATH append every tick to PATH for later replay/plotting
--log-format csv|json session log format (default: csv; json is one object per line)

What the sounds mean

Sound Meaning
Slow low beeps far away
Faster, higher beeps closing in
Two-tone rising chirp you moved closer — keep going
Two-tone falling chirp you moved further away — turn back
Rapid triple chirp within arm's reach
Sparse low tick signal lost, sweep the area

Finding a phone specifically

Phones rotate their Bluetooth address every ~15 minutes to stop exactly this kind of tracking. That does not break the tool, because you scan, pick and track inside one session — but it does mean you cannot save an address today and reuse it tomorrow. Always scan fresh.

  • Android shows up well, usually with a recognisable name.
  • iPhone advertises with a randomised address and typically no name, so it appears as (unnamed). Opening the Bluetooth settings screen on the phone makes it advertise much more visibly — useful if a friend can help — and pairing it with your computer first makes it far easier to pick out.
  • macOS hides real MAC addresses and gives each host its own UUID for the same device, so addresses are not portable between computers.

If the target is not in the list at all: it is asleep, out of range, or is a classic-Bluetooth-only device (see below).

Troubleshooting

"Bluetooth device is turned off" — but it isn't

On macOS this one message has two completely different causes, because a permission denial is reported as a powered-off radio. If Bluetooth is on and you still get this, it is permissions: find the Bluetooth entry in the system privacy settings and enable your terminal app there. (The exact path moves between macOS releases; search the system settings for "Bluetooth".)

Related: run btrack from a terminal you launched yourself. Started from inside another tool — an IDE task runner, an agent, a daemon — macOS denies Bluetooth silently and reports the radio as off. The permission prompt only appears for a terminal you opened.

Nothing appears in the list

The target is asleep, out of range, or classic-Bluetooth only. Waking a phone and opening its Bluetooth settings screen makes it advertise far more visibly.

It keeps saying "signal lost"

Some devices advertise very slowly — well under once per second. The lost-signal threshold adapts to each device's observed rate, but if it still flickers, raise the floor with --lost-after 8.

The percentage jumps around

Normal — RSSI is noisy. Watch the sparkline and the trend arrow rather than the raw number, and lower --smoothing (e.g. 0.1) for a steadier but slower read.

How it works

  1. Scanbleak performs a BLE scan and reports each advertisement's RSSI. On macOS this needs a nudge: CoreBluetooth coalesces repeat advertisements from the same device unless the scan opts in, and bleak starts its scan with no options, so a device is reported roughly once per scan. Fine for discovery, useless for tracking. scanner.py patches the scan to set CBCentralManagerScanOptionAllowDuplicatesKey, which measurably raised the sample rate on real hardware. If the patch ever fails against a future bleak, scanning still works — just with far fewer readings.
  2. Smooth — raw RSSI swings several dB while perfectly still, so the displayed level is an exponential moving average.
  3. Trend — warmer/colder comes from a least-squares fit over the raw samples, not the smoothed ones. Regressing the EMA reports confident nonsense because its samples are autocorrelated by construction. A trend is announced only when the slope beats 2.5× its own standard error, so the detector tightens up in quiet conditions and goes conservative in noisy ones.
  4. Distance — the usual log-distance path loss model. Indicative only; treat it as a rough band, not a measurement.
  5. Sound — pulse rate and pitch encode proximity, pattern shape encodes trend, all generated as sine bursts at runtime.

Limitations worth knowing

  • BLE only. Classic Bluetooth (BR/EDR) has no cross-platform Python story, so an old headset that only does classic and is not in pairing mode will not appear. Every modern phone advertises BLE, so this rarely matters in practice.
  • One radio, no triangulation. Two receivers at known positions could actually locate a device rather than just rank distance. That is a much bigger project.
  • The distance number is not survey-grade. A wall can shift it by a factor of two.

Development

pip install -r requirements.txt pytest
python3 -m pytest tests/ -q

The test suite runs without Bluetooth hardware or a sound card: the trend detector is tested statistically against simulated noise, and the tracking loop is driven end-to-end by a fake radio with a virtual clock.

Contributing

Issues and pull requests welcome at github.com/tal7aouy/btrack.

Please run the tests before opening a PR. If you change anything in rssi.py, check the statistical tests still pass — the thresholds there are tuned, and a change that makes the trend detector more eager will show up as a rising false-positive rate rather than as a broken test elsewhere.

License

MIT © tal7aouy — see LICENSE.

About

ᛒ Offline Bluetooth proximity tracker — find your phone, keys or headphones by following beeps that get faster as you close in. Cross-platform CLI, no cloud, no account.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages