Library / SDK
postlund/pyatv avatar
postlund/pyatv

postlund/pyatv: an asyncio client library for Apple TV and AirPlay devices

A client library for Apple TV and AirPlay devices

1,195 stars159 forksPythonMIT

At a glance

What is it?
pyatv is an MIT licensed Python library that speaks Apple's device protocols directly, covering remote control, metadata with push updates, AirPlay streaming, app and user account management, audio output devices, keyboard input and stored credentials. It ships four command line tools, a container image for x86_64, aarch64 and armv7, and a set of example scripts that double as its documentation.
Who is it for?
Adopt pyatv if you are building home automation around an Apple TV, a HomePod or an AirPort Express and you want a typed asyncio library with a maintained protocol implementation rather than a screen scraping workaround, since the pairing flow, the metadata push updates and the four command line tools are all maintained here.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 70 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A library that speaks the protocols directly

pyatv is an asyncio Python library for interacting with Apple TV and AirPlay devices. It mainly targets Apple TVs, all generations, including tvOS 15 and later, but also supports audio streaming via AirPlay to receivers such as the HomePod, the AirPort Express and third party speakers, and it can act as a remote control for the Music app or iTunes in macOS.

The feature list is what tells you the scope. Remote control commands, metadata retrieval with push updates, streaming files via AirPlay, listing and launching installed apps, listing and switching user accounts, adding, removing or setting audio output devices such as HomePods, keyboard support, and persistent storage of credentials and settings.

Three of those are worth pausing on. Metadata retrieval with push updates means the library is not polling the device for what is playing, it is subscribing to changes, which is the difference between a script that works and one that fights the device. Audio output device management means the library can turn a HomePod into the speaker for the Apple TV, which is the kind of household automation that is otherwise a matter of physically moving a device. And persistent storage of credentials and settings is what makes the pairing work usable, because the alternative is re-pairing on every run.

The keywords in the package metadata give the protocol families away, with `raop`, `companion`, `dmap` and `dacp` listed alongside the obvious ones, which is a reminder that this is a library sitting on top of several different Apple protocols rather than one API.

The wizard is the onboarding experience

Installation is a pip install, and the setup is a wizard, and the transcript of that wizard is the most instructive thing in the README.

raw
$ pip install pyatv
$ atvremote wizard
Looking for devices...
Found the following devices:
    Name                      Model                    Address
--  ------------------------  -----------------------  -----------
  1  Receiver+                 airupnp                  10.0.10.200
  2  Receiver                  RX-V773                  10.0.10.82
  3  Pierre's AirPort Express  AirPort Express (gen 2)  10.0.10.168
  4  FakeATV                   Unknown                  10.0.10.254
  5  Vardagsrum                Apple TV 4K              10.0.10.81
  6  Apple TV                  Apple TV 3               10.0.10.83
Enter index of device to set up (q to quit): 4
Starting to set up FakeATV
Starting to pair Protocol.MRP
Enter PIN on screen: 1111
Successfully paired Protocol.MRP, moving on...
Pairing finished, trying to connect and get some metadata...

The wizard discovers devices on the network and lists them with a name, a model and an address, which means the tool identifies what it found rather than assuming a device type, and you choose which one to set up. The pairing step then names the protocol it is using, Protocol.MRP, and asks for the PIN shown on screen, which is the standard Apple TV pairing flow.

The last lines of the transcript show the payoff, because the wizard does not stop at pairing. It connects and fetches metadata, so you see immediately whether the pairing actually worked and what the device is playing. The sample device happens to be a fake Apple TV, and the track is a joke, but the point is that the wizard ends with a working, verifiable state.

Everyday commands and four entry points

Once a device is set up, the same tool works against it directly, either by address or by the name you gave it in the wizard.

raw
$ atvremote -s 10.0.10.254 playing
  Media type: Music
Device state: Playing
       Title: Never Gonna Give You Up
      Artist: Rick Astley
    Position: 1/213s (0.0%)
      Repeat: Off
     Shuffle: Off
$ atvremote -s 10.0.10.254 pause
$ atvremote -n FakeATV play

The `-s` flag selects by address and `-n` by name, and the output is a plain, readable state dump rather than JSON, which is what you want from a command you run by hand.

The package metadata declares four console scripts, and the names describe the roles. `atvremote` is the one above, the general purpose remote control. `atvproxy` exposes a device's interface locally, which is the piece that lets another program or a home automation system talk to the Apple TV as if it were directly reachable. `atvscript` runs a script against a device, which is the scripting entry point. And `atvlog` is the diagnostic tool, which is the one you want when a protocol implementation has to be matched against what a device is actually doing.

Having a dedicated logging tool in the same package as the client is a good sign. Libraries like this one break because a vendor changes a protocol, and a tool that can show you the raw conversation is the difference between a bug report and a guess.

The container image, and a deliberately loose dependency policy

The library runs in a container, and the image is published for three architectures.

raw
docker run -it --rm --network=host ghcr.io/postlund/pyatv:0.14.0 atvremote scan

The `--network=host` is required, because the discovery and pairing flows work over the local network and a container on its own bridge would not see the devices. The tag policy is stated too, with `master` pointing at the latest commit on the master branch and `latest` pointing at the latest release, which is a useful distinction for a project that ships a library and a container together.

The Dockerfile itself is worth a look because of what it removes. It starts from `python:3.14.6-alpine`, installs a full build toolchain including `gcc musl-dev build-base linux-headers libffi-dev rust cargo openssl-dev git` and `setuptools-rust`, installs the requirements and the built wheel, and then deletes every one of those build packages, the wheel and the requirements file, and clears the root and cargo caches. The final image carries the runtime and nothing else, which is a meaningful reduction for an image that people will run on a home server.

The dependency policy is equally deliberate. The `setup.py` reads `base_versions.txt` and turns the pinned `==` constraints into `>=` ones before handing them to setuptools, with a comment describing the result as loose version restrictions. So the project tests against pinned versions but installs with a floor, which keeps a library from dragging in a conflicting major version of a dependency in someone else's application.

The examples directory is the real tutorial

The repository carries a `examples/` directory with nine scripts, and the file names are a table of contents for the library's capabilities. There is `pairing.py`, `scan_and_connect.py`, `manual_connect.py`, `auto_connect.py` and `connect_with_credentials.py`, which covers the four ways to get from nothing to a connected device. There is `play_url.py` and `stream.py` for the AirPlay side. There is `storage.py` for the credential persistence. And there is `tutorial.py`.

The distinction between the connection examples is the interesting part. A manual connection means you point the library at a device and pair it by hand, an auto connection means the library finds the device and works out what it can do, and a connection with credentials means the stored pairing is reused. Those are three different code paths in a protocol library, and having a runnable example of each is worth more than a paragraph of prose.

The project is also packaged for a typed world. The classifiers include `Typing :: Typed` and versions 3.9 through 3.14, the development status is Beta, and the tooling is Black with isort configured to the Black profile, with `pyatv`, `tests` and `scripts` declared as first party and the protobuf directory in the protocol implementation skipped by the import sorter. There is a `pylintrc`, a `.codecov.yml` and a `.coveragerc`, a `chickn.yaml` for the test task runner, a `base_versions.txt`, and a Gitpod configuration so a new contributor can press one button and get a working environment.

Where pyatv is the wrong choice

The first wrong case is a production integration that needs a support contract. The library is classified as Beta, it implements vendor protocols that are not a published public API, and a device firmware update can change the behaviour underneath it. If a customer's home automation depends on this working, someone has to own the risk, and a community library does not provide that.

The second is a device or a use case that is not in the supported list. The README targets Apple TVs of all generations including tvOS 15 and later, AirPlay receivers, and the Music app or iTunes on macOS, and it points to a supported features page on the documentation site for the complete list. A feature that is not listed there is a feature you will be discovering by experiment.

The third is a household that has moved on. The pairing flow and the metadata push updates depend on protocols Apple implements, and the direction of travel for a vendor's device platform is not towards third party libraries.

The fourth is a project that wants a general remote control rather than an Apple one. The whole model is Apple's device model, its protocols and its pairing, and the value is in the depth of that support rather than in the breadth of device support.

On the state of the project, the releases are frequent and interestingly named, v0.16.1 Uter in July 2025, v0.17.0 Velma in January 2026 and v0.18.0 Willie in June 2026, with the last push to the repository on 2026-08-14 and the repository not archived. The naming after people is a small signal of a project with a community behind it rather than a company behind it, which is exactly what you want in a library that has to track undocumented protocol behaviour.

Editorial conclusion

Adopt pyatv if you are building home automation around an Apple TV, a HomePod or an AirPort Express and you want a typed asyncio library with a maintained protocol implementation rather than a screen scraping workaround, since the pairing flow, the metadata push updates and the four command line tools are all maintained here. Do not adopt it if you need a supported commercial integration, because this is a community library working against undocumented device protocols and it is classified as beta. Verify first that your device generation and firmware are in the supported features list on pyatv.dev, that you can complete the MRP pairing flow with the PIN shown on screen, and that the version you install matches the docs you read, since the project names releases after people and the latest is v0.18.0 from 2026-06-19.

Frequently asked questions

What can pyatv do?

It provides remote control commands, metadata retrieval with push updates, streaming files via AirPlay, listing and launching installed apps, listing and switching user accounts, adding, removing or setting audio output devices such as HomePods, keyboard support, and persistent storage of credentials and settings.

How do I set up pyatv for a new Apple TV?

Install it with `pip install pyatv` and run `atvremote wizard`. The wizard scans the network, lists the devices it finds with their names, models and addresses, lets you pick one, and then pairs with it using Protocol.MRP and the PIN shown on the screen, finishing by connecting and fetching metadata so you can see that it worked.

Which command line tools does pyatv install?

Four console scripts, `atvremote` for the general remote control, `atvproxy` to expose a device locally, `atvscript` to run scripts against a device, and `atvlog` as the diagnostic logger for protocol troubleshooting.

How do I run pyatv in a container?

The image is published at ghcr.io/postlund/pyatv for x86_64, aarch64 and armv7, and the documented invocation is `docker run -it --rm --network=host ghcr.io/postlund/pyatv:0.14.0 atvremote scan`, where the host network is needed for local discovery and pairing.

How does pyatv handle dependency versions?

The `setup.py` reads `base_versions.txt` and replaces the pinned `==` constraints with `>=` ones before installing, so the project tests against pinned versions but installs with a floor and does not force a conflicting major version of a dependency on applications that depend on it.

Official sources

  1. License: MIT
  2. postlund/pyatv on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/postlund-pyatv.svg)](https://hysenlabs.com/projects/postlund-pyatv)

Was this useful?