Library / SDK
home-assistant-libs/pychromecast avatar
home-assistant-libs/pychromecast

PyChromecast: the README names one dependency set, the package installs another

Library for Python 3 to communicate with the Google Chromecast.

2,685 stars392 forksPythonMIT

At a glance

What is it?
PyChromecast is a Python library for talking to Google Chromecast devices over the local network. Its prose and its packaging metadata disagree about what gets installed, its version number is a literal in a file, and its build badge still points at the project's former owner and a retired CI domain.
Who is it for?
PyChromecast is the right dependency when you control the network the devices sit on and you need Python to drive them: auto-discovery, the default media receiver, transport control and channel messages are all present, and the controller extension point is genuinely small. Two things deserve checking before you build on it.
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 5 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 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The dependency prose names requests, the metadata installs casttube

The Dependencies section of README.rst names three packages, requests, protobuf and zeroconf, and points readers at a requirements file for them. Neither the metadata nor that file contains requests. What pyproject.toml declares is a different third package with a floor on each of the three:

toml
dependencies = [
    "protobuf>=4.25.1",
    "zeroconf>=0.25.1",
    "casttube>=0.2.0",
]

casttube appears nowhere in the prose, and requests appears nowhere in the metadata. The zeroconf floor sits at 0.25.1 while the checked-in requirements file freezes a much later release, so a resolver is free to pick something far newer than the prose implies. Anyone who follows the install sentence in a fresh environment ends up with the packages that matter and no explanation of why requests was mentioned, which is the sort of detail that sends people looking through a changelog for a transition that never happened.

requirements.txt uses equals signs, pyproject.toml uses floors, both ship

Two dependency files travel together, and they encode different intentions.

code
casttube==0.2.1
protobuf==5.29.3
zeroconf==0.150.0

The three pins do satisfy the three floors, so a build cannot contradict itself on version numbers. What the pair disagrees about is intent. Metadata for a library on an index should accept a range; a file committed to a repository should freeze one known-good set for a test run. Here the metadata allows zeroconf 0.25.1 and the file pins 0.150.0. Because the prose sends readers to the pinned file, a user following the documentation gets whichever versions were convenient on one machine, and a user reading the metadata gets whatever the resolver picks. The build backend is bounded on both sides as well, with setuptools>=65.6,<85.0 and wheel>=0.37.1,<0.49.0, so the next setuptools major release will not be adopted without an edit here.

The version number is a literal in pyproject.toml, and master has moved past it

The string 14.0.10 sits in pyproject.toml as ordinary text, next to the distribution name and the MIT license declaration. Nothing in the build computes it from git history, so a commit made after a release keeps reporting the released number until somebody edits the file.

The release list lines up with that. 14.0.10 was published on 2026-03-07, 14.0.9 on 2025-08-20 and 14.0.7 on 2025-03-26, so seven patch numbers between 7 and 9 never got a release of their own. The default branch is master rather than main, and it received a push on 2026-09-21, about six months after the newest tag. A checkout of master today therefore carries a version string that already existed in March, plus everything that changed since. That gap is the practical argument for tracking master on purpose, or for staying on a tag, rather than installing from it and assuming the version tells you what you have.

The build badge and the controller links still point at balloob

The outbound references in README.rst predate the project's current home. The build badge at the top of the file is an image served from travis-ci.org and its click target is that same domain rather than a currently used CI service. The two controller references resolve to github.com/balloob/pychromecast paths, one for BaseController and one for MediaController, and the repository now lives under the home-assistant-libs organisation.

The packaging metadata moved with the code: the urls table in pyproject.toml points at the home-assistant-libs URL, and it is the only homepage the material records. So the project layout, the author field naming Paulus Schoutsen and the license text all reflect the current repository, while the badge and the two source links still address the old one. A reader who wants to see how a controller is implemented has to know that the link target is stale, and the badge in particular promises a build status that the file cannot deliver from where it is drawn.

Two adjacent examples read the friendly name from different attributes

The discovery walkthrough prints the same kind of value through two different objects. The single-device example selects by name and prints `cc.cast_info.friendly_name`. A few lines later, the three-device example prints `cc.device.friendly_name` for the same purpose. Both produce a list of names in the transcript, so nothing errors either way.

A caller who generalises from the first example has to determine which attribute is the stable one on a Cast object, and README.rst offers no note on the difference. The CastInfo transcript printed further down shows what the first attribute carries: the mDNS service type, the UUID, model_name, friendly_name, host, port and cast_type, with the sample device an audio cast on port 8009 at 192.168.0.189. The same passage introduces discovery_timeout as the remedy for seeing fewer devices than expected, passing 30 on a call that already carries three friendly names, with a single sentence of explanation and no stated default.

Reverse engineering a namespace means capturing a log full of cookies

Each app on the device speaks a JSON mini-protocol addressed by a URN, and the extension point is a controller that claims one. The minimal shape is short:

python
class MyController(BaseController):
    def __init__(self):
        super(MyController, self).__init__(
            "urn:x-cast:my.super.awesome.namespace")

    def receive_message(self, message, data):
        print("Wow, I received this message: {}".format(data))

        return True  # indicate you handled this message

    def request_beer(self):
        self.send_message({'request': 'beer'})

An instance is attached with `cast.register_handler(MyController())` and messages for that namespace route to it. The half of this workflow that deserves a warning is the discovery half. The instructions for exploring an unknown namespace begin by opening chrome://net-export/ and selecting Include raw bytes (will include cookies and credentials) before logging to disk. The resulting event log file therefore holds session cookies and credentials for whatever site you browsed while capturing, so it belongs outside version control and somewhere you can delete it.

Three status objects snapshot what the library knows, and nothing polls for you

The examples print all three of them. CastInfo carries the discovered service type, UUID, model_name, friendly_name, host, port, cast_type and manufacturer. CastStatus adds the active input and standby flags, volume level and mute state, app_id, the list of live namespaces, session_id and transport_id, with the sample transcript showing the Default Media Receiver under app_id CC1AD845. MediaStatus adds current_time, content_id, duration, stream_type, playback_rate, player_state and the supported_media_commands bitmask, which the transcript shows as 15.

All three are snapshots taken at the moment you ask for them. The walkthrough does not offer a polling loop, a state-change callback or a reconnect path, so deciding how often to sample and what to do when a device drops is left to the caller. The one piece of device state the walkthrough tries to explain is which input is active, stored at `cast.status.is_active_input`. That passage says the device typically reports whether it is the active input on the device it is connected to, and then stops after the first few words of a following paragraph, with no closing guidance on the rest of the CEC data.

Editorial conclusion

PyChromecast is the right dependency when you control the network the devices sit on and you need Python to drive them: auto-discovery, the default media receiver, transport control and channel messages are all present, and the controller extension point is genuinely small. Two things deserve checking before you build on it. First, the dependency prose in README.rst and the installed dependency set differ, so read pyproject.toml and pin zeroconf and protobuf yourself rather than the pip line the prose suggests. Second, 14.0.10 is a literal that master has already moved past, so decide now whether you follow master or a tag. If you need a media platform with vendor support and an SLA, this is the wrong layer: it speaks the cast protocol directly, and its release history skips patch numbers.

Frequently asked questions

What Python version does PyChromecast need?

3.11 or newer. README.rst opens with "Library for Python 3.11+ to communicate with the Google Chromecast", and pyproject.toml sets requires-python to >=3.11.0. The distribution also ships a py.typed marker and declares the Typing :: Typed classifier, so type information is packaged rather than generated.

Which packages does PyChromecast actually depend on?

protobuf at 4.25.1 or newer, zeroconf at 0.25.1 or newer, and casttube at 0.2.0 or newer, according to pyproject.toml. The dependency section of README.rst names requests, protobuf and zeroconf instead, and requests appears in neither pyproject.toml nor requirements.txt.

Is PyChromecast discontinued, and what device is meant to replace Chromecast?

Those questions are about Google's hardware, and this repository does not answer them. What it shows is library work continuing: release 14.0.10 on 2026-03-07, along with a push to the master branch on 2026-09-21. Nothing in the tree states a device roadmap or names a replacement.

How do I add support for a Cast app PyChromecast does not know yet?

Subclass BaseController, hand your namespace URN to its constructor, implement receive_message so it returns True when it has handled the message, and attach an instance with cast.register_handler(). Messages arriving on that namespace are then routed to the controller.

How do I find Chromecast devices on the network from Python?

Build a CastBrowser with a listener over a zeroconf.Zeroconf instance and call start_discovery(), or use get_listed_chromecasts() with friendly_names to select devices by name. Pass discovery_timeout when fewer devices appear than expected, and end the run with pychromecast.discovery.stop_discovery(browser).

Official sources

  1. home-assistant-libs/pychromecast on GitHub
  2. Issues
  3. License: MIT
  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/home-assistant-libs-pychromecast.svg)](https://hysenlabs.com/projects/home-assistant-libs-pychromecast)