Find My iPhone / Wo Ist? / iPhone / iCloud / aktuellen Ort per WebAPI abfragen

Begonnen von Torxgewinde, 10 April 2022, 16:06:02

Vorheriges Thema - Nächstes Thema

Torxgewinde

Hallo,
Es ist recht einfach den Ort eines iCloud Gerätes abzufragen. Hier ein BASH Script wie das geht:

ICLOUD_USERNAME="bla@blubb.de"
ICLOUD_PASSWORD="passwort123"
ICLOUD_DEVICE="Bla iPhone"

#urlencode the USERNAME, its part of the URL below
URL_CODED_USERNAME="$(echo $ICLOUD_USERNAME | sed -e "s/@/%40/g" -e "s/\ /%20/g")"

RESPONSE="$(curl -s --dump-header /dev/null -X POST -L -u $ICLOUD_USERNAME:$ICLOUD_PASSWORD -H 'Content-Type: application/json; charset=utf-8' -H 'X-Apple-Find-Api-Ver: 2.0' -H 'X-Apple-Authscheme: UserIdGuest' -H 'X-Apple-Realm-Support: 1.0' -H 'User-agent: Find iPhone/1.3 MeKit (iPad: iPhone OS/4.2.1)' -H 'X-Client-Name: iPad' -H 'X-Client-UUID: 0cf3dc501ff813adb0b202baed4f37274b210843' -H 'Accept-Language: de-de' -H 'Connection: keep-alive' --cacert fmipmobile-icloud-com.pem https://fmipmobile.icloud.com/fmipservice/device/$URL_CODED_USERNAME/initClient)"

LONGITUDE="$(echo "$RESPONSE" | jq .[] | jq ".[] | select(.name==\"$ICLOUD_DEVICE\") | .location | .longitude" 2>/dev/null)"
LATITUDE="$(echo "$RESPONSE" | jq .[] | jq ".[] | select(.name==\"$ICLOUD_DEVICE\") | .location | .latitude" 2>/dev/null)"

echo "LONG: $LONGITUDE"
echo "LAT:  $LATITUDE"

#convert to human readable adress
# !!! perhaps you do not want to use this everytime, as it needs to tell the queried server the location !!!
curl -o- "https://nominatim.openstreetmap.org/reverse?lat=$LATITUDE&lon=$LONGITUDE&format=json" 2>/dev/null | jq '.display_name'


Das Script setzt die Programme jq und curl vorraus. Ggf. muss man diese noch installieren.

Damit curl das Zertifikat von Apples eigener PKI akzeptiert, kann man dieses runterladen und in der Datei fmipmobile-icloud-com.pem speichern. Hier der Inhalt der Datei:
-----BEGIN CERTIFICATE-----
MIID+DCCAuCgAwIBAgIII2l0BK3LgxQwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTE0
MDMwODAxNTMwNFoXDTI5MDMwODAxNTMwNFowbTEnMCUGA1UEAwweQXBwbGUgU2Vy
dmVyIEF1dGhlbnRpY2F0aW9uIENBMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5Jhawy4ercRWSjt+qPuGA11O6pGDM
fIVy9zB8CU9XDUr/4V7JS1ATAmSxvTk10dcEUcEY+iL6rt+YGNa/Tk1DEPoliJ/T
QIV25SKBtlRFc5qL45xIGoZ6w1Hi2pX4pH3bMN5sDsTF9WyY56b6VyAdGXN6Ds1j
D7cniC7hmmiCuEBsYxYkZivnsuJUfeeIOaIbgT4C0znYl3dKMgzWCgqzBJvxcm9j
qBUebDfoD9tTkNYpXLxqV5tGeAo+JOqaP6HYP/XbbqhsgrXdmTjsklaUpsVzJtGu
CLLGUueOdkuJuFQPbuDZQtsqZYdGFLuWuFe7UeaEE/cNobaJrHzRIXSrAgMBAAGj
gaYwgaMwHQYDVR0OBBYEFCzFbVLdMe+M7AiB7d/cykMARQHQMA8GA1UdEwEB/wQF
MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw
JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/
BAQDAgEGMBAGCiqGSIb3Y2QGAgwEAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQAj8QZ+
UEGBol7TcKRJka/YzGeMoSV9xJqTOS/YafsbQVtE19lryzslCRry9OPHnOiwW/Df
3SIlERWTuUle2gxmel7Xb/Bj1GWMxHpUfVZPZZr92sSyyLC4oct94EeoQBW4Fhnt
W2GO36rQzdI6wH46nyJO39/0ThrNk//Q8EVVZDM+1OXaaKATinYwJ9S/+B529vnD
AO+xg+pTbVw1xw0HAbr4Ybn+xZprQ2GBA+u6X3Cd6G+UJEvczpKoLqI1PONJ4BZ3
otxruY0YQrk2lkMyxst2mTU22FbGmF3Db6V+lcLVegoCIGZ4kvJnpCMN6Am9zCEx
EKC9vrXdTN1GA5mZ
-----END CERTIFICATE-----


Vielleicht findet sich ja jemand, der dies nach PERL & FHEM bringt. Das wäre praktisch.

gestein

Hallo,

Das klingt echt toll.
Könnte man damit auch das Gerät klingeln lassen?

Lg, Gerhard

betateilchen

Zitat von: Torxgewinde am 10 April 2022, 16:06:02
Das Script setzt die Programme jq und curl vorraus. Ggf. muss man diese noch installieren.
...
Vielleicht findet sich ja jemand, der dies nach PERL & FHEM bringt. Das wäre praktisch.

jq zum Extrahieren von JSON und curl zum Aufruf einer URL?
Dafür gibt es ein fertiges Modul in FHEM: 98_JsonMod.pm

Alternativ kann man die Aufbereitung der URL auch in der 99_myUtils.pm umsetzen und dort mit HttpUtils.pm und den FHEM eigenen JSON Funktionen arbeiten.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

CQuadrat

Hallo,

ich würde das Script gerne mal ausprobieren. Aber ich muss doch bestimmt eine eigene X-Client-UUID übergeben. Wo bekomme ich die denn her?

Danke und Gruß

Christoph
FHEM auf Mini-ITX-Server mit Intel Quad-Core J1900:
+ HM: HM-LAN, HM-USB, HM-MOD-UART mit div. HM-Komponenten
+ RFXtrx: Funkwetterstation Bresser mit ext. Thermometer, Regenmesser und Windmesser
+ TUL (KNX-Anbindung), KM271 (per ser2net), SONOS (div. Gimmicks), OneWire, Hue

Torxgewinde

Moin!

@gestein: Ja, die API bietet die Möglichkeiten. Einige Bibliotheken, vor allem in Python und Node, haben da bereits gut vorgearbeitet. Interessant wäre zum Beispiel dieses Projekt https://github.com/picklepete/pyicloud. Ich hätte es lieber mit FHEM und damit vorzugsweise in PERL, allerdings habe ich da noch nichts fertiges gefunden. Bei NPM, PIP und Docker bin ich zurückhaltend, da die Qualität durchaus "durchwachsen" sein kann - da ist viel Licht, aber auch einige Schatten. Pakete aus den Distributionen sind da IMHO deutlich unkritischer.

@betateilchen: Ich hoffe mit dem Codeschnipsel jemanden aus dem FHEM Umfeld zu inspirieren. Wenn ich es für mich selbst zusammenfrickel, wähle ich lieber BASH und melde es mir mit MQTT an FHEM.

@CQuadrat / Christoph: Die UUID wird scheinbar nicht geprüft, ich habe die URL auch aus zusammengesuchten Fragmenten zusammengebaut, bis es funktionierte. Da ist keine exakte UUID hinterlegt. Vielleicht kann man diesen Header sogar komplett weglassen, das müsste man mal ausprobieren.

CQuadrat

Ich habe es zum Laufen bekommen. Allerdings muss ich bei curl noch die Option ,,-k" verwenden. Irgendwie habe ich da wohl noch Probleme mit der Überprüfung des Zertifikates - mal schauen.

Mal sehen, wie ich das in FHEM integriert bekomme. Damit könnte mindestens mal so etwas wie eine Anwesenheitsprüfung gut realisieren.
FHEM auf Mini-ITX-Server mit Intel Quad-Core J1900:
+ HM: HM-LAN, HM-USB, HM-MOD-UART mit div. HM-Komponenten
+ RFXtrx: Funkwetterstation Bresser mit ext. Thermometer, Regenmesser und Windmesser
+ TUL (KNX-Anbindung), KM271 (per ser2net), SONOS (div. Gimmicks), OneWire, Hue

kalleknx

Die Anwesenheitserkennung mittels Apple bzw. HomeKit kannst Du auch einfacher gestalten (Voraussetzung homebridge läuft):
- definiere dir einen dummy Schalter in FHEM und integriere ihn in homebridge
- in der Apple Home App kannst Du einfache Automatisierungen erstellen, zB Wenn ich das Haus verlasse, dann schalte <Dummy Schalter> ein

Das wiederum kannst Du einfach in FHEM auswerten. Ich hatte früher geofancy laufen, welches jetzt nicht mehr notwendig ist.

CQuadrat

Danke. Mit homebridge hatte ich bisher noch nichts am Hut. Ich schaue es mir mal an.
FHEM auf Mini-ITX-Server mit Intel Quad-Core J1900:
+ HM: HM-LAN, HM-USB, HM-MOD-UART mit div. HM-Komponenten
+ RFXtrx: Funkwetterstation Bresser mit ext. Thermometer, Regenmesser und Windmesser
+ TUL (KNX-Anbindung), KM271 (per ser2net), SONOS (div. Gimmicks), OneWire, Hue

erwin

Hi,
Ich habe es am Laufen, mittels JsonMod Modul. Allerdings ist ein Patch für das Modul erforderlich, siehe: https://forum.fhem.de/index.php/topic,127428.0.html
Client Certifikat ist bei mir nicht erforderlich.
Sobald der support offiziell ist, kann ich gerne die Details zur definition liefern.
l.g. erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

erwin

Hi nochmal !
..nachdem sich beim Jsonmod-Thread nix tut, hier mal die raw-definition:

defmod jtestM JsonMod https://[uid]:[pwd]@fmipmobile.icloud.com/fmipservice/device/[uid]/initClient\

attr jtestM httpHeader Accept: */*\
Content-Type: application/json;; charset=utf-8\
X-Apple-Find-Api-Ver: 2.0\
X-Apple-Authscheme: UserIdGuest\
X-Apple-Realm-Support: 1.0\
User-agent: Find iPhone/1.3 MeKit (iPad: iPhone OS/4.2.1)\
X-Client-Name: iPadM\
X-Client-UUID: 0cf3dc501ff813adb0b202baed4f37274b210843\
Accept-Language: de-de\
Connection: keep-alive\

attr jtestM interval 1/15 * * * *
attr jtestM readingList #single(jsonPath('content.2.location.latitude'),'lat',0);;\
#single(jsonPath('content.2.location.longitude'),'lon',0);;\
single(jsonPath('content.2.location.horizontalAccuracy'),'acc',0);;\
single(jsonPathf('content.2.batteryLevel','%0.2f'),'Battlevel',0);;\
single(jsonPath('serverContext.serverTimestamp'),'ServerTS',0);;\
single(jsonPath('content.2.location.timeStamp'),'ts',0);;\
multi(jsonPath('$.content.2.location'), 'location', concat(propertyf('latitude', 0, '%02.6f'), ',', propertyf('longitude', 0, '%03.6f')));;\
#complete();;\

attr jtestM userReadings TS:ts.* {my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(ReadingsVal($name,'ts',0)/1000 );; sprintf("%02d:%02d:%02d %02d/%02d/%04d ", $hour,$min,$sec,$mday,$mon + 1,$year + 1900)}, \
#location:lon.* {sprintf('%.6f,%.6f',ReadingsNum($name,'lat',0), ReadingsNum($name,'lon',0)) }

Funktioniert allerdings nur mit dem JsonMod-patch aus dem vorigem post !
entweder die ersten beiden "single(..." verwenden, für lat und lon,
oder das "multi(..." , das ein kombiniertes reading erzeugt.
ob "content.2.location oder "content.1.location" der richtige json-path ist, hängt von der Anzahl der definierten Geräte im Apple-ID ab!

Als secret zu setzen: uid <apple userID> und pwd <apple-passwort>
l.g. erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

gestein

Hallo Erwin,

ich habe mich gerade an Deinem Code versucht und wollte das diff manuell eingeben.
Allerdings scheint meine "98_JsonMod.pm" neuer zu sein.
SVN24783 2021-07-21 22:37:12 UTC

Die erste Zeile ist noch klar:
46                @attrList = qw(
47                        httpHeader:textField-long
->+                      httpMethod:GET,POST
48                        httpTimeout
49                        update-on-start:0,1


Aber wo muss ich die zweite Zeile dazugeben?

Danke im Voraus
lg, Gerhard

erwin

Hi, Sorry bitte um Geduld bis nächste Woche, ich bin mit einem Sozialprojekt
in der Adria Segeln, hab keinen Zugriff aufs System.
L.g. erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

binford6000

Zitat von: gestein am 21 September 2022, 13:00:05
Hallo Erwin,

ich habe mich gerade an Deinem Code versucht und wollte das diff manuell eingeben.
Allerdings scheint meine "98_JsonMod.pm" neuer zu sein.
SVN24783 2021-07-21 22:37:12 UTC

Die erste Zeile ist noch klar:
46                @attrList = qw(
47                        httpHeader:textField-long
->+                      httpMethod:GET,POST
48                        httpTimeout
49                        update-on-start:0,1


Aber wo muss ich die zweite Zeile dazugeben?

Danke im Voraus
lg, Gerhard

Hallo Gerhard,
das klappt auch mit deiner Version. Gerade getestet:
patch 98_JsonMod.pm 98_JsonMod.diff

VG Sebastian

gestein

Hallo Sebastian,

Vielen Dank. Wieder was dazu gelernt.
Hat geklappt.
Man muss nur noch das Attribut ,,httpMethod" auf ,,POST" umstellen und schon sind die Parameter da.

Wenn ich mehrere Geräte im ,,Wo ist?" habe, muss ich alle einzeln mit einem entsprechenden ,,Single"-Eintrag in der readingList eintragen.
Oder gibt es eine Art ,,for"-Schleife mit der man das lösen könnte?

Vielen Dank auf alle Fälle.
Lg, Gerhard

gestein

Und noch eine Frage bitte:
Mit dem aktuellen Code werden die Geräte angezeigt.
Kann man auch auf die Objekte zugreifen (also AirTags etc.)?

Danke, lg, Gerhard

eddy242

Hallo zusammen,

Patch habe ich angewendet, Secrets gesetzt, Method auf POST gesetzt. Trotzdem:
API_LAST_MSG
<hidden>: wrong authentication
API_LAST_RES
1665822598.65733


Was kann es sein? Danke!

erwin

ZitatWas kann es sein?

sorry, bin erst jetzt auf das Problem gestoßen...

Bei mir gleich! (wrong auth...) Offensichtlich hat Apple was an der API-geändert, letzter gültiger Wert vom 5/10 18:10:00
Weiß momentan auch keine Lösung.
l.g. erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

eddy242

Es hat sich was getan, zwar noch nicht zum besseren, aber immerhin. Vielleicht lässt sich die Response analysieren, was geändert werden muss?

API_LAST_MSG
invalid server response
API_LAST_RES
1667666086.79828

ReneR1986

Hallo zusammen,
hat noch einmal jemand versucht dem nachzugehen oder besteht kein Bedarf mehr?
Wollte mir ein paar AirTags kaufen. Wäre super, wenn man die Infos in FHEM bekommen würde  :D
Würde gern auch selber mal ein bisschen testen, allerdings bin ich noch nicht wirklich fit was JsonMod angeht.

erwin

Ja, hab ich versucht, die login Prozedur ist so komplex, ich durchschau das einfach nicht!
Es gibt ein JS script im web, das ist allerdings auch schon etwas älter. ich gehe davon aus, dass uns das nicht weiterhilft!

Interesse/Bedarf wär schon vorhanden,  es geht nicht um das knowhow v. HTTPMOD od. JSONMOD, sondern das apple-login zu verstehen....
Falls irgendwer eine Lösung findet, egal mit welcher Programmiersprache, kann man das sicher auch mit FHEM umsetzen....
l.g.erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

ReneR1986

Hm OK,
hier ist es vermutlich nicht weniger komplex, oder?
https://www.icloud.com/

Oder läuft es am Ende eh auf den Link im obigen JsonMod raus..?

Das JS Script habe ich auch gesehen aber wie du sagst, es ist auch schon älter..

ReneR1986

Habe gerade auch noch gesehen, dass die Anmeldung über 2-Faktor Authentifizierung läuft.  :-\

erwin

ZitatOder läuft es am Ende eh auf den Link im obigen JsonMod raus..?
im Prinzip ja, mit einem zusätzlichen redirect.....
Ich hab offengestanden keine Idee wie man das login "nachbauen" könnte.
Da müsste sich ein JS / Browser experte finden!
Link zum Einstieg: https://www.icloud.com/find/
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

ReneR1986

Ich habe einen Workaround gefunden!
Hier kann man das über ein Python Script laufen lassen.
https://github.com/picklepete/pyicloud
Das ganze funktioniert auch mit 2-Faktor Authentifizierung.
Nach der ersten Anmeldung wird man nach der ID, also dem 2ten Faktor gefragt, der dann wie gewohnt auf dem Apple Device erscheint und der dann hier eingegeben werden muss. Bissher musste ich das nur einmal machen. Wie oft Apple danach fragt, weiß ich nicht.
Am Ende habe ich einen Cronjob laufen, der das Script stündlich ausführt.
Sicherlich nicht immer zuverlässig. Spätestens wenn erneut nach dem 2ten Faktor gefragt wird, muss man manuell wieder Hand anlegen. Bissher läuft es aber gut.

1. Abhängigkeiten installieren
pip install -r requirements.txt

requirements.txt

tzlocal==2.0.0
keyring==9.3.1
requests
keyrings.alt
click
certifi==2021.10.8
pyicloud
paho-mqtt


2.Phyton Script (Details siehe im Link oben)
EMAIL und PASSWORT anpassen.
Weiter unten die Werte beim Broker anpassen BROKERIP und Client ID bzw. Topics wenn gewünscht.


from pyicloud import PyiCloudService
from paho.mqtt import client as mqtt_client
import re

api = PyiCloudService('<EMail>', '<PASSWORT>')

if api.requires_2fa:
    print("Two-factor authentication required.")
    code = input("Enter the code you received of one of your approved devices: ")
    result = api.validate_2fa_code(code)
    print("Code validation result: %s" % result)

    if not result:
        print("Failed to verify security code")
        sys.exit(1)

    if not api.is_trusted_session:
        print("Session is not trusted. Requesting trust...")
        result = api.trust_session()
        print("Session trust result %s" % result)

        if not result:
            print("Failed to request trust. You will likely be prompted for the code again in the coming weeks")
elif api.requires_2sa:
    import click
    print("Two-step authentication required. Your trusted devices are:")

    devices = api.trusted_devices
    for i, device in enumerate(devices):
        print(
            "  %s: %s" % (i, device.get('deviceName',
            "SMS to %s" % device.get('phoneNumber')))
        )

    device = click.prompt('Which device would you like to use?', default=0)
    device = devices[device]
    if not api.send_verification_code(device):
        print("Failed to send verification code")
        sys.exit(1)

    code = click.prompt('Please enter validation code')
    if not api.validate_verification_code(device, code):
        print("Failed to verify verification code")
        sys.exit(1)
       
#print("Devices")
#print(api.devices)
print(api.iphone.location())


#Mqtt
broker = '<BrokerIP>'
port = 1883
topic = "location/ReneiPhone"
client_id = 'iCloud'
# username = 'xxx'
# password = 'xxx'

def connect_mqtt():
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            print("Connected to MQTT Broker!")
        else:
            print("Failed to connect, return code %d\n", rc)

    client = mqtt_client.Client(client_id)
    #client.username_pw_set(username, password)
    client.on_connect = on_connect
    client.connect(broker, port)
    return client


def publish(client):
    string = str(api.iphone.location())
   
    longitude_regex = re.compile(r"u'longitude':\s*([-+]?\d*\.\d+|\d+)")
    match = longitude_regex.search(string)
    longitude = match.group(1)
   
    latitude_regex = re.compile(r"u'latitude':\s*([-+]?\d*\.\d+|\d+)")
    match = latitude_regex.search(string)
    latitude = match.group(1)
   
    msg = latitude + "," + longitude
    result = client.publish(topic, msg)


client = connect_mqtt()
publish(client)