retrieve data from FindMy network and send it somewhere else
note: this is still a work in progress
why #
we want to programatically retrieve findmy items data and send it somewhere else
the goal is to ingest that data in traccar
notes #
-
I am running ios 17.x on an old iphone and macos 14 Sonoma in a vm on proxmox (I wrote an article about that)
this vm has only 2 cpu cores and 4gb of ram (of which only 2 are used right now) -
The tests are run with a dedicated icloud account
-
I am sharing some airtags from a second icloud account
-
You cannot re-share the tags that have been shared with you
phantom item: #
i've shared a tag, then removed and re-shared before I could accept it, on macos I found 2 invites for the same tag and accepted both. At this point I could only remove the "alive" one since the first one, that have been unshared) cannot be managed or deleted.
phantom notifications #
on the iphone I see the notifications for the invites but don't see the invites inside the section "items" in "findmy"
partial data before accepting shared item #
I've shared a second tag and even before I could accept the invite, I've seen the item data in Items.data
the data is without sensitive data until you accept the tag sharing (see more in "how to read the data in the csv")
airtags #
there's a limit of 16 AirTags per Apple ID
show me the data #
the items (airtags and compatible devices) data is in:
~/Library/Caches/com.apple.findmy.fmipcore/Items.data
converting data to csv #
for our tests we'll use airtag alex script: https://github.com/icepick3000/AirtagAlex
it converts the items data to csv
how to read the data in the csv #
example of the yet to be accepted shared tag:
2023-11-12 16:00:00,"Tag01","Not Available","b777",77777,77,3,"2.0.0",0,null,null,null,null,0,0,0,0,"null","null","null","","","","","","","","","",""
example of the accepted tag (so all data is there)
:
2023-11-12 16:01:00,"Tag01","283KHVFAHSF832","b777",77777,77,3,"2.0.0",4,"crowdsourced",44.113763000000000,12.575151000000000,1699801200000,-1,42.000000000000000,0,-1,"false","true","true","Via Fasulla 0","0","IT","","Lombardia","Via Fasulla","Bergamo","Italy","",""
(the data has been altered for privacy)
use case #
a headless vm with macos Sonoma (which supports items=airtag sharing),
that receives shared airtags and sends the data somewhere else
this way we can leverage the power of FindMy network but still using the tools we like,
we can see this as a "compatibility layer"
what is working/thoughts #
- having only macOS online seems to be the way to go, the device sharing arrive very slowly but arrive
- having also the icloud account logged on an iphone does not seem to give any added value
- on iOS I see the "phantom notification" of a new item but nowhere to accept it, where on macos at least I can accept the invite
- having the macOS user not logged in does not seem to impact the update of the devices
important #
it seems that for having the items update their position you need to have macos logged in and findmy open,
the screen can be locked.
traccar script #
this is a modified version of AirtagAlex's script,
this writes data to the csv and also sends it to traccar
#!/bin/bash
ITEMS_FILE="./Items.data"
CSV_FILE="./Airtags.csv"
CSV_HEADER="datetime,name,serialnumber,producttype,productindentifier,vendoridentifier,antennapower,systemversion,batterystatus,locationpositiontype,locationlatitude,locationlongitude,locationtimestamp,locationverticalaccuracy,locationhorizontalaccuracy,locationfloorlevel,locationaltitude,locationisinaccurate,locationisold,locationfinished,addresslabel,addressstreetaddress,addresscountrycode,addressstatecode,addressadministrativearea,addressstreetname,addresslocality,addresscountry,addressareaofinteresta,addressareaofinterestb"
### VARS for traccar:
traccar_url="http://your.traccar.url:5055"
copy_items_data() {
echo "Creating a copy of Items.data to prevent potential file corruption"
if ! cp -p ~/Library/Caches/com.apple.findmy.fmipcore/Items.data "$ITEMS_FILE"; then
echo "Failed to copy Items.data file. Please ensure Terminal has 'Full Disk Access' in the 'Privacy & Security' section in macOS Preferences" >&2
exit 1
fi
}
create_csv_file() {
echo "Checking if $CSV_FILE exists"
if [ ! -f "$CSV_FILE" ]; then
echo "$CSV_FILE does not exist, creating one"
if ! echo "$CSV_HEADER" >> "$CSV_FILE"; then
echo "Failed to create $CSV_FILE. Please ensure the destination directory is writable." >&2
exit 1
fi
fi
}
while true; do
copy_items_data
create_csv_file
echo "Checking number of Airtags to process"
airtagsnumber=$(jq ".[].serialNumber" "$ITEMS_FILE" | wc -l)
echo "Number of Airtags to process: $airtagsnumber"
airtagsnumber=$((airtagsnumber-1))
for j in $(seq 0 "$airtagsnumber"); do
echo "Processing airtag number $j"
datetime=$(date +"%Y-%m-%d %T")
serialnumber=$(jq ".[$j].serialNumber" "$ITEMS_FILE")
name=$(jq ".[$j].name" "$ITEMS_FILE")
producttype=$(jq ".[$j].productType.type" "$ITEMS_FILE")
productindentifier=$(jq ".[$j].productType.productInformation.productIdentifier" "$ITEMS_FILE")
vendoridentifier=$(jq ".[$j].productType.productInformation.vendorIdentifier" "$ITEMS_FILE")
antennapower=$(jq ".[$j].productType.productInformation.antennaPower" "$ITEMS_FILE")
systemversion=$(jq ".[$j].systemVersion" "$ITEMS_FILE")
batterystatus=$(jq ".[$j].batteryStatus" "$ITEMS_FILE")
locationpositiontype=$(jq ".[$j].location.positionType" "$ITEMS_FILE")
locationlatitude=$(jq ".[$j].location.latitude" "$ITEMS_FILE")
locationlongitude=$(jq ".[$j].location.longitude" "$ITEMS_FILE")
locationtimestamp=$(jq ".[$j].location.timeStamp" "$ITEMS_FILE")
locationverticalaccuracy=$(jq ".[$j].location.verticalAccuracy // 0" "$ITEMS_FILE")
locationhorizontalaccuracy=$(jq ".[$j].location.horizontalAccuracy // 0" "$ITEMS_FILE")
locationfloorlevel=$(jq ".[$j].location.floorlevel // 0" "$ITEMS_FILE")
locationaltitude=$(jq ".[$j].location.altitude // 0" "$ITEMS_FILE")
locationisinaccurate=$(jq ".[$j].location.isInaccurate" "$ITEMS_FILE" | awk '{ print "\""$0"\"" }')
locationisold=$(jq ".[$j].location.isOld" "$ITEMS_FILE" | awk '{ print "\""$0"\"" }' )
locationfinished=$(jq ".[$j].location.locationFinished" "$ITEMS_FILE" | awk '{ print "\""$0"\"" }' )
addresslabel=$(jq ".[$j].address.label // \"\"" "$ITEMS_FILE")
addressstreetaddress=$(jq ".[$j].address.streetAddress // \"\"" "$ITEMS_FILE")
addresscountrycode=$(jq ".[$j].address.countryCode // \"\"" "$ITEMS_FILE")
addressstatecode=$(jq ".[$j].address.stateCode // \"\"" "$ITEMS_FILE")
addressadministrativearea=$(jq ".[$j].address.administrativeArea // \"\"" "$ITEMS_FILE")
addressstreetname=$(jq ".[$j].address.streetName // \"\"" "$ITEMS_FILE")
addresslocality=$(jq ".[$j].address.locality // \"\"" "$ITEMS_FILE")
addresscountry=$(jq ".[$j].address.country // \"\"" "$ITEMS_FILE")
addressareaofinteresta=$(jq ".[$j].address.areaOfInterest[0] // \"\"" "$ITEMS_FILE")
addressareaofinterestb=$(jq ".[$j].address.areaOfInterest[1] // \"\"" "$ITEMS_FILE")
echo "Writing data to $CSV_FILE"
echo "$datetime","$name","$serialnumber","$producttype","$productindentifier","$vendoridentifier","$antennapower","$systemversion","$batterystatus","$locationpositiontype","$locationlatitude","$locationlongitude","$locationtimestamp","$locationverticalaccuracy","$locationhorizontalaccuracy","$locationfloorlevel","$locationaltitude","$locationisinaccurate","$locationisold","$locationfinished","$addresslabel","$addressstreetaddress","$addresscountrycode","$addressstatecode","$addressadministrativearea","$addressstreetname","$addresslocality","$addresscountry","$addressareaofinteresta","$addressareaofinterestb" >> "$CSV_FILE"
### block to write data into traccar:
#
serialnumber=`echo $serialnumber | sed 's/\"//g'`
### removed due to updated code: tracname=`cat ~/Desktop/Airtags/Items.data | jq .[$j].name | sed 's!"!!g'`
### batterystatus=`cat ~/Desktop/Airtags/Items.data | jq .[$j].batteryStatus`
akku=$((batterystatus * 100))
# send data to traccar:
wget --spider $traccar_url/?id=$serialnumber\&lat=$locationlatitude\&lon=$locationlongitude\&speed=0\&user=Airtag\&batteryLevel=$akku\&accuracy=$locationhorizontalaccuracy\×tamp=$locationtimestamp\&serialnumber=$serialnumber\&lastupdate=$locationtimestamp
#
echo "Sleep for 1 second between Airtags"
sleep 1
#
### END block to write data into traccar
done
echo -e "Checking again in 1 minute...\n"
sleep 60
done