npm.io
0.3.0 • Published yesterdayCLI

bare-appletv-remote

Licence
MIT
Version
0.3.0
Deps
12
Size
77 kB
Vulns
0
Weekly
0

bare-appletv-remote

Apple TV remote control library for the Bare runtime

Controls Apple TV via the Companion Link protocol — the same protocol used by the iOS Remote app. Handles discovery, pairing, session encryption, and remote commands including navigation, playback, touch gestures, and power.

Install

npm install bare-appletv-remote

Usage

As a library
const AppleTVRemote = require('bare-appletv-remote')

// First run: scans the network, pairs (showing a PIN on the TV screen),
// and saves credentials to ~/.appletv-credentials.json automatically.
// Subsequent runs: loads credentials from disk silently.
const remote = new AppleTVRemote({
  onpin: async () => {
    // called only when pairing is needed — return the PIN shown on screen
    return myPromptFunction('Enter the PIN shown on the Apple TV: ')
  }
})

await remote.ready()

// Navigation
await remote.up()
await remote.down()
await remote.left()
await remote.right()
await remote.click() // select focused item
await remote.menu() // menu / back button
await remote.back() // alias for menu()

// Playback
await remote.playPause()

// Volume
await remote.volumeUp()
await remote.volumeDown()

// Power
await remote.sleep()
await remote.wake() // Wake-on-LAN (requires Ethernet on the Apple TV)

// Touch gestures — coordinates are on a 0–1000 × 0–1000 surface.
// Swipes travel edge to edge by default (distance 1000, centered);
// short swipes may not register as directional on tvOS.
await remote.swipe('right') // fast-forward
await remote.swipe('left') // rewind
await remote.swipe('right', { distance: 500, steps: 20 })

// Low-level touch — useful for custom UI or continuous scrubbing
await remote.touchBegin(500, 500)
await remote.touchMove(700, 500)
await remote.touchEnd(700, 500)

await remote.close()

ready() loads (or obtains via pairing) the credentials; the encrypted session itself opens lazily on the first command and is then kept alive — the library answers the Apple TV's heartbeats and sends a keepalive every 25 s (the ATV drops idle Companion sockets after ~30 s). If the connection drops anyway (e.g. the ATV sleeps), it is re-established transparently on the next command, retrying up to three times with re-discovery in between.

If the Apple TV definitively rejects the stored credentials, the failing command throws an error with code: 'EREVOKED'. Re-pairing is never automatic — it needs a PIN from the TV screen — so handle it explicitly:

try {
  await remote.playPause()
} catch (err) {
  if (err.code === 'EREVOKED') {
    await remote.repair() // prompts via onpin; reuses our identity so the ATV replaces the old pairing
    await remote.playPause()
  } else {
    throw err
  }
}
Options
Option Type Default Description
onpin () => Promise<string> | string Called when pairing is needed. Return the PIN shown on screen. Required for first-time pairing.
credentials Credentials Pass credentials directly, bypassing disk.
credentialsFile string ~/.appletv-credentials.json Override the credentials file path.
host string Skip mDNS discovery and connect directly to this IP address.
port number Port to use when host is set.
idleTimeout number 0 Milliseconds of inactivity before the session is closed. Default keeps it open indefinitely.
debug boolean false Log protocol traffic.
Events
remote.on('paired', () => console.log('Paired and saved.'))
remote.on('ready', () => console.log('Ready.'))
remote.on('close', () => console.log('Closed.'))
CLI
npm install -g bare-appletv-remote
# First run: scans, pairs, saves credentials
appletv pair

# Navigation
appletv up | down | left | right | click

# Playback & volume
appletv play
appletv volup | voldown

# Menu
appletv back

# Power
appletv sleep
appletv wake
Pairing troubleshooting

When pairing is initiated the Apple TV should display a PIN automatically. If nothing appears on screen:

  1. Wake the TV first. The PIN dialog only appears when the TV is active. Press a button on the physical Siri Remote to wake it, then run pair again.

  2. The PIN appears as an overlay anywhere. It will appear over full-screen apps, the home screen, or even during video playback — you do not need to navigate to a specific screen first.

  3. Enable remote access. On the Apple TV go to Settings → AirPlay & HomeKit and make sure Allow Access is not set to No One.

  4. Remove a stale pairing. If the TV silently rejects the request (no PIN shown, but the handshake completes), a leftover pairing entry may be blocking it. On the Apple TV go to Settings → AirPlay & HomeKit → Remote App and Devices (or Settings → Remotes and Devices → Remote App and Devices) and remove any existing entry for this device, then pair again.

  5. Delete local credentials and retry. If ~/.appletv-credentials.json exists from a previous attempt that did not complete, delete it before running pair again.

Advanced: scan and pair manually
const AppleTVRemote = require('bare-appletv-remote')

// Scan the network for Apple TVs
const devices = await AppleTVRemote.scan()
console.log(devices[0].name, devices[0].address)

// Pair with a specific device
const credentials = await AppleTVRemote.pair(devices[0], async () => getPin())

// Use the credentials
const remote = new AppleTVRemote({ credentials })
await remote.up()
await remote.close()

Protocol

Uses the Companion Link protocol (_companion-link._tcp mDNS service) with HAP (HomeKit Accessory Protocol) security:

  • Pairing: SRP-6a (3072-bit) + Ed25519 long-term keys + ChaCha20-Poly1305
  • Sessions: X25519 ephemeral DH + HKDF-SHA512 session keys
  • Commands: Encrypted OPACK messages with HID event payloads
  • Touch: _hidT events (press/move/release phases) on a 1000×1000 virtual touchpad surface
  • Wake: UDP Wake-on-LAN magic packet (MAC from mDNS rpAD TXT record)

Crypto via sodium-universal.

Acknowledgements

License

MIT

Keywords