My Home Assistant install spans multiple VLANs (one per physical building). Home Assistant itself has an IP in each of the subnets and for the most part everything works exactly how you’d hope it would for device discovery and network device integrations. Unfortunately, Apple TV support loses its mind in this environment, a fundamental limitation of both the pyatv
library and Apple’s protocols themselves. I’ve never found a way to get the native appletv media player integration to actually work.
I finally found a good workaround via MQTT. There’s still room for improvement, but it’s working well enough for me that I thought there might be some value to posting the configuration in case anyone else is facing a similar challenge. I don’t expect either Apple or the pyatv maintainers to prioritize this issue.
I found pyatv-mqtt-bridge on GitHub recently. Coupled with MQTT Sensor and MQTT Binary Sensor I can at least get a device and some useful entities that track what’s going on with the Apple TV.
You’ll need a way to run the pyatv-mqtt-bridge service (node npm or there’s a docker container) in the same subnet where the Apple TV lives. I spun up a VM and I’m running the bridge in docker inside the VM which is in the correct VLAN. My firewall allows this VM to talk to my MQTT server (Mosquitto Broker add-on in my case).
I used the pyatv atvremote wizard
which pairs with the Apple TV and then gives you the protocol credentials it negotiated with the device.
You’ll need those AirPlay credentials and whichever Identifier
matches the format of the one in the config file. Also make note of Apple TV’s MAC address as reported by this tool, you’ll need it later.
Username and password are embedded into the broker
URL and are the credentials needed to connect to your MQTT server. If you’re using the Mosquitto broker add-on in Home Assistant, then this will be any person or user you’ve added to your Home Assistant server in Settings>People.
My config.json looks like this:
{
"broker": "mqtts://username:[email protected]:8883",
"devices": [
{
"name": "Theater AppleTV",
"topic": "house/theater/appletv",
"host": "10.0.0.42",
"id": "123A456B-7CD8-9E97-FAB8-9012C345DE6F",
"airplayCredentials": "giant:string:of:linenoise"
}
]
}
Unless you run it with --debug
, pyatv-mqtt-bridge gives no indication or logging that it’s working. If it doesn’t crash/exit immediately, then it’s probably connected to the Apple TV and sending MQTT messages to the configured topic.
A tool like MQTT Explorer can be really helpful for debugging at this point. You can use it to connect to your MQTT server and see what messages show up on a topic.
Next we need to do something with those messages on the Home Assistant side.
This is what I added to my configuration.yaml
:
mqtt:
binary_sensor:
- name: "Power State"
state_topic: house/theater/appletv/powerState
object_id: theater_appletv_powerstate
unique_id: theater_appletv_powerstate
payload_off: "off"
payload_on: "on"
device:
name: "Theater Apple TV"
manufacturer: "Apple"
model: "Apple TV 4K"
model_id: "A2843"
identifiers:
- "theater_appletv"
connections:
- ["mac", "1c:b3:c9:22:be:43"]
sensor:
- name: "Device State"
state_topic: house/theater/appletv/deviceState
object_id: theater_appletv_devicestate
unique_id: theater_appletv_devicestate
device:
name: "Theater AppleTV"
identifiers:
- "theater_appletv"
Some of those items are optional and some are purely cosmetic, but I figure we’re in this deep already, why not?
Also, there are many more topics available which you can add as additional sensor:
items following the the form of the deviceState
topic sensor above (powerState
deviceState
host
name
mediaType
title
app
appId
position
totalTime
shuffle
repeat
although support varies by app).
There’s enough there that I can start do so some fun automations, like turning the lights off when mediaState
changes to playing
and back on when it changes to paused
or idle
.
It’s nowhere near as nice as a proper media player device, but I’ll take it. Hope this is helpful to someone. I’ll keep up the thread as I continue to refine my setup here.