Lightweight HTTP service to control a Google TV / Chromecast over the Cast protocol.
Find a file
Nidahl Damen 04d0dc3fc0 Google Cast Control: public release
Lightweight HTTP service to control a Google TV / Chromecast over the
Cast protocol. Read-only status polling (never wakes the TV), explicit
cast actions only, idle watchdog releases the TV input.

Includes MIT license, whitepaper, README, and offline CI.
2026-06-14 11:51:42 -07:00
.github/workflows Google Cast Control: public release 2026-06-14 11:51:42 -07:00
tests Google Cast Control: public release 2026-06-14 11:51:42 -07:00
.gitignore Google Cast Control: public release 2026-06-14 11:51:42 -07:00
.sanity-patterns.json Google Cast Control: public release 2026-06-14 11:51:42 -07:00
cast_server.py Google Cast Control: public release 2026-06-14 11:51:42 -07:00
LICENSE Google Cast Control: public release 2026-06-14 11:51:42 -07:00
README.md Google Cast Control: public release 2026-06-14 11:51:42 -07:00
WHITEPAPER.md Google Cast Control: public release 2026-06-14 11:51:42 -07:00

Google Cast Control

A small, dependency-light HTTP service that controls your own Google TV or Chromecast over the Google Cast protocol. Point it at a device on your home network and you get a tiny JSON API to search YouTube, cast a video, scrub to an exact second, toggle play/pause, set volume, and read live now-playing state.

Unlike Fire TV (no seek API over ADB) or Apple TV on tvOS 26, Cast exposes real now-playing data (title / duration / position) and exact absolute seek, so a draggable scrub bar lands on the exact second you ask for. It is meant to sit behind a simple home control panel or any UI you want to build.

It is deliberately conservative about waking your TV: status polling is strictly read-only and never opens a Cast connection, so a UI that polls every few seconds will not hijack your TV's HDMI input. A connection is opened only on an explicit cast action. See WHITEPAPER.md for the full design.

Requirements

  • Python 3.12+
  • A Google TV or Chromecast on the same local network
  • pychromecast and yt-dlp (installed below)

Install

git clone https://github.com/nidamen/google-cast-control.git
cd google-cast-control
python3.12 -m venv .venv
./.venv/bin/pip install pychromecast yt-dlp

Run

Find your cast device's IP (in the Google Home app, or your router's client list) and pass it as CAST_DEVICE_HOST:

CAST_DEVICE_HOST=192.168.1.8 ./.venv/bin/python cast_server.py
# Cast control -> http://127.0.0.1:4530/  (device 192.168.1.8)

By default the server binds to 127.0.0.1:4530 (localhost only). Test it:

curl http://127.0.0.1:4530/api/status
curl -X POST http://127.0.0.1:4530/api/search_play -d '{"q":"lofi hip hop"}'

API

Method + path Body Purpose
GET /api/status now-playing: title, duration_ms, position_ms, player_state, supports_seek, volume. Read-only, never opens a connection.
POST /api/search_play {"q": "..."} resolve a query on YouTube (via yt-dlp) and cast the top result
POST /api/play {"video_id": "..."} cast a specific YouTube video id
POST /api/seek {"ms": 90000} seek to an exact position in milliseconds
POST /api/playpause toggle play/pause on the active session
POST /api/volume {"volume": 40} set volume (0100)
POST /api/stop stop playback and tear down the session

search_play also accepts the query as a ?q= URL parameter.

Configuration

All configuration is via environment variables:

Variable Default Meaning
CAST_DEVICE_HOST 192.168.1.8 IP address of your Google TV / Chromecast
CAST_DEVICE_NAME (empty) optional friendly-name match (alternative to IP)
CAST_PORT 4530 port the HTTP service listens on
CAST_BIND 127.0.0.1 bind address (use 0.0.0.0 to expose on the LAN)
CAST_IDLE_DISCONNECT_S 90 seconds an idle session may sit before the watchdog disconnects it to release the TV input

Notes

  • Register exactly one YouTubeController per connection; a second registration breaks the media-session binding so seek silently no-ops.
  • casttube's YouTube lounge/screen_id pairing can transiently 400 ("Parameter missing") under rapid re-casts (YouTube anti-abuse); the service retries, and it recovers after a short cooldown.
  • This service is unauthenticated. Keep the default 127.0.0.1 bind, or put it behind your own auth/proxy before exposing it on a network.

License

MIT. See LICENSE.