Skip to main content

remoku

PyPI - Version PyPI - License PyPI - Python Version build

A small native desktop remote for Roku TVs and players, built directly on Roku's public External Control Protocol (ECP).

The UI is NiceGUI running in a native window (pywebview): remoku opens a window, full stop — no browser tab, no server to manage, no browser mode.

Every Roku runs a tiny REST server on TCP port 8060. This app talks to it directly — no accounts, no cloud, no companion service.

Install

uv tool install remoku

or with pipx:

pipx install remoku

That gives you the remoku command (plus a rokuremote alias) in an isolated environment. To also get a desktop entry and icons, run the installer:

curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh

It installs with uv (or pipx) under ~/.local — no root, nothing system-wide — and drops a launcher into your app menu. If PyPI is unreachable, the installer falls back to the GitHub repository.

# install somewhere else
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --prefix /opt/remoku

# a specific branch or tag (also used for the desktop entry and icons)
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --ref v2.0.0

# just the command, no desktop entry
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --no-desktop

# uninstall (settings and icon cache are kept)
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --uninstall

Features

  • Purple Roku-style remote in a native window: power, back, home, info, instant replay, D-pad with OK, playback, volume
  • App shortcuts grid, loaded from the device (/query/apps) with real icons; click a tile to launch
  • TV inputs in their own row, including custom names (e.g. "Nintendo Switch", "Soundbar") configured on the TV
  • A "Type to the Roku" box: each keystroke streams to the device, and deleting a character sends Backspace, so you can edit as you type
  • Device discovery: SSDP, plus an ARP-assisted scan of the local /24 that stays gentle on consumer routers (broad high-concurrency scans can take the whole Wi-Fi network down for ~10 seconds)
  • Wake button: Roku TVs in standby only answer device-info and reply 403 to everything else; the button sends Wake-on-LAN and waits for the TV to come up. It also works in a deeper sleep that stops answering ECP altogether, using the MAC address remembered from the last connection.
  • Typing: letters, digits and punctuation go straight to the Roku whenever its on-screen keyboard is up, with no mode to toggle
  • Keyboard control: the arrow keys behave like the D-pad, so the whole remote is usable without a mouse

Requirements

  • Python 3.10+
  • A graphical session (X11 or Wayland)

That's all: the Qt backend ships as wheels, so there is nothing to install for GTK or WebKit. On Linux the window uses Qt (PyQt6) and falls back to GTK/WebKit2 when PyGObject is available.

Usage

remoku                   # start the native window
remoku --ip 192.0.2.50
remoku --list            # print Roku devices found on the network
remoku --apps            # print apps/inputs of the last used device
remoku --key Home        # send a single keypress
rokuremote --help        # rokuremote is an alias for remoku

The last used device is remembered in ~/.config/remoku/devices.json; app icons are cached in ~/.cache/remoku/icons/.

Keyboard shortcuts

Key Action
Arrows Up, Down, Left, Right
Enter Select (OK), or press the focused on-screen button
Esc Home
Backspace Backspace (deletes while typing)
Letters / digits / punctuation / space Typed to the Roku
Alt + W / A / S / D Up / Left / Down / Right
Alt + B Back
Alt + H Home
Alt + I Info
Alt + O Select (OK)
Alt + R, F or P Rewind, Fast forward, Play/pause
Alt + , . or / Rewind, Fast forward, Play/pause
Alt + [ or - Volume down
Alt + ], + or = Volume up
Alt + \ or M Mute
Ctrl + Up / Down Volume up / down
Tab / Shift+Tab Move focus through the window

Printable keys are always forwarded to the Roku as Lit_ characters. Roku ignores those unless a text field is focused, which means typing simply works whenever the TV's on-screen keyboard is up. Roku's API has no way to ask whether a keyboard is open (verified against /query/active-app, /query/device-info and friends), which is why the remote keys that used to live on letters moved to Alt combinations. When a button in the window has focus, Enter presses that button instead of reaching the Roku.

Development

git clone https://github.com/slug-enjoyer/remoku
cd remoku

just install        # uv sync --all-extras
just run            # launch the native window from source
just lint           # ruff format + check, pyrefly
just test           # pytest with coverage (gate: 80%)
just security       # bandit
just build          # uv build
just version bump patch   # commitizen: bump, changelog, tag

Every push and pull request runs lint, the test matrix (Python 3.10–3.14), bandit and a build check; tagging v* runs the tests again and publishes to PyPI with uv (trusted publishing).

The test suite is hermetic by construction: an autouse guard raises on any non-loopback network access, and XDG paths are redirected into a temp sandbox, so tests never touch a real device, the network or your real config — tests/test_hermeticity.py proves it.

Secret scanning

A pre-commit hook blocks commits that contain secrets and warns (without blocking) when staged lines look like local/private values such as LAN addresses, MAC addresses or home directory paths.

just hooks          # enable the hook for this clone (per-clone git config)
just audit          # scan the whole repo, history included, any time

make hooks          # same thing, if you prefer make
make audit

It runs gitleaks when installed (sudo pacman -S gitleaks) and falls back to a small built-in check otherwise. A deliberate example can be marked on its line with the comment sensitive-example; git commit --no-verify bypasses the hook entirely.

Notes and troubleshooting

  • Since Roku OS 14.1, remote commands require Settings → System → Advanced system settings → Control by mobile apps → Enabled on the device. If a command is refused, the app says so in the status bar.
  • A Roku TV in standby (power-mode: Ready) answers device-info but refuses every other command with HTTP 403, which looks like the setting above being off even when it is on. In that case the app shows a Wake TV button, which sends a Wake-on-LAN magic packet and waits for the TV to come up (this is what Roku's own mobile app does). If waking does not work, enable Settings → System → Power → Fast TV start on the TV.
  • SSDP discovery is unreliable on many Wi-Fi networks because access points drop multicast between clients. When that happens the app falls back to scanning: it pokes the kernel into resolving every address in the local /24 via ARP (a single UDP datagram each), then TCP-probes only the hosts that actually exist. If the ARP table cannot be read it falls back to a low-concurrency TCP scan, and it never scans wider than a /24.
  • Connecting to a device is retried up to three times if the network is briefly busy, and the startup scan runs only after the first connection attempt finishes.
  • Power/volume buttons only appear for devices that report support for them (Roku TVs, or players using TV controls over HDMI-CEC).

License

GPL-3.0. See LICENSE.

Metadata

Release files for remoku 2.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for remoku 2.0.1
File Size Uploaded
remoku-2.0.1.tar.gz 41.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for remoku 2.0.1
File Interpreter ABI Platform
remoku-2.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 82.9 kB

Release files / remoku-2.0.1.tar.gz

Download URL remoku-2.0.1.tar.gz
Size 41.6 kB
Tags Source
SHA-256 checksum
How to use checksums
bed543ab3ca96b1b910e16cebd19177c0b7f54ff49a41e7b60e79ebebc4bc26c
BLAKE2b-256 checksum
How to use checksums
5d5fa1b2ce03fef199b5ab6a994b1ce4d13993231c2359bb9dcaf55ef45959fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / remoku-2.0.1-py3-none-any.whl

Download URL remoku-2.0.1-py3-none-any.whl
Size 41.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9e9ac5b2060c0ec49e5e9fc78bc9429c747a90af2e7230694a6b31c23523fa6a
BLAKE2b-256 checksum
How to use checksums
1a3dc9d2e241b4712790354c4d6b24d7049ca36694701ed57dc12ca8b01d6159
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

2.0.2

2 release files

This release

2.0.1 This release

2 release files

2.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page