A modern, event-driven Node.js module for controlling Android TV devices via ADB. Supports sending keycodes, comprehensive keyboard input (71 characters with smart shift detection), and remote control commands. Designed for compatibility with a wide range of Android TV devices, including Fire TV, Chromecast with Google TV, and more.
- 🎯 Event-driven architecture - Comprehensive event system with structured data
- ⌨️ Comprehensive keyboard input - 71 characters with smart shift detection and special symbols
- 🔄 ESM & CommonJS support - Works with both
importandrequire - 🛡️ Error resilience - Errors emit events instead of crashing your app
- 🔗 Method chaining - Fluent API for event listener management
- 📊 Structured logging - Timestamped, categorized log events with source tracking
- 🚀 Promise-based - Modern async/await support with callback compatibility
- 📸 Advanced screencap - High-performance PNG screenshots with resizing and thumbnails
- ⚡ Performance optimized - Direct PNG streaming with 20x speed improvements
- 🔄 Device management - Reboot, wake, settings configuration with event tracking
- 📱 Universal compatibility - Works with Fire TV, Chromecast, Shield, and more
This module requires the Android Debug Bridge (ADB) binary to be installed and available in your system PATH.
- Download the SDK Platform Tools for Windows.
- Extract the ZIP file.
- Add the extracted folder to your system PATH (so you can run
adbfrom any terminal). - Test by running
adb versionin Command Prompt.
- Download the SDK Platform Tools for Mac.
- Extract the ZIP file.
- Move the extracted folder to a location like
/usr/local/binor add it to your PATH. - Test by running
adb versionin Terminal.
- Download the SDK Platform Tools for Linux.
- Extract the ZIP file.
- Move the extracted folder to
/usr/local/binor add it to your PATH. - You may also install via your package manager:
- Ubuntu/Debian:
sudo apt-get install android-tools-adb - Fedora/Red Hat:
sudo dnf install android-tools - CentOS/yum:
sudo yum install android-tools
- Ubuntu/Debian:
- Test by running
adb versionin your shell.
Note: You must accept the ADB authorization prompt on your Android TV device the first time you connect.
npm install android-tv-remote```js// ESM (Node.js with type: "module" in package.json)importcreateRemotefrom"android-tv-remote";// CommonJSconstcreateRemote=require("android-tv-remote");// Create remote (async function)constremote=awaitcreateRemote({ip: "192.168.1.100"});// Use with async/await (recommended)awaitremote.press.home();awaitremote.press.up();awaitremote.press.ok();// Or chain promisescreateRemote({ip: "192.168.1.100"}).then(remote=>remote.press.home()).then(()=>remote.press.up()).then(()=>remote.press.ok());
### Event-Driven Usage (Recommended)
This module uses an **event-driven architecture** instead of console logging. All operations emit structured events that you can listen to:
```js
import createRemote from "android-tv-remote";
const remote = await createRemote({ ip: "192.168.1.100" });
// Listen for log events (info, warn, error, debug)
remote.on("log", (data) => {
console.log(`[${data.level}] ${data.source}: ${data.message}`);
});
// Listen for error events
remote.on("error", (data) => {
console.error(`ERROR from ${data.source}:`, data.error.message);
// Handle specific error types
if (data.error.message.includes("device unauthorized")) {
console.log("Please authorize ADB on your Android TV device");
}
});
// Listen for screenshot events
remote.on("screencap-complete", (data) => {
console.log(`Screenshot completed in ${data.timing.total}ms`);
});
// Use the remote
await remote.press.home();
await remote.screencap({ filepath: './screenshot.png' });
The createRemote function is async and returns a Promise:
importcreateRemotefrom"android-tv-remote";// Async initialization with awaitconstremote=awaitcreateRemote({ip: "192.168.1.100"});remote.on("log",console.log);awaitremote.press.play();// Or use .then()createRemote({ip: "192.168.1.100"}).then(remote=>{remote.on("log",console.log);returnremote.press.play();});Advanced screencap with resizing, thumbnails, and file saving:
importcreateRemotefrom"android-tv-remote";constremote=awaitcreateRemote({ip: "192.168.1.100"});// Basic screenshot (returns PNG stream)conststream=awaitremote.screencap();// Screenshot with resizingconstresizedStream=awaitremote.screencap({width: 1280,height: 720});// Save screenshot to file (non-blocking)awaitremote.screencap({filepath: './screenshot.png'});// Resized screenshot saved to fileawaitremote.screencap({width: 640,height: 360,filepath: './thumbnail.png'});// Quick thumbnail (default 240px width)constthumbStream=awaitremote.thumbnail();// Custom thumbnail dimensionsconstcustomThumb=awaitremote.thumbnail({width: 320,height: 180,filepath: './thumb.png'});// Access last screenshot dataconsole.log('Last screenshot available:',!!remote.lastScreencapData);// Listen for screenshot eventsremote.on('screencap-complete',(data)=>{console.log(`Screenshot completed in ${data.timing.total}ms`);});Comprehensive device control with event tracking:
importcreateRemotefrom"android-tv-remote";constremote=awaitcreateRemote({ip: "192.168.1.100"});// Reboot the deviceawaitremote.reboot();// Ensure device is awake and responsiveconstisAwake=awaitremote.ensureAwake();// Configure optimal settings for remote controlawaitremote.setSettings();// Set optimal settingsawaitremote.setSettings('get');// Get current settings// Wait for device to finish booting (after reboot)awaitremote.waitBootComplete(60000);// 60 second timeout// Connection managementawaitremote.connect();console.log('Connected:',remote.isConnected);awaitremote.disconnect();// Listen for device management eventsremote.on('log',(data)=>{if(data.source==='reboot'){console.log(`Reboot: ${data.message}`);}if(data.source==='ensureAwake'){console.log(`Wake: ${data.message}`);}});Comprehensive text input with individual key support and smart shift detection:
importcreateRemotefrom"android-tv-remote";constremote=awaitcreateRemote({ip: "192.168.1.100"});// Text input (recommended for typing sentences)awaitremote.keyboard.text("Hello World!");awaitremote.keyboard.text("user@example.com");// Individual character inputawaitremote.keyboard.key.h();// Types "h"awaitremote.keyboard.key.e();// Types "e" awaitremote.keyboard.key.l();// Types "l"awaitremote.keyboard.key.l();// Types "l"awaitremote.keyboard.key.o();// Types "o"// Shifted characters (only available for keys that change when shifted)awaitremote.keyboard.key.shift.h();// Types "H"awaitremote.keyboard.key.shift.one();// Types "!"awaitremote.keyboard.key.shift.semicolon();// Types ":"// Special characters and symbolsawaitremote.keyboard.key.space();// Types " "awaitremote.keyboard.key.exclamation();// Types "!"awaitremote.keyboard.key.at();// Types "@"awaitremote.keyboard.key.hash();// Types "#"awaitremote.keyboard.key.dollar();// Types "$"// Control keysawaitremote.keyboard.key.tab();// Tab characterawaitremote.keyboard.key.enter();// Enter/Returnawaitremote.keyboard.key.backspace();// Backspace// Using keycode fallback (when available for regular keys)awaitremote.keyboard.key.a.keycode();// Sends keycode instead of character// Note: Shift variants are only available for keys that actually change// when shifted (letters, numbers, and some symbols). Special characters // like @, #, !, etc. don't have shift variants since they're already // the shifted form. Shift keys only support text input, not keycodes,// since Android ADB doesn't support sending multiple keycodes simultaneously.Log Events:
{level: 'info'|'warn'|'error'|'debug',message: 'Human readable message',source: 'connect'|'disconnect'|'screencap'|'reboot'|'ensureAwake'|etc,timestamp: '2025-10-15T18:37:44.854Z',data?: any// Optional additional data}Error Events:
{error: Error,// The actual error objectsource: 'connect'|'adb'|'screencap'|'reboot'|etc,message: 'Human readable error message',timestamp: '2025-10-15T18:37:44.854Z'}Screenshot Events:
// screencap-complete event{
timestamp: '2025-10-15T18:37:44.854Z',filepath?: './screenshot.png',// If saved to fileprocessed: true,// Whether Sharp processing was usedwidth?: 1280,height?: 720,timing: {capture: 25.4,// ADB capture time in mssharpProcess?: 1.2,// Sharp processing time in mstotal: 26.6// Total operation time in ms}}connect()/disconnect()- Device connection managementpress.<key>()/press.long.<key>()- Remote control buttonskeyboard.text(text)- Text inputkeyboard.key.<key>()/keyboard.key.<key>.keycode()- Individual keys (71 available)keyboard.key.shift.<key>()- Shifted keys (47 available, text input only)inputKeycode(code)- Raw Android keycodesreboot()- Reboot the Android TV deviceensureAwake()- Ensure device is awake and responsivesetSettings(mode)- Configure optimal Android TV settingswaitBootComplete(timeout)- Wait for device boot completionscreencap(options)- Take PNG screenshots with optional resizing and file savingthumbnail(options)- Take thumbnail screenshots (default 240px width)
on(event, listener)- Add event listener (returns remote for chaining)off(event, listener)- Remove event listener (returns remote for chaining)once(event, listener)- Add one-time event listener (returns remote for chaining)emit(event, ...args)- Emit custom events
log- Emitted for all operations (info, warn, error, debug levels)error- Emitted when errors occur (structured error data)screencap-start- Emitted when screenshot capture beginsscreencap-captured- Emitted when raw screenshot is capturedscreencap-processing- Emitted when image processing beginsscreencap-ready- Emitted when processed stream is readyscreencap-saved- Emitted when screenshot is saved to filescreencap-complete- Emitted when entire screenshot operation completes
isConnected- Boolean indicating connection statusinitPromise- Promise that resolves when initialization completeslastScreencapData- Buffer/Stream containing the last captured screenshot data
See JSDoc comments in source code for full API documentation.
| Device | Supported | Native Remote Buttons |
|---|---|---|
| Fire TV Stick | Yes | Home, Back, Menu, D-Pad, Play/Pause, Volume |
| Chromecast w/ Google TV | Yes | Home, Back, Assistant, D-Pad, Volume, Input |
| Nvidia Shield | Yes | Home, Back, D-Pad, Play/Pause, Volume |
| Roku (Android TV) | Partial | Home, Back, D-Pad, Play/Pause |
| Xiaomi Mi Box | Yes | Home, Back, D-Pad, Volume |
| Sony Bravia (Android TV) | Yes | Home, Back, D-Pad, Volume, Input |
See Keyboard Keys and Keycodes for full lists.
MIT