Externe Integration - Apple TV

Hallo zusammen,

Nach mehr als zehn Jahren, in denen ich diesem Projekt (Gladys) gefolgt bin, ohne wirklich Zeit zu finden, es zu installieren und zu konfigurieren, bin ich endlich mit einer ersten externen Integration zurück: Apple TV.

Sie basiert auf pyatv in Python (wie HA) und node-pyatv auf Node.js-Seite (Wrapper).

Hinweis: Die Integration wurde stark von GPT 5.6 Sol (Codex) entwickelt.

Gut gemacht bei der Integration, das interessiert mich, weil ich eine Apple TV zu Hause habe :grin: Ich werde es testen!

Ich habe es getestet und diese Fehlermeldung erhalten:

Vielleicht ist Codex weniger streng als Claude aha, aber ich denke immer daran, den Validator laufen zu lassen, bevor ich etwas veröffentliche:

Der Befehl:

npx github:GladysAssistant/integration-store .

Aie! Ich schaue mir das später etwas genauer an… das ist seltsam, denn ich habe gestern Abend eine Installation durchgeführt und es hat funktioniert :o

Der Fehler ist:

{
    "status": 422,
    "code": "UNPROCESSABLE_ENTITY",
    "properties": "actions[1].depends_on: unbekanntes Feld ; actions[2].depends_on: unbekanntes Feld"
}

Ich werde den Fehler in der Oberfläche einfügen, dann ist es einfacher zu debuggen :slight_smile:

Neues Release verfügbar, versuche es nochmal und sag mir, ob etwas korrigiert werden muss :slight_smile:

Dieses Mal kann ich installieren, aber beim Scan gibt es einen Fehler:

Ich habe Claude gefragt und es handelt sich tatsächlich um einen Bug der Integration, nicht von Gladys:

[2026-08-03T12:51:38.091Z] [INFO] [apple-tv] Start der Gladys Apple TV Integration
[2026-08-03T12:51:38.288Z] [INFO] [gladys-sdk] Verbindung zu Gladys hergestellt (http://172.30.0.1:80)
[2026-08-03T12:51:41.621Z] [INFO] [apple-tv] Start der vermittelten Apple TV-Erkennung
[2026-08-03T12:51:42.339Z] [INFO] [apple-tv] Start der vermittelten Apple TV-Erkennung
[2026-08-03T12:51:42.356Z] [ERROR] [apple-tv] Vermittelte mDNS-Erkennung fehlgeschlagen GladysApiError: Konflikt
    at HttpClient.request (/app/node_modules/@gladysassistant/integration-sdk/lib/http-client.js:49:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async AppleTvIntegration.scan (file:///app/src/integration.js:76:29)
    at async GladysIntegration._runHandler (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1166:7)
    at async GladysIntegration._handleMessage (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1022:9) {
  status: 409,
  code: 'CONFLICT'
}
[2026-08-03T12:51:50.977Z] [ERROR] [apple-tv] Apple TV-Probe fehlgeschlagen für 3 Kandidaten-Hosts Error: Keine Geräte gefunden, aber 1 Fehler erhalten: [
  {
    "result": "failure",
    "datetime": "2026-08-03T14:51:50.763359+02:00",
    "exception": "Expected 4 octets in 'fd33:5f93:3f40:49cd:3:fd3b:c82a:7ced'",
    "stacktrace": "Traceback (most recent call last):\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 413, in appstart\n    args.output(await _handle_command(args, abort_sem, storage, loop)),\n                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 285, in _handle_command\n    return await _scan_devices(loop, storage, args.scan_hosts)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 231, in _scan_devices\n    for atv in await scan(loop, hosts=hosts, storage=storage):\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/__init__.py\", line 68, in scan\n    scanner = UnicastMdnsScanner([IPv4Address(host) for host in hosts], loop)\n                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/__init__.py\", line 68, in <listcomp>\n    scanner = UnicastMdnsScanner([IPv4Address(host) for host in hosts], loop)\n                                  ^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.11/ipaddress.py\", line 1319, in __init__\n    self._ip = self._ip_int_from_string(addr_str)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.11/ipaddress.py\", line 1206, in _ip_int_from_string\n    raise AddressValueError(\"Expected 4 octets in %r\" % ip_str)\nipaddress.AddressValueError: Expected 4 octets in 'fd33:5f93:3f40:49cd:3:fd3b:c82a:7ced'\n"
  }
]
    at e.find (file:///app/node_modules/@sebbo2002/node-pyatv/dist/lib-DynU3Xla.mjs:7:8365)
    at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async AppleTvIntegration.scan (file:///app/src/integration.js:95:15)
    at async GladysIntegration._runHandler (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1166:7)
    at async GladysIntegration._handleMessage (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1022:9)
[2026-08-03T12:51:58.718Z] [INFO] [apple-tv] Start der vermittelten Apple TV-Erkennung
[2026-08-03T12:52:07.997Z] [ERROR] [apple-tv] Apple TV-Probe fehlgeschlagen für 3 Kandidaten-Hosts Error: Keine Geräte gefunden, aber 1 Fehler erhalten: [
  {
    "result": "failure",
    "datetime": "2026-08-03T14:52:07.795239+02:00",
    "exception": "Expected 4 octets in 'fd33:5f93:3f40:49cd:3:fd3b:c82a:7ced'",
    "stacktrace": "Traceback (most recent call last):\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 413, in appstart\n    args.output(await _handle_command(args, abort_sem, storage, loop)),\n                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 285, in _handle_command\n    return await _scan_devices(loop, storage, args.scan_hosts)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/scripts/atvscript.py\", line 231, in _scan_devices\n    for atv in await scan(loop, hosts=hosts, storage=storage):\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/__init__.py\", line 68, in scan\n    scanner = UnicastMdnsScanner([IPv4Address(host) for host in hosts], loop)\n                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/opt/pyatv/lib/python3.11/site-packages/pyatv/__init__.py\", line 68, in <listcomp>\n    scanner = UnicastMdnsScanner([IPv4Address(host) for host in hosts], loop)\n                                  ^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.11/ipaddress.py\", line 1319, in __init__\n    self._ip = self._ip_int_from_string(addr_str)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.11/ipaddress.py\", line 1206, in _ip_int_from_string\n    raise AddressValueError(\"Expected 4 octets in %r\" % ip_str)\nipaddress.AddressValueError: Expected 4 octets in 'fd33:5f93:3f40:49cd:3:fd3b:c82a:7ced'\n"
  }
]
    at e.find (file:///app/node_modules/@sebbo2002/node-pyatv/dist/lib-DynU3Xla.mjs:7:8365)
    at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async AppleTvIntegration.scan (file:///app/src/integration.js:95:15)
    at async GladysIntegration._runHandler (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1166:7)
    at async GladysIntegration._handleMessage (/app/node_modules/@gladysassistant/integration-sdk/lib/gladys-integration.js:1022:9)

Also wirklich :joy: ! Ich habe gerade ein neues Release veröffentlicht :+1: (läuft noch…)
Falls es immer noch nicht funktioniert, kannst du in der Zwischenzeit die IP deiner Apple TV direkt im Konfigurations-Tab eingeben.

Wir kommen voran, aber diesmal habe ich einen anderen Fehler:

Der Selektor könnte etwas eindeutiger sein ^^

Da gibt es noch Verbesserungspotenzial, Codex :joy:

Ich war vielleicht ein bisschen „knauserig“ in der verwendeten Argumentation haha
Aber es stimmt schon, er hat sich diesmal nicht gerade Mühe gegeben :joy:

Neue Version verfügbar, ich habe ihn gebeten, sich selbst zu überprüfen und andere potenzielle Probleme dieser Art zu beheben!

Zögere nicht, mir Bescheid zu sagen, wenn du weitere Probleme hast, falls nötig

Dieses Mal konnte ich die Apple TV hinzufügen :slight_smile:

Allerdings könnte der Gerätename « Apple TV » enthalten, denn momentan ist es nur der Name des Raumes (das muss der von Apple vergebene Name für meine Apple TV sein), und das ist nicht sehr lesbar:

Übrigens, ich habe versucht, die Apple TV mit dem Typ « Power » einzuschalten, aber es passiert nichts:

Die Logs zeigen nichts Besonderes:

[2026-08-03T15:22:33.784Z] [INFO] [apple-tv] Starting the Gladys Apple TV integration
[2026-08-03T15:22:33.982Z] [INFO] [gladys-sdk] connected to Gladys (http://172.30.0.1:80)
[2026-08-03T15:22:39.142Z] [INFO] [apple-tv] Starting mediated Apple TV discovery
[2026-08-03T15:22:48.502Z] [INFO] [apple-tv] Discovery completed: 1 Apple TV device(s) from 1 candidate host(s)
[2026-08-03T15:24:50.959Z] [WARN] [apple-tv] Apple TV real-time updates unavailable for Salon: Got pyatv Error: power_state is not supported; using polling
[2026-08-03T15:24:55.556Z] [WARN] [apple-tv] Apple TV real-time updates unavailable for Salon: Got pyatv Error: power_state is not supported; using polling

Funktioniert es bei dir?

Achtung, deine Integration wurde im Integrations-Store aufgelistet, obwohl sie noch nicht funktionsfähig ist. Die Idee ist, sie erst zu veröffentlichen, wenn sie betriebsbereit ist. :wink:

Bis alles funktioniert, kannst du das GitHub-Topic entfernen. So wird verhindert, dass Nutzer sie zu früh entdecken und bei ihren Tests enttäuscht sind. :slightly_smiling_face:

Oups… ich habe es entfernt! Codex hat den Tag ohne mich zu informieren hinzugefügt! Entschuldigung

Ich schaue, um es zu korrigieren, aber ja, Power funktionierte gestern Abend bei mir.

Hallo zusammen!

Also… Ich habe alles von Grund auf mit Claude Opus 5 neu geschrieben, das ist kein Patch für die alte Version, sondern eine neue Implementierung.

Was sich im Ansatz ändert

Die alte Version nutzte node-pyatv, ein Node-Wrapper, der die CLI atvremote als Unterprozess aufruft. Das funktioniert, um eine Taste zu senden, aber man verliert alles andere: Jeder Befehl startet einen Prozess, jede Apple TV-Sitzung dauert eine bis zwei Sekunden zum Öffnen, und Echtzeit-Updates gibt es nicht, sie existieren nur, solange die Verbindung gehalten wird.

Die neue Version enthält eine Python-Brücke, die direkt mit pyatv kommuniziert. Der Node-Prozess behält die Gladys-Seite (SDK, Geräte, Aktionen) und steuert einen dauerhaften Python-Worker über eine JSON-Line-Pipe. Konkret:

  • eine dauerhaft offene Apple TV-Sitzung → Die Tasten reagieren sofort, und der Zustand (Wiedergabe/Pause, was auf dem Bildschirm ist, die aktuelle App, die Lautstärke) wird per Push hochgeladen, nicht per Polling;
  • die gesamte pyatv-API ist zugänglich, nicht nur das, was die CLI anbietet: Pairing der beiden Protokolle, Erkennung der tatsächlichen Gerätefähigkeiten, Listener, Liste der installierten Anwendungen;
  • das Pairing erfolgt über den Konfigurationsbildschirm. tvOS 15+ erfordert zwei Sätze von Anmeldedaten — AirPlay und dann Companion — also zwei Codes, die auf dem Fernseher angezeigt werden. Die Integration führt beide Schritte automatisch aus, und wenn ein Code abläuft (die Verbindung, die ihn trägt, schließt sich nach einer Minute), zeigt sie einen neuen an, anstatt Sie im Stich zu lassen.

Was das in Gladys bedeutet

  • Die klickbare Fernbedienung auf einem Dashboard (PR #2765): Richtungskreuz, OK, Zurück, Start, Steuerzentrum, Wiedergabe/Pause, Transporttasten, Lautstärkeregler. Fügen Sie eine „Geräte“-Box hinzu und wählen Sie Ihre Tasten.
  • Die Musikbox zur Steuerung der Wiedergabe wie ein Sonos.
  • Textsensoren „Aktuelle Wiedergabe“ und „Anwendung“.
  • Ein Shortcut pro installierter Anwendung, damit eine Szene Netflix oder Disney+ direkt öffnet.
  • Stromversorgung, Standby und automatische Erkennung über AirPlay-Ankündigungen im Netzwerk.

Die bedingten Funktionen (Lautstärke, Stromversorgung, Anwendungen) werden erst veröffentlicht, wenn eine echte Verbindung bewiesen hat, dass das Gerät sie unterstützt, kein Lautstärkeregler, der sich nie bewegt.

Getestet auf einer Apple TV 4K (3. Generation) unter tvOS 26: Pairing, Fernbedienung, Anwendungen, Echtzeit-Zustandsrückmeldung.

Sie können es testen

Das Entwicklungsimage ist öffentlich:

ghcr.io/valentinhttr/gladys-apple-tv:dev

Ein wichtiger Punkt: Wenn Sie Gladys in Docker auf einem Mac oder unter Windows ausführen, funktioniert die automatische Erkennung nicht, da Docker Desktop, OrbStack usw. die Container in einer Linux-VM ausführen, sodass network_mode: host das Netzwerk der VM bezeichnet und das Apple TV-Multicast nie ankommt. Das Unicast funktioniert sehr gut: Geben Sie die IP-Adresse Ihrer Apple TV in „Manuelle IPv4-Adressen“ ein, und alles andere funktioniert normal. Bei einem auf Linux installierten Gladys (Raspberry Pi, NAS, Server) gibt es nichts zu tun.

Der neue Code: GitHub - valentinhttr/gladys-apple-tv: External Gladys Assistant integration for discovering, pairing and controlling Apple TV devices · GitHub

Es ist noch nicht im Store veröffentlicht, ich möchte zuerst Feedback sammeln. Zögern Sie nicht!

Danke für diese neue Version, es ist viel besser, und diesmal funktioniert es auf Anhieb. :slightly_smiling_face:

Ich habe trotzdem ein paar kleine Rückmeldungen zur Benutzerfreundlichkeit. Anfangs habe ich nicht verstanden, dass ich den Pairing-Prozess starten muss, oder dass ich die IP-Adresse meines Apple TV abrufen muss, bevor ich das tun kann.

Im Tab Konfiguration ist es möglich, einen Geräteauswahl vor dem Ausführen einer Aktion hinzuzufügen, und ich denke, das ist genau der richtige Anwendungsfall hier. Das würde den Benutzer viel besser führen.

Der Text könnte auch etwas überarbeitet werden, um den gesamten Ablauf der Konfiguration klarer zu erklären.

Außerdem ein kleiner lustiger Bug: Wenn ich die Links oder Rechts Tasten auf dem Apple TV Startbildschirm verwende, springt es bei jeder Bewegung zwei Anwendungen statt einer. :grinning_face_with_smiling_eyes:

Das erinnert mich auch daran, dass wir wirklich ein echtes Widget für diese Art von Gerät entwickeln sollten. Die Liste mit Dutzenden von Funktionen ist nicht sehr angenehm zu verwenden. :sweat_smile: Aber bis dahin ist es bereits ein riesiger Fortschritt, und vor allem ermöglicht es endlich, Szenen zu erstellen!

Jedenfalls vielen Dank für die Entwicklung, es ist eine super Ergänzung!

Vielen Dank für das detaillierte Feedback, alles ist korrigiert :wink: Zögere nicht, es noch einmal zu testen und mir deine letzten Kommentare zu geben, bevor du es im Store verfügbar machst!

Die Geräteauswahl: Ich hatte es anfangs versucht, aber der Core validierte die Aktionsfelder gegen field.options, die ein select mit source: "devices" nicht hat, daher „must be one of“ und eine leere Liste, egal was gesendet wird. Ich hatte einen Kommentar im Code hinterlassen, um zu erklären, warum ich mich auf ein Textfeld zurückgezogen hatte. Seit der Version 4.85.0 funktioniert es, daher haben die sieben Aktionen jetzt ein Dropdown-Menü und niemand muss mehr seine IP-Adresse suchen. Die Integration erfordert daher Gladys >= 4.85.0.

Die Texte: Umgeschrieben rund um den gesamten Ablauf, mit den echten UI-Button-Namen: ein Einführungsabschnitt, der Scanner → Zu Gladys hinzufügen → Paaren → Aktualisieren aufrollt, dann ein Abschnitt pro Einstellungsblock und ein letzter direkt über den Aktionsbuttons. Die Abschlussnachricht des Pairings verweist explizit auf „Aktualisieren“ statt vage zu sagen, die Aktualisierung zu akzeptieren.

Der Sprung zwischen zwei Anwendungen: Gut beobachtet, und der Übeltäter ist nicht dort, wo ich ihn erwartet hätte. Ich habe überprüft: Niemand sendet den Tastendruck zweimal, weder Gladys, noch das Frontend, noch mein Bridge, noch pyatv. Tatsächlich liest tvOS einen Richtungs-Tastendruck nicht als Ereignis, sondern als Geste, es misst das Intervall zwischen dem key-down und dem key-up HID und gibt es an einen Erkennungsalgorithmus für Tap oder Long-Press weiter (pyatv#792). In MRP durchqueren beide das AirPlay-Tunnel, und diese Latenz reicht aus, damit der Home-Bildschirm einen gehaltenen Tastendruck annimmt und automatisch wiederholt.

Die Navigations-Tasten werden jetzt über Companion gesendet, das Protokoll, das die klickbare Krone einer echten Siri Remote spricht, anstatt über MRP, das die pyatv-Fassade standardmäßig wählt. Die Medienbefehle bleiben bei MRP, das echte Wiedergabe-Semantik hat.

Ich konnte es vor der Veröffentlichung nicht an einem Fernseher testen, also wenn du bestätigen könntest, ob der Fokus jetzt gut von einer App zur nächsten wechselt, wäre das eine große Hilfe.

Das Widget: Vollkommen einverstanden, die Funktionsliste ist keine Fernbedienung. Dort liegt es am Core, und ich kann nichts von der Integration aus tun. In der Zwischenzeit können wir die App-Verknüpfungen in der Konfiguration deaktivieren, das entfernt bereits bis zu 25 Zeilen.

Alles ist auf dem Dev-Image: ghcr.io/valentinhttr/gladys-apple-tv:dev

Die Integration wird in etwa 1 Stunde im Store verfügbar sein, da sie mir recht stabil erscheint, insbesondere zum Erstellen von Szenen!

Sie ist dazu bestimmt, sich weiterzuentwickeln, zögern Sie nicht, mir hier direkt Ihr Feedback zu geben :slight_smile:

Hallo,

Danke für die Integration
Ich bin auf meiner Seite in einem separaten Netzwerk zwischen Gladys und meiner Apple TV
Ich habe daher die IP-Adresse der Apple TV angegeben und alle Ports geöffnet, die ich zwischen meinen beiden Netzwerken blockiert sah
Airplay ist aktiviert und ich habe es getestet, indem ich auf der Apple TV „Zugang für alle“ eingestellt habe
Meine Gladys-Installation hat „network mode: host“
Aber die Suche findet nichts
Ich habe auch bemerkt, dass die Integration versucht hat, einige IP-Adressen in der Nähe der IP-Adresse meiner Apple TV zu scannen

Hier sind die Protokolle:

[2026-08-16T07:52:30.184Z] [INFO] [apple-tv] Gladys hat 5 AirPlay-Ankündigungen erfasst
[2026-08-16T07:52:30.184Z] [INFO] [apple-tv] Überprüfung von 5 Kandidatenadressen mit pyatv
[2026-08-16T07:52:38.186Z] [INFO] [apple-tv] 0 Apple TV(s) gefunden
[2026-08-16T08:07:18.324Z] [INFO] [apple-tv] Scan angefordert

Eine Idee? :slight_smile:

Hallo,

Vielen Dank für deine Rückmeldung!
Laut GPT 5.6 Sol xHigh ist die Ursache:

Der Grund ist eine echte Einschränkung der Integration 1.0.0 und keine falsche Benutzerkonfiguration.
Die manuelle Adresse wird in discovery.js zu den Kandidaten hinzugefügt und dann an pyatv.scan(hosts=…) in pyatv_bridge.py weitergegeben. Diese Operation ist jedoch eine direkte mDNS-Anfrage über UDP 5353. pyatv weist explizit darauf hin, dass sie nicht zwischen Subnetzen funktioniert, da der Apple TV Anfragen von einem anderen Netzwerk ignoriert. Dies entspricht der pyatv-Dokumentation und Abschnitt 5.5 von RFC 6762.

Der network_mode: host ändert nichts an der Trennung zwischen VLANs. „Zugang für alle“ betrifft die AirPlay-Berechtigung, nicht die mDNS-Antwort. Das Öffnen von UDP 5353 reicht ebenfalls nicht aus, wenn die Anfrage eine Quelladresse beibehält, die sich im anderen Subnetz befindet.

Der Fix erfordert eine Anpassung auf Gladys-Seite, da der Container der Integration in einem Docker-Bridge-Netzwerk ist und den mDNS-Multicast nicht empfängt. Nur der Gladys-Core, der im Host-Netzwerk platziert ist, kann diese Ankündigungen erfassen.

Derzeit überträgt die vermittelte Entdeckung nur einen mDNS-Dienst, der im Manifest deklariert ist. pyatv benötigt jedoch mindestens die Ankündigungen _airplay._tcp und _companion-link._tcp, um die Identifikatoren, TXT-Eigenschaften und dynamischen Ports abzurufen. Andernfalls versucht die Integration, diese direkt vom Apple TV anzufordern, aber dieser ignoriert diese Anfrage, wenn sie von einem anderen Subnetz stammt.

@Pierre-Gilles, wäre es möglich, dass Gladys mehrere mDNS-Dienste für eine Integration deklarieren und abrufen kann?

Falls nicht, habt ihr eine andere Idee?