Lightweight Python script that continuously scans for BLE devices on a Raspberry Pi and publishes their presence to Home Assistant via MQTT discovery. Supports any number of devices.
- Persistent BLE scanning using bleak — adapter stays open continuously, no start/stop overhead per cycle
- Instant detection via advertisement callback — devices are seen as soon as they advertise, not at the end of a fixed scan window
- Multiple devices tracked in a single scanner
- Automatic Home Assistant MQTT discovery (appears as
device_trackerentities) - Publishes
home/not_homestate + JSON attributes (RSSI, last seen, MAC) — attributes only sent when device is present - Time-based expiry: marks a device away only after it has not been seen for a configurable duration
- Graceful shutdown on SIGINT / SIGTERM
- Raspberry Pi with Bluetooth (any model with built-in BT or USB dongle)
- Debian-based OS (Raspberry Pi OS, etc.)
- Python 3.10+
sudo apt install python3-bleak python3-paho-mqttAll tunable parameters live in ble_scanner.conf (INI format). A template is provided as ble_scanner.conf.sample — copy and edit it before running:
cp ble_scanner.conf.sample ble_scanner.conf
nano ble_scanner.confThe script searches for the config in this order:
- Same directory as the script (
/opt/ble_scanner/ble_scanner.conf) /etc/ble_scanner.conf
[bluetooth]
scan_duration = 2.0 # Check interval in seconds (how often expiry is evaluated)
expiry_time = 60 # seconds without detection before marking as not_home
[devices]
# mac = nickname (nickname used in MQTT topic and HA entity name)
AA:BB:CC:DD:EE:FF = tracker1_bob
AA:BB:CC:DD:EE:FF = tracker2_roger
[mqtt]
host = 192.168.x.x
port = 1883
user = mqtt
password = changeme
# $(hostname) is expanded at runtime; default is homeassistant/$(hostname)
topic = homeassistant/$(hostname)| Key | Section | Description |
|---|---|---|
scan_duration |
bluetooth | How often (in seconds) the expiry check runs; scanning itself is continuous |
expiry_time |
bluetooth | Seconds without detection before marking as not_home (default: 60) |
mac = nickname |
devices | One entry per device; nickname used in MQTT topic and HA entity name |
host |
mqtt | MQTT broker IP or hostname |
port |
mqtt | MQTT broker port |
user |
mqtt | MQTT username |
password |
mqtt | MQTT password |
topic |
mqtt | MQTT topic base; $(hostname) is expanded at runtime (default: homeassistant/$(hostname)) |
Topics are namespaced by hostname and device nickname:
| Topic | Description |
|---|---|
homeassistant/<host>/<nickname>/state |
home or not_home |
homeassistant/<host>/<nickname>/attributes |
JSON: rssi, last_seen, mac |
homeassistant/device_tracker/<host>_<nickname>/config |
HA discovery payload (retained) |
sudo python3 ble_scanner.pyRoot is required for BLE scanning on Linux.
A ready-made unit file is provided: ble_scanner.service.
# Copy script and config to a permanent location
sudo mkdir -p /opt/ble_scanner
sudo cp ble_scanner.py ble_scanner.conf /opt/ble_scanner/
# Edit config before starting
sudo nano /opt/ble_scanner/ble_scanner.conf
# Install the unit file
sudo cp ble_scanner.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now ble_scannersudo systemctl status ble_scanner # check status
sudo systemctl restart ble_scanner # restart
sudo systemctl stop ble_scanner # stop
sudo journalctl -u ble_scanner -f # follow logsDevices are registered automatically via MQTT discovery. No manual HA configuration needed — just ensure the MQTT integration is set up in HA and the broker is reachable.
Each device appears as:
- Entity type:
device_tracker - Name:
<nickname> <MAC>(e.g.tracker1_bob AA:BB:CC:DD:EE:FF) - States:
home/not_home
- Faster state updates: lower
scan_durationto1.0to reduce the delay between a device being detected and its state being published — detection itself is immediate regardless of this value - Reduce false away: raise
expiry_time(e.g.120) to wait longer before goingnot_home - Debug logging: change
logging.basicConfig(level=logging.INFO, ...)tolevel=logging.DEBUG