Apple,  SmartHome,  SmartRemote

Apple TV Info auslesen

Als nächsten Baustein möchte ich Daten aus einem Apple TV 4K* auslesen, um diese weiter verarbeiten zu können. In meinem Fall Informationen, was aktuell abgespielt wird und diese Daten auf einer LED Matrix anzeigen zu lassen. Im vorherigen Beitrag findet ihr meine detaillierte Beschreibung, wie man eine LED Matrix mit einem SAT Receiver bespielt und diese sogar in HomeKit einbindet.

In meinem Setup kommen zwei Apple HomePods als Standard-Lautsprecher mit einem Apple TV 4K* zum Einsatz.

*Affiliate Link zu Amazon


Zudem nutze ich den Streaming Dienst Apple Music um Songs, Podcasts abzuspielen. Da bereits auf einem iPhone / iPad Informationen (über das Control Center) der aktuell laufenden Songs, Sendungen, Filme eines Apple TV 4K* anzeigt werden, wäre es doch durchaus sinnvoll, wenn eben diese Informationen auf einem anderen Bildschirm oder einer LED Matrix ausgegeben werden könnten.

Im Folgenden lest ihr, wie man dies Daten abgreift und für eigene Zwecke wiederverwendet.
Hier ein paar Beispiele meiner Umsetzung zu diesem Projekt, anhand von Videos:

Amazon Prime Video

Apple Music


Start über pyatv

Bei meiner Recherche bin ich auf das Python Projekt pyatv gestoßen. Hiermit ist es möglich, aktuelle laufenden Medieninformationen eines Apple TV* auszulesen. Details und Fragen werden gerne auch vom Entwickler Pierre Ståhl direkt über GitHub beantwortet.

Für meinen Anwendungsfall ist insbesondere das Skript atvscript entscheidend, da dies auf einfache Art und Weise Daten eines Apple TV* ausliest. In diesem Beitrag möchte ich euch eine detaillierte Beschreibung meines Projekts mitgeben, so dass ihr auch dies für eure Bedürfnisse verwenden könnt. 

Installation von pyatv – atvremote / atvscript

Bei meinem Setup kommt erneut ein Raspberry Pi Zero W* zum Einsatz, da dieser von seiner Leistung vollkommend ausreichend ist. Mehr ist als Hardware nicht erforderlich.

*Affiliate Link zu Amazon

Solltet ihr noch keine Erfahrung mit einem Raspberry Pi haben, hier eine kurze Beschreibung zur Installation:

Neuaufsetzen eines Raspberry PI – Image:

  1. Download eines Image von raspberrypi.org
  2. Micro-SD Karte formatieren (SD Card Formatter) und flashen (Etcher) mit oben genanntem Image
  3. Ablage folgender Config-Dateien auf Micro-SD Karte, um direkt im WLAN zu sein und SSH Zugriff über die Konsole zu haben:
    1. leere ssh Datei
    2. wpa_supplicant.conf 
# Datei wpa_supplicant.conf in der Boot-Partition
network={
       ssid="wlan-bezeichnung"
       psk="passwort"
       key_mgmt=WPA-PSK
}

Software Installation pyatv

Nun geht’s mit der Installation von pyatv weiter:

  1. ssh Zugriff auf PI: ssh pi@IP
    Hierbei ist IP durch deine IP des Raspberry Pi zu ersetzen, in meinem Fall: ssh pi@192.168.178.48
  2. Das Standardpasswort des Raspberry PI lautet: raspberrry
  3. Eingabe des Befehls: pip3 install pyatv
    Die Installation sollte recht schnell gehen.
  4. Wenn alles fertig ist, könnt ihr die Funktionsweise mit folgendem Befehl testen: atvscript scan

Troubleshooting

Da es bei mir anfänglich ein paar Hürden gab, nehme ich hier auf etwas „Troubleshooting“ mit auf.

  1. WARNING: The script pypprint is installed in '/home/pi/.local/bin' which is not on PATH.
    Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location


    Es kann bei der Ausführung von atvscript scan zu folgender Meldung kommen:
    -bash: atvscript: command not found

    Dieses Problem behebt ihr mit folgendem Befehl:
    sudo mv ~/.local/bin/atvscript /usr/bin/atvscript

    Dabei wird atvscript nach PATH verschoben und ihr sollte startklar sein.
  1. Bei einigen Installationen kommt es weiterhin bei der Ausführung zu Problemen:
    Illegal instruction
    bzw. auch
    File "/usr/bin/atvscript", line 5, in <module> from pyatv.scripts.atvscript import main ModuleNotFoundError: No module named 'pyatv'

    Hier gibt es auch eine Lösung, mittels dem Deinstallieren von miniaudio über den Befehl:
    pip uninstall miniaudio

    Anschließend müsst ihr wieder miniaudio direkt von der Quelle installieren:
    pip install --no-binary :all: miniaudio

Sobald pyatv erfolgreich installiert wurde, kann es mit einem ersten Test losgehen:

  • IP Adresse des Apple TV* bestimmen mit Befehl:
    atvscript scan
  • Test einer Ausgabe, was auf dem Apple TV* aktuell läuft, wobei ihr natürlich eure IP des Apple TV* verwenden müsst:
    atvscript -s 192.168.178.27 playing 
  • Ausgabe in der Konsole:
    • Beispiel für Magenta TV
      {"result": "success", "datetime": "2021-08-23T12:53:37.239648+02:00", "hash": "32617F16-88BE-4C63-9E56-4C166CFA18BC", "media_type": "unknown", "device_state": "playing", "title": "drehscheibe", "artist": "ZDF", "album": null, "genre": null, "total_time": null, "position": 22, "shuffle": "off", "repeat": "off", "series_name": null, "season_number": null, "episode_number": null, "app": "Magenta TV", "app_id": "de.telekom.entertaintv-iphone"}
    • Beispiel für Apple Music
      {"result": "success", "datetime": "2021-08-09T18:40:17.931148+01:00", "hash": "rR5ERqh9TQ6JtTpSe1XTFQ\u2206R64LAqHWTBy1pZTP8dL1Gw", "media_type": "music", "device_state": "playing", "title": "The Riverboat Song", "artist": "Ocean Colour Scene", "album": "21", "genre": "Rock", "total_time": 298, "position": 67, "shuffle": "off", "repeat": "off", "series_name": null, "season_number": null, "episode_number": null, "app": "Musik", "app_id": "com.apple.TVMusic"}

Nun könnt ihr also mit diesen Daten die Informationen von einem Apple TV* auslesen und weiterverarbeiten. Ziemlich cool, oder? 😎

Vorrangig sind die Daten zu „device_state“, „title“, „artist“ und „app“ von Bedeutung.

In meinem Falle nutze ich, wie im vorherigen Beitrag, erneut Node-Red zur weiteren Verarbeitung, auch in Richtung meiner LED Matrix.

Node-Red Integration

Um die Umsetzung einfacher zu gestalten, könnt ihr meinen vorbereiteten NODE-FLOW importieren.
Habe diesen auf meine Bedürfnisse angepasst, möchte aber auch der Ursprungsversion von CRXPorter danken.

Nun geht es weiter mit der Übermittlung der Daten eines Apple TV* an die LED Matrix.

Zum Ausgang des Node-In Link „Apple TV 4K Playing“ bediene ich mich einer Funktion, welche, wie im vorherigen Beitrag, einen payload an die LED Matrix weiterreicht.

Troubleshooting

Sollte es zu Problemen bei der Ausführung in Node-Red zu einer Fehlermeldung kommen, dass importierte Typen nicht erkannt werden (ui_text, ui_button, ui_group, ui_tab) müssen über Palette verwaltenInstallation folgende nodes installiert werden:

  • node-red-node-daemon
  • node-red-dashboard

Ausgabe auf LED Matrix

Bei der Ausgabe wird ein entsprechendes Icon vor dem Textlauf auf der LED Matrix dargestellt. Dies könnt ihr natürlich individuell gestalten, dies hier ist nur mein Beispiel:

// Icons:
// 422, 588: Netflix
// 368: Music
// 1064, 1051, 1052: TV
// 212: Apple

Innerhalb der Funktion unterscheide ich zwischen den Apps Apple Music, ob normale Musik oder Radio gespielt wird, sowie ob es sich um Podcasts handelt.

// if Apple Music is playing
if (msg.payload.app == "Musik") {
    // if Apple Music Songs / Alben / Playbook are playing
    if (msg.payload.device_state == "playing" && msg.payload.artist != null){
        msg.payload = {"name":"test","force":true,"icon":368,"repeat":2,"text":msg.payload.artist +": "+ msg.payload.title,"color":[51,204,51]};
    }
    // if Apple Music - Radio is playing
    if (msg.payload.device_state == "playing" && msg.payload.artist == null){
        msg.payload = {"name":"test","force":true,"icon":368,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
    }
    msg.headers = {"content-type": "application/json"};
    return msg;
}

// if Podcasts is playing
if (msg.payload.app == "Podcasts") {
    if (msg.payload.device_state == "playing"){
        msg.payload = {"name":"test","force":true,"icon":1341,"repeat":2,"text":msg.payload.artist +": "+ msg.payload.title,"color":[51,204,51]};
    }
    /*if (msg.payload.device_state == "paused"){
        msg.payload = {"name":"test","force":true,"icon":1064,"repeat":1,"text":"Musik aus","color":[51,204,51]};
    }*/
    msg.headers = {"content-type": "application/json"};
    return msg;
}

Das Ganze dann für euch auch in einem kurzen Video.

Siri Control Apple Music

Siri Control Podcasts

Da auf meinem Apple TV* weitere Apps für TV, Serien und Filme zum Einsatz kommen, habe ich auch hierzu eine Abfrage mit eingebaut. Konkret für Magenta TV, Netflix, Apple TV+, Amazon Prime Video, Disney+ und die Medien App infuse.

Magenta TV (SAT1)

Magenta TV (ZDF, ARD)


Die Umsetzung in Node-Red sieht dabei wie folgt aus:

// if a streaming app is playing
        // if Magenta TV is playing
        if (msg.payload.app == "Magenta TV" || msg.payload.app_id == "de.telekom.entertaintv-iphone") {
            if (msg.payload.device_state == "playing"){
                msg.payload = {"name":"test","force":true,"icon":131,"repeat":2,"text":msg.payload.artist +": "+ msg.payload.title,"color":[51,204,51]};
                msg.headers = {"content-type": "application/json"};
        return msg;
            }
        } 
        // if Netflix is playing
        if (msg.payload.app == "Netflix") {
                if (msg.payload.device_state == "playing" && msg.payload.title != null){
                    msg.payload = {"name":"test","force":true,"icon":588,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
                }
                if (msg.payload.device_state == "playing" && msg.payload.title == null){
                    msg.payload = {"name":"test","force":true,"icon":588,"repeat":2,"text":"Netflix","color":[51,204,51]};    
                }
        msg.headers = {"content-type": "application/json"};
        return msg;
        }
        // if Apple TV+ is playing
        if (msg.payload.app == "TV") {
                if (msg.payload.device_state == "playing" && msg.payload.title != null){
                    msg.payload = {"name":"test","force":true,"icon":212,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
                }
                if (msg.payload.device_state == "playing" && msg.payload.title == null){
                    msg.payload = {"name":"test","force":true,"icon":212,"repeat":2,"text":"Apple TV+","color":[51,204,51]};
                }
        msg.headers = {"content-type": "application/json"};
        return msg;
        }
        // if Amazon Prime Video is playing
        if (msg.payload.app == "Prime Video") {
                if (msg.payload.device_state == "playing" && msg.payload.title != null){
                    msg.payload = {"name":"test","force":true,"icon":327,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
                }
                if (msg.payload.device_state == "playing" && msg.payload.title == null){
                    msg.payload = {"name":"test","force":true,"icon":327,"repeat":2,"text":"Prime Video","color":[51,204,51]};
                }
        msg.headers = {"content-type": "application/json"};
        return msg;
        }
        // if Disney+ is playing
        if (msg.payload.app_id == "com.disney.disneyplus") {
                if (msg.payload.device_state == "playing" && msg.payload.title != null){
                    msg.payload = {"name":"test","force":true,"icon":1052,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
                }
                if (msg.payload.device_state == "playing" && msg.payload.title == null){
                    msg.payload = {"name":"test","force":true,"icon":1052,"repeat":2,"text":"Disney+","color":[51,204,51]};
                }
        msg.headers = {"content-type": "application/json"};
        return msg;
        }
        // if infuse is playing
        if (msg.payload.app_id == "com.firecore.infuse") {
                if (msg.payload.device_state == "playing" && msg.payload.title != null){
                    msg.payload = {"name":"test","force":true,"repeat":2,"text":msg.payload.title,"color":[51,204,51]};
                }
                if (msg.payload.device_state == "playing" && msg.payload.title == null){
                    msg.payload = {"name":"test","force":true,"repeat":2,"text":"infuse","color":[51,204,51]};
                }
        msg.headers = {"content-type": "application/json"};
        return msg;
        }

Zur Veranschaulichung ein paar weitere Videos:

Netflix

Disney+

Apple Music – Eins Live

infuse – NAS


Fazit

Für mich funktioniert die Ausgabe perfekt. Bin wirklich happy mit dieser Ausführung. 🙂 Die Reaktionszeit ist dabei sehr gut und bei Song- / Programmwechsel funktioniert der Wechsel reibungslos. Mein Projekt wird sicherlich erweitert werden. 

Ausblick

Interessant ist pyatv auch für die Integration von Apple Music bzw. Apple TV* in eine Universal Fernbedienung, wie die kommende Remote Two – auch in meinem Beitrag zu lesen. Hier wäre das Einbinden von Metadaten artwork_url - Return artwork URL for what is currently playing (siehe pyatv docu) sinnvoll, so dass Alben Cover direkt auf Remote Two angezeigt wird.

Mit dem Gründer Marton Borzak (Link zu Twitter Account YIO Remote) bin ich hierzu in regem Kontakt und möchte auch meinen Blog nutzen, um hierfür etwas Werbung zu machen. Für euch mag dies auch ein interessanter Ausblick in kommende Themen sein.

Wie immer wäre es klasse, wenn ihr auch über eure Erfahrungen und / oder Tipps beim Auslesen von Daten eines Apple TV* teilen würdet. 

CyberDeals

* verlinkte Produkte / Links zu Amazon / Cyberport sind Affiliate-Links

12 Kommentare

  • Markus23

    Hi,

    bin gerade über deine 2 Blogposts mit Node-Red und AWtrix und Apple gestolpert.
    Bin gerade auch mich da am reinarbeiten, ist aber alles noch ziemlich neu und frisch ( node-red und awtrix – erst am diese Woche aktiv genommen.
    Mal zu deinen Ausführungen hier: Sehe ich das richtig, dass es nicht reicht nur deinen flow zu importieren, die beiden fehlenden Nodes habe ich noch installiert und aus deinem Flow erstmal die Dashboard Sachen rausgeschmissen. Dann die ATV IP und die AWtrix IP angepasst. Ich sehe aber im Debug Fenster jetzt nur alle 10 Sekunden ein „Restart atvscript“. ich vermutet da fehlt noch was ( was ist z.B. mit dem pyatv ? hab ich bewusst nix installiert. Wenn da was fehlt, wie integriere ich das in meiner Docker node-red ?

    Erstmal danke für deine Vorabeit und hoffentlich für weitere Infos 🙂

    • Simon

      Hallo Markus,

      danke für deinen Kommentar und dein Feedback.

      Um meine Lösung zu nutzen musst du auch den Abschnitt „Software Installation pyatv“ durchführen. Das heißt, es muss pyatv auf einen Raspberry Pi installiert werden. Über pyatv läuft das Auslesen eines Apple TV.

      Hab zudem die Datei atvscript_flow.json aktualisiert, so dass es auch ohne die separate Installation von node-red-dashboard laufen sollte. Ist damit alles aufs Wesentliche reduziert.

      Schau nochmals, ob es mit dem aktuallisierten atvscript_flow.json aus diesem Post funktioniert.

  • Markus23

    Hallo,

    Ja hate gestern zu schnell quergelesen und komplett übersehen, dass da lokal noch was installiert werden muss – nämlich pyatv ;-). Hier hab ich gleich mein nächstes Problem: das pyatv muss ja wohl lokal auf dem node-red laufen, aber so einfach bekomme ich das pyatv nicht in dem docker-container installiert (habs – Holzhammermässig – mal in der Shell des docker containers probiert (pip3 install pyatv) aber es bricht mit einigen Fehlern ab – ist halt docker-container speziell für node-red und nicht für pyatv ausgelegt.
    Nun kann ich entweder nen spezielles docker-image mit node-red und pyatv bauen, oder ne Linux-VM mit node-red und pyatv bauen , oder nen extra docker-container nur für pyatv (aber dann fehlt der direkte Zugriff von node-red). Muss mal schauen. Nen extra rpi laufen lassen dafür möchte ich nicht, lieber alles per Docker oder Vm auf meinem NAS 😉

    • Simon

      Ok hab’s alles auf einem Pi Zero W laufen.
      Im Grunde musst du dich auch, wenn nach meinem Beispiel geht, an node-red-node-daemon orientieren, da hierüber atvscript gestartet wird.

    • Simon

      Mittels node-red-contrib-bigssh kannst du auch Befehle von externen IPs absetzen. Damit sollte es auch funktioniert, wie in deinem Fall beschrieben. node-red-contrib-bigssh setze ich bei nem anderen Projekt ein, um Daten aus einem Retro Handheld auszulesen und auf AWTRIX auszugeben.

      Alternativ kann es auch mittels daemon-node ein Output via mqtt weitergereicht werden.

  • Markus23

    So,

    habe ein Docker-Image mit node-red gefunden , was auf debian basiert (für alle interessierten: dennis14e/node-red und lastest-buster auswählen). In diesem konnte ich dann python3-pip und dann pyatv nachinstallieren. Danach noch im node-red die beiden Nodes node-red-daemon und node-red-dashboard nachinstalliert und dann lief dein Flow :-). (Ok, IP-Adressen musste ich noch anpassen).
    Nachteil: das Debian Buster node-red Image braucht das 6fache an RAM (500MB zu 80MB) und bisschen mehr CPU (0.1% zu 0.08%), da ich aber auf meinem NAS noch genügent Resourcen habe, ist das zu vernachlässigen.

    Ich werde mal weiter testen und deinen Blog im Auge behalten, vielleicht gibts ja noch Updates.

    Soweit erstmal 1a 😀

    • Simon

      Hi Markus.

      Klasse, dass es läuft, freut mich. 👍🏻😎

      Mit dem aktualisieren Flow, gleicher Link, benötigst du nicht mehr node-red-dashboard, sondern nur node-red-daemon

      Bald kommt bestimmt auch meine Lösung fürs Auslesen von anderen Devices.

      Also Newsletter abonnieren 😉

  • Markus23

    Hi,

    muss nochmal was nachfragen:

    Der daemon node „AppleTv 4k“ aus deinem Flow startet ja automatisch.
    – Was hat es aber mit dem „Trigger 30s“ und „Restart“ Nodes aufsich, die sind ja so nirgends angebunden ?
    – Was macht der „Send“ Input Node mit dem „sigterm“ Node ( ich spekuliere manuelles Neustarten des Daemon Nodes) ?
    – Für was ist der „AppleTV 4K Power“ Node , ist auch nirgends angeschlossen ?

    Gruß,

    Markus

    • Simon

      Hi Markus,

      vielen Dank für deine Durchsicht des Flows. Offenbar war noch zu viel drin.
      Hab nochmals atvscript_flow.json aktualisiert und auf das Wesentliche reduziert. 😀
      Buffer sind ebenfalls entfernt.
      Denke nun sollte alles passen. 😎

      Gruß
      Simon

  • MARKUS23

    Hallo Simon,

    danke für die Überarbeitung.
    Ich habe bei mir nochmal alles neu gemacht und meine „uralten“ Docker-Skills mal aufgefrischt 😉

    Für alle die in Docker direkt mit node-red und pyatv loslegen möchten -> ich habe basierend auf dem o.g. Image von dennis14e/node-red:latest-buster ein eigenes, angepasstes Image erstellt, was schon gleich pip3 und pyatv enthält – so braucht man selbst in Docker nicht mehr zu basteln.

    Das Image findet man unter markus023/node-red-pyatv:latest.

    Für komplette Funktion unter Synology NAS muss man allerdings bischen Hand in der SSH-Konsole, weil die Node-Red Vorlage auf UID 1000 und GID 1000 setzt und man das ggf. verlinkte Datenverzeichnis manuell anpassen muss. Falls hier jemand das genauer wissen möchte, bitte nochmal nen *ping* da lassen, ich habe aber auf der Beschreibung zum Image schon etwas geschrieben.

    Gruß,

    Markus

    PS: @Simon -> kannst Du mir vielleicht nochmal die Dashboard-Settings zukommen lassen ? Würde mir das gerne mal anschauen, aber Du hast es ja entfernt (weil man es ja normalweise nicht braucht).

    • Simon

      Hi Markus,

      hab das Dashboard auch aufs Wesentliche reduziert und im Beitrag hinterlegt als „atvscript_flow_dashboard.json“ 🤗

      Freu mich auf weiteren Austausch – auch mit Blick auf deine Dockerlösung, welche ich gerne hier als Download bereitstelllen kann.
      Folg mir doch gerne auch auf Instagram / Twitter – findest du ganz oben auf der Seite.

      Gruß
      Simon

  • Markus23

    Hallo Simon,

    Danke für Dashboard und Hosting-Angebot.
    Das Docker-Image findet man aber – ganz komfortabel – bei hub.docker.com , dort einfach suchen nach o.g. Namen. Ebenso findet man das komfortabel in den meinsten Docker-Apps (u.a. auch bei Synology), dort auch einfach nach ‚markus023/node-red-pyatv‘ suchen und dann runterladen , als Container starten und gut.

    Ich schau mal bei Twitter , meld ich da ggf. gleich mal bei Dir.

    Gruß,

    Markus

Schreibe einen Kommentar zu MARKUS23 Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Translate »
GDPR Cookie Consent mit Real Cookie Banner