- Python 100%
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. |
||
|---|---|---|
| .github/workflows | ||
| tests | ||
| .gitignore | ||
| .sanity-patterns.json | ||
| cast_server.py | ||
| LICENSE | ||
| README.md | ||
| WHITEPAPER.md | ||
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
pychromecastandyt-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 (0–100) |
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
YouTubeControllerper connection; a second registration breaks the media-session binding soseeksilently no-ops. casttube's YouTube lounge/screen_idpairing 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.1bind, or put it behind your own auth/proxy before exposing it on a network.
License
MIT. See LICENSE.