Apple Watch BLE to MQTT Android app
Bluey is an Android app that detects nearby Apple Watches and Bluetooth Low Energy (BLE) iBeacons. It "publishes" info on detected devices via MQTT to integrate with Home Automation software like Home Assistant and Mosquitto.
The appeal of presence detection with Apple Watches is that they are popular, usually worn around the house and emit BLE advertising packets one can passively detect. The issue is they're not easy to detect (more below).
I built this project for educational purposes and personal use in my home. Please do not be sketchy nor use it for scanning/tracking others. Please be respectful of privacy norms and laws in your area.
I was inspired by the ESPHome Apple Watch detection README from dalehumby which hints at how to detect known Apple Watches, and the Blessed library which BLE programming accessible to a noob like me.
- ⌚ Home presence detection with Apple Watches - no need for iBeacons or extra apps on the Watch!
- 👌 Simplicity - Just needs to work for my personal setup, so I've hacked, harcoded and taken shortcuts here and there
- 🤘 Tap into native code - I had scripted a Tasker solution initially but was frustrated by its limitations
- ♻️ Upcycling - Give old Android phones a second life
- Apple devices randomize MAC addresses every ~XX minutes for privacy purposes, so you can't use common BLE detection solutions that scan for a "fixed" MAC address
- However, you can scan for Apple devices (including Apple Watches) which periodically emit (https://github.com/furiousMAC/continuity/blob/master/messages/nearby_action.md)[Nearby Info messages] in BLE advertising packets.
- If you GATT connect to the device, you can read its "characteristics" to infer the specific Apple Watch model is yours (This is also a limitation: If you live in a household or have neighbors with the same model, you may get false positives)
- Once you've "seen" the Apple Watch, you can look for its last known MAC address in subsequent scans without connecting to it to detect if its Nearby. If not, you repeat the previous steps to find its new MAC address - rinse and repeat
- You can automate these steps using an App running in the background on an old Android (bluey)
- Runs on Android 6 and up (API 23+)
- Detect nearby Apple Watches by model
- Detect nearby iBeacons by static MAC address
- Adjustable settings for BLE scan period and cool-off
- MQTT TCP server support
- Runs "in the background" as a foreground service, with low battery usage, even if your Android screen turns off
- Publishes JSON events over MQTT with customizable labels
- Supports multiple instances (eg, place Androids in different rooms)
- Download this project and build the APK in Android Studio with Gradle, or
- Install the latest unsigned APK (under Releases)
- In Settings, enter your MQTT server address (eg, tcp://192.168.86.101:1883), username and password
- Set a Device name to distinguish this Android phone instance from any others you might also run (eg, Kitchen)
- In main screen dropdown, select the Apple Watch model you'd like to detect by its "machine code" (see this list to find yours), then click "Add iOS" to add it to your list
- For instance, the code for the "Apple Watch SE 40mm case (GPS+Cellular)" is
Watch5,11
- You can also confirm what your watch's code is by using a mobile BLE scanner app (Eg, Nrf Connect, making a GATT connection and reading its Device Make characteristics, like this
- (Optional) Enter in static MAC address(es) of iBeacons that you'd also like to detect and click "Add"
- Click "Play" in the Floating Action Button in bottom right corner to begin BLE scanning in the background (you can pause it at anytime)
- Note: Tap-and-hold any list items to delete them from your scan filter
- Once you press "Play", the app will automatically scan for the devices in your filter list periodically (then cool off), based on your settings. If it detects your designated Apple Watch or iBeacon nearby, it will publish an MQTT event to your configured server
- Confirm MQTT messages are being sent by using your favorite MQTT monitoring tool, eg I use the MQTT tool in Home Assistant:
- Create a MQTT platform sensor in Home Assistant or Node-red automation based on the MQTT topic and payload
The MQTT message that is published follows this format:
- Topic:
bluey/[Device name]/[MAC address | Apple Watch model machine code]
- JSON dictionary payload attributes
id
: MAC Address of the iBeacon or Apple Watchrssi
: RSSI detecting during BLE scandistance
: Estimated distance from detected device (m) - very inaccurate!timeSinceScan
: elapsed time (ms) between scan start and MQTT event publish
Eg, The message may look like this for Topic: bluey/Bedroom2/Watch5,11
{
"id": "55:55:55:C2:7E:55",
"rssi": -58,
"distance": 0.5779692884153314,
"timeSinceScan": 17014
}
If you use Home Assistant, you can edit your configuration.yaml to create an MQTT sensor from this topic. Here we're creating an MQTT sensor that sets the value to "Nearby" when the watch is detected, and to "Unavailable" if 480 seconds pass without detecting it:
mqtt:
sensor:
- name: "Apple Watch SE"
state_topic: "bluey/+/Watch5,11"
value_template: >-
Nearby
json_attributes_topic: "bluey/+/Watch5,11"
expire_after: 480
In my setup, I use this sensor to feed into a Bayesian sensor (in combination with an nmap tracker for an iPhone) that predicts whether my spouse is home or not. The Apple Watch sensor helps "smooth" out the values as "sleeping" iPhones often drop from the network when they doze:
- This was my first Android app and am just a hobby-coder, so I took a lot of shortcuts and copy-pasted example snippets to get things working; the code can be messy (but Gemini is helping a ton!)
I haven't put in any visual feedback in the app showing what the app service is doing currently yetI've added a rudimentary Scan/Resume button- you'll have to debug from Android Studio ADB logs or rely on MQTT logging to better troubleshoot- The app prompts for specific but necessary permissions (Bluetooth scanning, Location, Background Scanning) based on Android version, but it's a complex area and I may have missed cases for some versions, or particular makes. Please grant permissions when prompted for Location (most Precise) and background Bluetooth scanning (All the Time); you may need to manually grant in Permissions settings if I missed a particular permission your device requires
- Turn off Battery Optimization for the app - Some devices may kill Bluey foreground service when the device sleeps; different manufacturers vary in aggressiveness. If you're seeing Bluey being killed after the phone starts dozing, follow the tips in dontkillmyapp.com for your device
- Have not tested on other protocol MQTT servers (eg, TLS) - maybe they work?
- Be careful entering input; there is minimal error and input validation so far
- Yah, the UX is crude (eg, the MQTT password field isn't hidden)- sorry!
- Pixel 3 (Android 12, API 31)
- Pixel 2 XL (Android 11, API 30)
- Nexus 4 (Lineage OS, Android 8.1, API 27)
- Nexus 5 (Android 6.0, API 23)
- If running on an Android phone that's always plugged in, I recommend you have a timer or automate with smart plug to unplug daily and charge when low to avoid battery over-heating/puffiness
- Because you can only infer an Apple Watch is yours by Model (Machine code), you can get false positives if neighbors or people in the home wear the same model. You can minimize this by adjusting the "RSSI filter" setting to a larger number to shrink your radius of detection (eg, adjust RSSI from -90 to -60 to detect only closer proximity, ie stronger signal)
- You can increase the "BLE Scan Period" to scan for a longer period of time. This can help discovery of weaker signals or BLE iBeacons that emit less frequently. This will increase your battery usage however
- You can adjust the "Cool-off Period" to set how long Bluey waits before it begins another BLE scan. Increasing this value can help reduce battery drain