Metadata-Version: 2.4
Name: remoku
Version: 2.0.0
Summary: A native desktop remote for Roku TVs and players, built on Roku's External Control Protocol.
Keywords: roku,remote-control,ecp,desktop,nicegui,home-automation
Author: slug-enjoyer
Author-email: slug-enjoyer <81844498+slug-enjoyer@users.noreply.github.com>
License-Expression: GPL-3.0-only
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: X11 Applications
Classifier: Intended Audience :: End Users/Desktop
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Home Automation
Classifier: Topic :: Multimedia :: Video
Classifier: Typing :: Typed
Requires-Dist: nicegui>=3.0
Requires-Dist: requests>=2.31
Requires-Dist: pywebview>=6.0 ; sys_platform != 'linux'
Requires-Dist: pywebview[qt]>=6.0 ; sys_platform == 'linux'
Requires-Python: >=3.10
Project-URL: Repository, https://github.com/slug-enjoyer/remoku
Project-URL: Issues, https://github.com/slug-enjoyer/remoku/issues
Project-URL: Changelog, https://github.com/slug-enjoyer/remoku/blob/main/CHANGELOG.md
Description-Content-Type: text/markdown

# remoku

[![PyPI - Version](https://img.shields.io/pypi/v/remoku?style=for-the-badge&color=blue)](https://pypi.org/project/remoku/)
[![PyPI - License](https://img.shields.io/pypi/l/remoku?style=for-the-badge&color=blue)](https://github.com/slug-enjoyer/remoku/blob/main/LICENSE)
[![PyPI - Python Version](https://img.shields.io/badge/python-3.10%2B-blue?style=for-the-badge)](https://pypi.org/project/remoku/)
[![build](https://img.shields.io/github/actions/workflow/status/slug-enjoyer/remoku/ci.yml?branch=main&style=for-the-badge&label=build)](https://github.com/slug-enjoyer/remoku/actions/workflows/ci.yml)

A small native desktop remote for Roku TVs and players, built directly on
Roku's public
[External Control Protocol (ECP)](https://developer.roku.com/docs/developer-program/dev-tools/external-control-api.md).

The UI is [NiceGUI](https://nicegui.io/) 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

```bash
uv tool install remoku
```

or with [pipx](https://pipx.pypa.io/):

```bash
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:

```bash
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.

```bash
# 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
- 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

```bash
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

```bash
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.

```bash
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](https://github.com/gitleaks/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](LICENSE).
