GitHub - ben-z/findphone: Locate a nearby Bluetooth device by signal strength, from the macOS command line — for when Find My isn't available · GitHub
/" data-turbo-transient="true" />
Skip to content
Type / to search
Sign in<br>Sign upAppearance settings
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
ben-z
findphone
Public
Notifications<br>You must be signed in to change notification settings
Fork<br>40
Star<br>262
main
BranchesTags
Go to file
CodeOpen more actions menu
Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit
History<br>13 Commits<br>13 Commits
.github/workflows
.github/workflows
Sources/findphone
Sources/findphone
scripts
scripts
.gitignore
.gitignore
Package.swift
Package.swift
README.md
README.md
View all files
Repository files navigation
findphone
Locate a nearby Bluetooth device by signal strength, from the command line.
Built for the case where Find My is unavailable — for example when a device is<br>enrolled in MDM that disables it — but the device is still within Bluetooth<br>range and you just need to know which corner of the room it is in.
Install
Grab the universal binary from Releases:
tar -xzf findphone-macos-universal.tar.gz<br>xattr -dr com.apple.quarantine findphone<br>./findphone --help
It is unsigned, so macOS quarantines it on download; the xattr line clears<br>that. Requires macOS 13 or later.
Build
swift build -c release<br>cp .build/release/findphone ~/bin/findphone
Requires the Swift toolchain from Xcode Command Line Tools. No dependencies.<br>For a universal arm64 + x86_64 binary, which is what CI ships:
./scripts/build-universal.sh dist
Use
findphone # survey mode: every nearby Apple handheld, by signal<br>findphone iphone # hunt mode: track one device by name (case-insensitive)<br>findphone --list # paired devices and their addresses
Add --sound in hunt mode for clicks that speed up as you close in, the way a<br>parking sensor does — about one a second across a room, rising to a buzz once<br>the signal reaches roughly -50 dBm, which is about where a laptop sits on top<br>of the phone. It lets you sweep a room by ear instead of watching the screen.<br>Clicks stop when contact goes stale, so silence means no signal rather than no<br>device.
This is not a Geiger counter, despite sounding a little like one. A counter<br>clicks once per detected particle and its rate is the measurement; here the<br>rate is computed from the smoothed signal. Packets arrive on a fixed 0.3s<br>polling timer, so their arrival rate says almost nothing about distance.
Add --redact if you are recording the screen. It masks Bluetooth addresses,<br>and in survey mode replaces discovered device names with the device kind,<br>since those names are not always yours. A public address is a stable hardware<br>identifier and, unlike a BLE advertising address, does not rotate, so it is<br>worth keeping out of a video.
Two things stay visible deliberately: the name you typed in hunt mode, and the<br>names in --list, because picking a target means reading them. --list is<br>not safe to film even with --redact.
Walk slowly and watch the bar. The reading is signal strength in dBm, which is<br>negative and closer to zero when nearer:
dBm<br>Rough meaning
-45 up<br>arm's reach
-60<br>same table
-72<br>same room
-85<br>far, or behind cover
below<br>very far, or shielded
Signal strength is a coarse proxy for distance. Metal, walls and human bodies<br>attenuate it heavily, so a device in a filing cabinet two metres away can read<br>the same as one fifteen metres away in open air. Trust the trend as you move,<br>not any single number.
How it works
Three sources feed one reading, in descending order of quality:
GATT link — once connected to the device over BLE, readRSSI() returns<br>a fresh measurement about three times a second. This is the good one.
BLE advertisements — passively observed. Apple devices rotate their<br>advertising addresses roughly every fifteen minutes and only include the<br>device name on occasional packets, so this source is sparse.
Classic link RSSI — read from system_profiler SPBluetoothDataType,<br>keyed by the stable public address of a paired device.
Source 3 has a trap worth knowing about: macOS refreshes that value only every<br>three to twelve seconds and serves a cached number in between. Polling it<br>faster does not yield more information. Measured over 112 polls at 0.4s, every<br>poll returned a value but the same value repeated for runs of 8 to 31 polls.<br>The tool therefore counts a measurement only when the value actually changes,<br>which is why the reported measurement count is far lower than the poll rate —<br>and honest.
Permissions
Needs Bluetooth access for whichever terminal runs it, under<br>System Settings > Privacy & Security > Bluetooth. It will say so if missing.
Limitations
Cannot make a device ring. There is no path...