-
-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Knowing if a particular AirTag presents in the scan results? #46
Comments
How did you compare the keys? It's also possible there is some timing inaccuracy, you could try using |
Yes, that's what I tried.
Thanks! I am running this script:
Doesn't seem to hit |
Ohhh yes indeed then that will not work. As you guessed, Airtags do not broadcast when they're near an owner device. Well, they do, but it's a very small payload that is only used to indicate to the owner device that it is still nearby (afaik). I think the scanner currently skips those, because there is no way to retrieve the full identity of the tag from those reports. It should still be possible to verify whether a given public key is nearby though. If you just scan for nearby bluetooth low energy MAC addresses, the last 4 bytes of your tag should be identical of byte 1 - 4 of the active public key (0-indexed). That's not currently implemented in the scanner, but I'll take it as a feature request 🙂 |
Fabulous! Here's my attempt: import datetime, pytz
import findmy
import findmy.scanner
import findmy.scanner.scanner
import asyncio
import logging
import time
logging.basicConfig(level=logging.INFO)
acc = findmy.accessory.FindMyAccessory.from_plist(open("myairtag.plist", "rb"))
now = datetime.datetime.now(pytz.timezone("EST"))
keys = acc.keys_between(now - datetime.timedelta(hours=24), now + datetime.timedelta(hours=24))
async def scan() -> None:
scanner = await findmy.scanner.OfflineFindingScanner.create()
print("Scanning for FindMy-devices...")
print()
async for device in scan_for(scanner, 1000, extend_timeout=True):
print(f"Device - {device.mac_address}")
print(f" Public key: {device.adv_key_b64}")
print(f" Lookup key: {device.hashed_adv_key_b64}")
print(f" Status byte: {device.status:x}")
print(f" Hint byte: {device.hint:x}")
print(" Extra data:")
for k, v in sorted(device.additional_data.items()):
print(f" {k:20}: {v}")
print()
if any((device.adv_key_b64 == k.adv_key_b64) for k in keys):
print("Found!!!!")
break
def has_consecutive_four_byte_match(bytes1, bytes2):
length1 = len(bytes1)
length2 = len(bytes2)
if length1 < 4 or length2 < 4:
return False
for i in range(length1 - 3):
four_bytes1 = bytes1[i:i+4]
for j in range(length2 - 3):
four_bytes2 = bytes2[j:j+4]
if four_bytes1 == four_bytes2:
return True
return False
async def _wait_for_device(self, timeout: float):
device, data = await asyncio.wait_for(self._device_fut, timeout=timeout)
mac_bytes = bytes(int(part, 16) for part in device.address.split(':'))
for k in keys:
if has_consecutive_four_byte_match(mac_bytes, k.adv_key_bytes):
print("Found MAC!!!")
print(device)
print(data)
print(mac_bytes)
print(k.adv_key_bytes)
apple_data = data.manufacturer_data.get(self.BLE_COMPANY_APPLE, b"")
if not apple_data:
return None
try:
additional_data = device.details.get("props", {})
except AttributeError:
# Likely Windows host, where details is a '_RawAdvData' object.
# See: https://github.com/malmeloo/FindMy.py/issues/24
additional_data = {}
return findmy.scanner.scanner.OfflineFindingDevice.from_payload(device.address, apple_data, additional_data)
async def scan_for(
self,
timeout: float = 10,
*,
extend_timeout: bool = False,
):
"""
Scan for `OfflineFindingDevice`s for up to `timeout` seconds.
If `extend_timeout` is set, the timer will be extended
by `timeout` seconds every time a new device is discovered.
"""
await self._start_scan()
stop_at = time.time() + timeout
devices_seen = set()
try:
time_left = stop_at - time.time()
while time_left > 0:
device = await _wait_for_device(self, time_left)
if device is not None and device not in devices_seen:
devices_seen.add(device)
if extend_timeout:
stop_at = time.time() + timeout
yield device
time_left = stop_at - time.time()
except (asyncio.CancelledError, asyncio.TimeoutError): # timeout reached
return
finally:
await self._stop_scan()
if __name__ == "__main__":
asyncio.run(scan()) Note that I tried what you described on the indexing but that didn't give me any match. So I used that bruteforce
It seems like |
5 matching bytes indeed, I think I had a brain fart there 😅. Actually, the first 6 bytes of the public key are encoded in the MAC address, with the exception of the two most significant bits of the first byte. Those two are always I've started implementing 'nearby' status device detection in #50. I'll extend that to allow checking whether a device corresponds to a specific keypair or findmy-accessory. |
Hi!
Since #41, I am pretty convinced that close-to-realtime location using the crowdsourcing FindMy approach won't fit my application (tracking my dog). I was thinking of using an AirTag as a BLE tracker just so that I can know in almost real-time that the AirTag is at home.
Is there a way to know if a particular AirTag is present in the scan results? I see that the scanner gives the MAC address and the public key of any AirTag. But, given an AirTag, how do I know either of these two values for that particular AirTag? The public key is rotating, am I right? So maybe there is a way to predict this public key using the seed plist?
I have tried:
But the key it returns doesn't seem to match with any of the scanner results. I also tried passing in my current timezone (EST) to
keys_at
, but still no luck.Any guidance or suggestions would be greatly appreciated!
The text was updated successfully, but these errors were encountered: