gtv

πŸ‘‹ Introduction

Google TV control library β€” discovery, pairing, a stateful session, and a shared device store

@kud/gtv is the framework-agnostic core of the Google TV stack β€” the engine that gtv-cli, mcp-gtv, and the desktop app all build on. It handles discovery, pairing, a stateful remote session, and a shared device registry, and leaves every side effect to you.

🧩 One idea: pure core, injected I/O

@kud/gtv never reads a terminal, shows a dialog, or owns a UI. Anything that touches the outside world is something you pass in β€” which is exactly why three very different clients can share one core:

  • Pairing β€” onSecret is your callback (terminal readline, an MCP round-trip, a Tauri dialog…); the library does no I/O itself.
  • Discovery β€” discover() returns pure data from mDNS, no side effects.
  • Session β€” createSession() returns an EventEmitter that reduces the raw protocol stream into one observable SessionState.
  • Shared store β€” every consumer reads and writes the same ~/.config/gtv/config.json, so one device registry serves them all.

πŸ“¦ Install

npm install @kud/gtv

πŸš€ Quick start

Discover and pair with a TV (the PIN shows on screen):

import { discover, pair } from "@kud/gtv";

const [tv] = await discover();

await pair({
  host: tv.host,
  hostname: tv.hostname,
  port: tv.port,
  name: tv.name,
  onSecret: async () => promptUserForPin(),
});

Then drive a stateful session:

import { createSession, KEYS } from "@kud/gtv";

const session = createSession(); // uses the configured device
session.on("change", (state) => console.log(state));

session.sendKey(KEYS.home);
session.typeText("interstellar");
session.launchApp("market://launch?id=com.netflix.ninja");
session.stop();

For scripts that don't need a long-lived connection, the one-shot helpers (sendKey, launchApp, connect, withRemote) handle connect-act-disconnect for you.

On this page