Skip to content

Companion features from pyatv (app launch/list, power, Control Center) + opack negative-integer fix - #1

Open
camschroedes wants to merge 2 commits into
bsharper:mainfrom
camschroedes:companion-features
Open

Companion features from pyatv (app launch/list, power, Control Center) + opack negative-integer fix#1
camschroedes wants to merge 2 commits into
bsharper:mainfrom
camschroedes:companion-features

Conversation

@camschroedes

Copy link
Copy Markdown

Hi Brian — thanks for atvjs, it powers a Raycast Apple TV extension I've been building (camschroedes/raycast-apple-tv-remote). Along the way I ported a few Companion capabilities from pyatv on top of your public sendCommand, and hit one real bug — contributing both back.

Bug fix: pack() crashes on negative integers

packInteger's final branch calls writeBigUInt64LE, which throws ERR_OUT_OF_RANGE for any negative value:

pack({ _skpS: -10 }) // RangeError: must be >= 0n

This bites anyone using MediaControl skip (_mcc: 7, _skpS: -10 for skip-backward). Negative integers now encode as float64 (0x36), which devices accept anywhere a number is expected — pyatv sends these fields as floats as well.

Features (ported from pyatv's Companion implementation)

  • launchApp(conn, bundleIdOrUrl)_launchApp with _bundleID, or _urlS for URLs/schemes (tvOS deep links)
  • listApps(conn)FetchLaunchableApplicationsEvent{ bundleId: displayName }
  • sleep(conn) / wake(conn) — HID 12/13 press
  • controlCenter(conn) — single HID PageDown (19); notably pyatv's control_center() is not a Home hold

All verified against an Apple TV 4K (tvOS 18) over a paired Companion session: bundle-id and URL launches, app listing, sleep/wake, and Control Center all work; skip ±10s works with the opack fix.

npm run build (tsc) passes. Happy to split the fix into its own PR or adjust API naming to taste.

🤖 Generated with Claude Code

camschroedes and others added 2 commits June 6, 2026 15:07
packInteger's final branch uses writeBigUInt64LE, which throws
ERR_OUT_OF_RANGE for any negative value (e.g. MediaControl skips:
sendCommand('_mcc', { _mcc: 7, _skpS: -10 })). Negative integers now
encode as float64 (0x36), which devices accept anywhere a number is
expected — pyatv sends these fields as floats too.

Co-Authored-By: Cameron Schroeder <64573040+camschroedes@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ports four Companion capabilities from pyatv's reference implementation
onto the existing public sendCommand path:

- launchApp(conn, bundleIdOrUrl): _launchApp with _bundleID, or _urlS
  for URLs/schemes (tvOS deep links)
- listApps(conn): FetchLaunchableApplicationsEvent → { bundleId: name }
- sleep(conn) / wake(conn): HID 12/13 button press
- controlCenter(conn): single HID PageDown (19) — matching pyatv's
  control_center(), which notably is not a Home hold

Verified against an Apple TV 4K (tvOS 18): launch by bundle id and URL,
app listing, sleep/wake all work over an existing paired session.

Co-Authored-By: Cameron Schroeder <64573040+camschroedes@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant