Summary
Near its owner, an AirTag (2nd generation) periodically stops sending the Apple Offline Finding advertisement (manufacturer data 0x004C, type 0x12) and sends the IETF DULT location-enabled payload instead. OfflineFindingScanner only looks at Apple manufacturer data, so during these periods the tag is invisible to local scanning even though its keys are known. The address of the DULT advertisement is derived from the same rolling key, so it can still be matched.
Observations
Two AirTag (2nd generation) tags on the same Apple Account, stationary at home with owner devices around (iOS 26.x, AirTag firmware 3.0.49). Captured with a raw HCI socket on a Linux host next to Home Assistant's Bluetooth stack, and matched against keys exported for FindMy.py.
- DULT payload. The advertisement carries no Apple manufacturer data, only service data with the 16-bit UUID
0xFCB2:
07 16 b2 fc 01 01 10 03 (tag A)
07 16 b2 fc 01 01 14 00 (tag B)
Per draft-ietf-dult-accessory-protocol this is the network ID (0x01, Apple), the near-owner bit (0x01), and proprietary data (last two bytes, meaning unknown).
- Address. The address is a random static address equal to the first 6 bytes of the current primary key. The two most significant bits of the first byte are forced to
11 by the address type. (adv_key[0] | 0xC0) + adv_key[1:6] matched the observed address for the primary key at the current index.
- Rotation. The address rotates every 15 minutes and on state changes.
- The rest of the time, the same tags send the full Offline Finding payload (
0x12, length 0x19) with the current primary key, not the daily secondary one.
- Duration. DULT periods lasted from ~15–22 minutes to well over an hour. During them, not a single
0x12 advertisement was received for any key index 0–20000 of the tag, while original AirTags nearby kept sending 0x12 (nearby, length 0x02).
- Original AirTags near the owner, in the same place, use the nearby Offline Finding format as usual. I have not seen DULT from them.
Impact
OfflineFindingScanner / is_from() cannot see second-generation AirTags while they are in the DULT state. Anything built on local scanning then reports the tag as gone for 15+ minutes at a time.
Possible approach
What I did in a Home Assistant integration: lonsdaleite/hass-FindMy@80590e4, match_dult_advertisement in local_bluetooth.py.
- Parse service data
0000fcb2-0000-1000-8000-00805f9b34fb, require network ID 0x01, and read the near-owner bit.
- Match the address against
adv_key[:6] of the candidate keys with the two top bits of the first byte masked: adv_key[0] & 0x3F. This is like the nearby format, but without the two bits that nearby carries in its payload.
- There is no status/battery byte in this payload.
Happy to test changes against these tags.
Summary
Near its owner, an AirTag (2nd generation) periodically stops sending the Apple Offline Finding advertisement (manufacturer data
0x004C, type0x12) and sends the IETF DULT location-enabled payload instead.OfflineFindingScanneronly looks at Apple manufacturer data, so during these periods the tag is invisible to local scanning even though its keys are known. The address of the DULT advertisement is derived from the same rolling key, so it can still be matched.Observations
Two AirTag (2nd generation) tags on the same Apple Account, stationary at home with owner devices around (iOS 26.x, AirTag firmware 3.0.49). Captured with a raw HCI socket on a Linux host next to Home Assistant's Bluetooth stack, and matched against keys exported for FindMy.py.
0xFCB2:0x01, Apple), the near-owner bit (0x01), and proprietary data (last two bytes, meaning unknown).11by the address type.(adv_key[0] | 0xC0) + adv_key[1:6]matched the observed address for the primary key at the current index.0x12, length0x19) with the current primary key, not the daily secondary one.0x12advertisement was received for any key index 0–20000 of the tag, while original AirTags nearby kept sending0x12(nearby, length0x02).Impact
OfflineFindingScanner/is_from()cannot see second-generation AirTags while they are in the DULT state. Anything built on local scanning then reports the tag as gone for 15+ minutes at a time.Possible approach
What I did in a Home Assistant integration: lonsdaleite/hass-FindMy@80590e4,
match_dult_advertisementinlocal_bluetooth.py.0000fcb2-0000-1000-8000-00805f9b34fb, require network ID0x01, and read the near-owner bit.adv_key[:6]of the candidate keys with the two top bits of the first byte masked:adv_key[0] & 0x3F. This is like the nearby format, but without the two bits that nearby carries in its payload.Happy to test changes against these tags.