-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Add apple-tv-remote extension #28594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e5d0d59
47ae04a
b2aec0c
d487eec
4349562
993c0e7
ad5eb7f
366593b
d76b20b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Dependencies | ||
| node_modules/ | ||
|
|
||
| # Raycast | ||
| raycast-env.d.ts | ||
| .raycast-swift-build | ||
| .swiftpm | ||
| compiled_raycast_swift | ||
| compiled_raycast_rust | ||
|
|
||
| # Build | ||
| dist/ | ||
|
|
||
| # Misc | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "printWidth": 120, | ||
| "singleQuote": false | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Apple TV Remote Changelog | ||
|
|
||
| ## [Apple TV Remote] - 2026-07-01 | ||
|
|
||
| - Visual Apple TV remote with a persistent connection — clickable button grid plus a no-modifier keyboard layer (WASD/HJKL, F select, Space play/pause) and ⌥-shortcuts | ||
| - Bonjour discovery + on-screen PIN pairing, with manual IP/port fallback | ||
| - Full Siri Remote function set ported from pyatv: context menu (hold-select), app switcher, Control Center, ±10s skip, sleep/wake, screensaver | ||
| - App launcher grid backed by the device's live app list, with hotkey-able Quicklinks per app | ||
| - Ask Apple TV: one-shot natural-language commands (pause, open an app, type text, play a show) | ||
| - Title playback via real JustWatch-resolved deep links where tvOS supports them, with an Apple TV universal-search typing flow for Netflix and unresolved titles | ||
| - Menu bar quick controls and per-key commands (disabled by default) for global hotkeys | ||
| - AI tools: press remote keys, launch apps, play content, sleep/wake — usable from Raycast AI chat and Quick AI |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) 2026 Cameron Schroeder | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| Apple TV Remote for Raycast | ||
| Copyright (c) 2026 Cameron Schroeder — MIT License (see LICENSE) | ||
|
|
||
| This project builds on the following third-party work: | ||
|
|
||
| 1. pyatv — https://github.com/postlund/pyatv | ||
| Copyright (c) 2020 Pierre Ståhl — MIT License | ||
| The Companion-protocol payloads implemented in src/lib/companion-extras.ts | ||
| (app launching, app listing, power, Control Center, hold gestures, skip) | ||
| are ported from pyatv's reference implementation and protocol | ||
| documentation (https://pyatv.dev/documentation/protocols/). | ||
|
|
||
| 2. @bharper/atv-js — https://github.com/bsharper/atvjs | ||
| Copyright (c) Brian Harper; carries the MIT license of pyatv | ||
| (Copyright (c) 2020 Pierre Ståhl), of which it is a derivative | ||
| TypeScript port. Used as the Companion-protocol transport | ||
| (discovery, pairing, encrypted session, remote keys, text input). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Apple TV Remote | ||
|
|
||
| Control your Apple TV from Raycast. No Python, no helper apps, nothing to install beyond the extension. It talks to the Apple TV directly over Apple's Companion protocol in pure TypeScript. | ||
|
|
||
|  | ||
|
|
||
| ## Features | ||
|
|
||
| - **Apple TV Remote.** A clickable on-screen remote that keeps one live connection open, so every press lands instantly. D-pad, select, back, home, hold-select context menu, app switcher, Control Center, playback, 10-second skip, and typing into TV search fields. | ||
| - **Launch Apple TV App.** A grid of the apps on your Apple TV. Open any of them, or save one as a hotkey-able Quicklink. | ||
| - **Ask Apple TV.** One-off natural language, like `pause`, `open netflix`, `play severance`, `type stranger things`, or `sleep`. | ||
| - **Menu bar.** Quick taps for play/pause, skip, navigation, opening apps, and sleep/wake, plus one click through to the full remote. | ||
| - **Per-key hotkey commands.** Every remote function is also its own command (most are off by default). Enable the ones you want and give them global hotkeys. | ||
| - **AI tools.** Drive it from Raycast AI, like `@apple-tv-remote pause` or `@apple-tv-remote play Rick and Morty on Netflix` (needs Raycast Pro). | ||
|
|
||
| | Every function, one search away | Remote in your menu bar | | ||
| | ------------------------------- | ------------------------------ | | ||
| |  |  | | ||
|
|
||
| ## Setup | ||
|
|
||
| 1. Run **Set up Apple TV**. | ||
| 2. Pick your Apple TV from the list, or add it by IP and port if discovery is blocked on your network. | ||
| 3. Type the 4-digit PIN that appears on the TV. | ||
|
|
||
| Pairing is interactive because the Apple TV shows the PIN on screen, which is why setup is a command rather than an extension preference. | ||
|
|
||
| ## The remote | ||
|
|
||
| Open **Apple TV Remote** and either click the buttons or use the keyboard. | ||
|
|
||
| | Control | Bare key | With ⌥ | | ||
| | -------------------------- | ------------------------------------ | ------------------- | | ||
| | Navigate | `W` `A` `S` `D` (or `H` `J` `K` `L`) | `⌥↑` `⌥↓` `⌥←` `⌥→` | | ||
| | Select | `F` (or `G`) | `⌥↩` | | ||
| | Back | `B` | `⌥⌫` | | ||
| | Home | `Q` | | | ||
| | Play/Pause | `Space` | `⌥P` | | ||
| | Skip 10s | `,` / `.` | | | ||
| | Previous / Next | `[` / `]` | | | ||
| | Context menu (hold select) | `V` | | | ||
| | App switcher | `X` | | | ||
| | Control Center | `C` | | | ||
| | Type text on TV | `T` | | | ||
| | Screensaver | | `⌥S` | | ||
|
|
||
| The bare keys work because the view treats your typing as button presses, so no modifiers are needed. Volume has optional hotkey commands (`Volume Up` and `Volume Down`), but they only do anything when the Apple TV controls volume over HDMI-CEC. Most TVs handle volume themselves. | ||
|
|
||
| ## Playing a show | ||
|
|
||
| `play <title> [on <app>]` looks the title up on JustWatch and opens a real provider deep link where tvOS supports one (Apple TV+, Disney+, Max, YouTube, and others). It does not guess at app IDs. Netflix dropped tvOS deep links in late 2025, so for Netflix and for anything that does not resolve, the extension opens the Apple TV's universal Search and types the title for you, then you pick the result on screen. The **Search Automation** preference sets how far it goes. | ||
|
|
||
| ## Preferences | ||
|
|
||
| - **Streaming Country.** Two-letter code for the where-to-watch lookup. Leave it blank to detect your Mac's region. | ||
| - **Search Automation.** What happens after a title is typed into Apple TV Search: stop there, open the top result (the default), or also press Play. Pressing Play can start the wrong app, since the title page usually lists several. | ||
| - **Connection Timeout.** How long a command waits to reach the Apple TV. | ||
|
|
||
| ## How it works | ||
|
|
||
| The extension speaks Apple's Companion protocol, the same one the iOS Remote uses, straight from Raycast's Node runtime through [`@bharper/atv-js`](https://github.com/bsharper/atvjs), a pure-TypeScript port of [pyatv](https://pyatv.dev). Discovery is Bonjour, pairing is HAP SRP with the on-screen PIN, and the session is chacha20-poly1305 encrypted. App launching, app listing, sleep/wake, Control Center, hold-select, and 10-second skip are built on top of the library in this extension, ported from pyatv's reference implementation. | ||
|
|
||
| ### Credential storage | ||
|
|
||
| Pairing produces machine-generated key material rather than a password you type, so it lives in Raycast's encrypted [LocalStorage](https://developers.raycast.com/api-reference/storage), scoped to this extension. The extension never touches the macOS Keychain, and it sends nothing off your local network. The only internet request is the optional JustWatch title lookup. | ||
|
|
||
| ## Development | ||
|
|
||
| ```bash | ||
| npm install | ||
| npm run dev # ray develop, hot-reloads into Raycast | ||
| npm run lint # ray lint | ||
| npm run build # ray build | ||
| ``` | ||
|
|
||
| | Module | Role | | ||
| | -------------------------------------- | ---------------------------------------------------------------------------------------------- | | ||
| | `src/lib/connection.ts` | Persistent session for the remote view, plus per-command `withConnection()` | | ||
| | `src/lib/companion-extras.ts` | Companion payloads ported from pyatv (launch app, app list, power, Control Center, hold, skip) | | ||
| | `src/lib/credentials.ts`, `devices.ts` | Pairing credentials and selected device in LocalStorage | | ||
| | `src/lib/justwatch.ts` | Title to deep-link resolution (keyless GraphQL, cached) | | ||
| | `src/lib/play-flow.ts` | Deep link, then universal-search typing, then app launch | | ||
| | `src/lib/deep-links.ts` | Curated app and bundle map, plus installed-app cache | | ||
| | `src/lib/errors.ts` | Typed errors turned into actionable toasts | | ||
|
|
||
| ## Credits | ||
|
|
||
| - [pyatv](https://pyatv.dev) by Pierre Ståhl (MIT). The reference implementation and protocol docs this extension is built on. The Companion payloads for app launching, app listing, power, and gestures are ported from it. | ||
| - [`@bharper/atv-js`](https://github.com/bsharper/atvjs) by Brian Harper (MIT). The TypeScript Companion-protocol port, itself derived from pyatv (© Pierre Ståhl). | ||
|
|
||
| See [NOTICE](NOTICE) for third-party license details. | ||
|
|
||
| ## License | ||
|
|
||
| MIT |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| const { defineConfig } = require("eslint/config"); | ||
| const raycastConfig = require("@raycast/eslint-config"); | ||
|
|
||
| module.exports = defineConfig([...raycastConfig]); |
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: check the metadata screenshot size and padding Can you make sure the metadata screenshot is the correct size and has the proper padding? Right now it looks cropped and has a white border. Please use the capture window command in Raycast. Reference:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same fix — recaptured via Window Capture at 2000×1250. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: check the metadata screenshot size and padding
Can you make sure the metadata screenshot is the correct size and has the proper padding?
Right now it looks cropped and has a white border. Please use the capture window command in Raycast.
Reference:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recaptured with Window Capture at a clean 2000×1250 — no white border, nothing cropped. The original was a manual screenshot that got letterboxed.