Pure-Python parser for Apple Continuity BLE proximity-pairing adverts (AirPods battery + Apple device presence)
Project description
apple-ble
Read AirPods battery (case / left / right + charging) and detect nearby Apple devices from Python, straight off the unencrypted Apple Continuity proximity-pairing BLE advertisement — no pairing, no connection.
This library powers the
Apple BLE Home Assistant integration,
but works standalone in any Python project (or straight from the command line).
The parser is pure and dependency-free; bleak is only needed for the optional
scanner CLI.
What works: AirPods broadcast their battery in an unencrypted Continuity advert when the lid is opened. What doesn't: Apple Watch / iPhone do not put battery in their adverts — BLE can only tell you an Apple device is nearby. Apple rotates the BLE MAC (~15 min), so there is no stable per-device id.
Features
- 🔋 AirPods battery — case, left, and right levels plus per-pod and case
charging flags, decoded from the manufacturer-
0x004Cproximity-pairing advert. - 🎧 Model detection — AirPods 1/2/3, Pro, Pro 2, Max.
- 📡 Presence-friendly — exposes the Apple manufacturer id so callers can count nearby Apple devices.
- 🧪 Pure parser —
parse_proximity_pairing(bytes) -> AirPodsData | None, no I/O, trivially unit-testable. - 🖥️ CLI —
apple-blescans and prints any AirPods it sees (needs thecliextra).
Requirements
- Python 3.12 or newer.
- A Bluetooth LE adapter (built-in or USB) — only for live scanning; the parser itself needs nothing.
Installation
pip install apple-ble # core (parsing only)
pip install "apple-ble[cli]" # + bleak scanner CLI
CLI usage
apple-ble # 15s BLE scan; open your AirPods lid nearby and watch the battery
Library usage
from apple_ble import parse_proximity_pairing, APPLE_MANUFACTURER_ID
# `payload` is manufacturer_data[76] from any BLE stack (bleak, Home Assistant, ...)
data = parse_proximity_pairing(payload)
if data:
print(data.model, data.left_battery, data.right_battery, data.case_battery)
print(data.left_charging, data.right_charging, data.case_charging)
parse_proximity_pairing returns None for anything that isn't a complete
AirPods proximity-pairing advert, so it is safe to feed it every Apple advert you
see and keep the non-None results.
API
| Symbol | Description |
|---|---|
parse_proximity_pairing(data) |
Parse manufacturer_data[76] bytes into AirPodsData, or None if it isn't AirPods. |
AirPodsData |
Frozen dataclass: model, left_battery, right_battery, case_battery (int % or None), left_charging, right_charging, case_charging. |
decode_battery_nibble(nibble) |
Pure helper: a 0x0–0xF battery nibble to a percentage (None when not present). |
APPLE_MANUFACTURER_ID |
76 (0x004C) — Apple's Bluetooth company id. |
MODEL_BY_CHAR |
Mapping of the model nibble to a human model name. |
Credits
Continuity reverse-engineering: furiousMAC/continuity, kavishdevar/librepods, delphiki/AirStatus, d4rken-org/capod. Author @hudsonbrendon.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file apple_ble-0.1.1.tar.gz.
File metadata
- Download URL: apple_ble-0.1.1.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
133ee2d389d1c05d65b166d0aea7a864e527e704fc42eea863c1d5762d00e345
|
|
| MD5 |
ea525551836b161203d03b9200e0d6c9
|
|
| BLAKE2b-256 |
d7b314eb4e8731d1d7945f602d8c65810ce720dd65d43d550174250575d59f70
|
File details
Details for the file apple_ble-0.1.1-py3-none-any.whl.
File metadata
- Download URL: apple_ble-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75fd4932583c658972f1cc4eb8bf7bebbceb9160be09a6493e1cbaf8a8c77cf3
|
|
| MD5 |
9b5cdd327151b4bbcfb76cd7d4677a5f
|
|
| BLAKE2b-256 |
7f47c0dcea13ac397e7593290edf2da8a2beaabbe2ffe69c762567e27b550429
|