remoku 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- remoku-2.0.0/PKG-INFO +232 -0
- remoku-2.0.0/README.md +200 -0
- remoku-2.0.0/pyproject.toml +147 -0
- remoku-2.0.0/pyproject.toml.orig +125 -0
- remoku-2.0.0/src/remoku/__init__.py +3 -0
- remoku-2.0.0/src/remoku/__main__.py +53 -0
- remoku-2.0.0/src/remoku/app.py +676 -0
- remoku-2.0.0/src/remoku/assets/remoku-128.png +0 -0
- remoku-2.0.0/src/remoku/assets/remoku-256.png +0 -0
- remoku-2.0.0/src/remoku/assets/remoku.svg +47 -0
- remoku-2.0.0/src/remoku/discovery.py +265 -0
- remoku-2.0.0/src/remoku/ecp.py +225 -0
- remoku-2.0.0/src/remoku/paths.py +11 -0
- remoku-2.0.0/src/remoku/py.typed +0 -0
remoku-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: remoku
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: A native desktop remote for Roku TVs and players, built on Roku's External Control Protocol.
|
|
5
|
+
Keywords: roku,remote-control,ecp,desktop,nicegui,home-automation
|
|
6
|
+
Author: slug-enjoyer
|
|
7
|
+
Author-email: slug-enjoyer <81844498+slug-enjoyer@users.noreply.github.com>
|
|
8
|
+
License-Expression: GPL-3.0-only
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: X11 Applications
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Home Automation
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Dist: nicegui>=3.0
|
|
24
|
+
Requires-Dist: requests>=2.31
|
|
25
|
+
Requires-Dist: pywebview>=6.0 ; sys_platform != 'linux'
|
|
26
|
+
Requires-Dist: pywebview[qt]>=6.0 ; sys_platform == 'linux'
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Project-URL: Repository, https://github.com/slug-enjoyer/remoku
|
|
29
|
+
Project-URL: Issues, https://github.com/slug-enjoyer/remoku/issues
|
|
30
|
+
Project-URL: Changelog, https://github.com/slug-enjoyer/remoku/blob/main/CHANGELOG.md
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# remoku
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/remoku/)
|
|
36
|
+
[](https://github.com/slug-enjoyer/remoku/blob/main/LICENSE)
|
|
37
|
+
[](https://pypi.org/project/remoku/)
|
|
38
|
+
[](https://github.com/slug-enjoyer/remoku/actions/workflows/ci.yml)
|
|
39
|
+
|
|
40
|
+
A small native desktop remote for Roku TVs and players, built directly on
|
|
41
|
+
Roku's public
|
|
42
|
+
[External Control Protocol (ECP)](https://developer.roku.com/docs/developer-program/dev-tools/external-control-api.md).
|
|
43
|
+
|
|
44
|
+
The UI is [NiceGUI](https://nicegui.io/) running in a native window
|
|
45
|
+
(pywebview): `remoku` opens a window, full stop — no browser tab, no server
|
|
46
|
+
to manage, no browser mode.
|
|
47
|
+
|
|
48
|
+
Every Roku runs a tiny REST server on TCP port 8060. This app talks to it
|
|
49
|
+
directly — no accounts, no cloud, no companion service.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv tool install remoku
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
or with [pipx](https://pipx.pypa.io/):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pipx install remoku
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
That gives you the `remoku` command (plus a `rokuremote` alias) in an
|
|
64
|
+
isolated environment. To also get a desktop entry and icons, run the
|
|
65
|
+
installer:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
It installs with uv (or pipx) under `~/.local` — no root, nothing
|
|
72
|
+
system-wide — and drops a launcher into your app menu. If PyPI is
|
|
73
|
+
unreachable, the installer falls back to the GitHub repository.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# install somewhere else
|
|
77
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --prefix /opt/remoku
|
|
78
|
+
|
|
79
|
+
# a specific branch or tag (also used for the desktop entry and icons)
|
|
80
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --ref v2.0.0
|
|
81
|
+
|
|
82
|
+
# just the command, no desktop entry
|
|
83
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --no-desktop
|
|
84
|
+
|
|
85
|
+
# uninstall (settings and icon cache are kept)
|
|
86
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --uninstall
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
- Purple Roku-style remote in a native window: power, back, home, info,
|
|
92
|
+
instant replay, D-pad with OK, playback, volume
|
|
93
|
+
- App shortcuts grid, loaded from the device (`/query/apps`) with real
|
|
94
|
+
icons; click a tile to launch
|
|
95
|
+
- TV inputs in their own row, including custom names (e.g. "Nintendo
|
|
96
|
+
Switch", "Soundbar") configured on the TV
|
|
97
|
+
- A "Type to the Roku" box: each keystroke streams to the device, and
|
|
98
|
+
deleting a character sends Backspace, so you can edit as you type
|
|
99
|
+
- Device discovery: SSDP, plus an ARP-assisted scan of the local /24 that
|
|
100
|
+
stays gentle on consumer routers (broad high-concurrency scans can take
|
|
101
|
+
the whole Wi-Fi network down for ~10 seconds)
|
|
102
|
+
- Wake button: Roku TVs in standby only answer `device-info` and reply 403
|
|
103
|
+
to everything else; the button sends Wake-on-LAN and waits for the TV to
|
|
104
|
+
come up
|
|
105
|
+
- Typing: letters, digits and punctuation go straight to the Roku whenever
|
|
106
|
+
its on-screen keyboard is up, with no mode to toggle
|
|
107
|
+
- Keyboard control: the arrow keys behave like the D-pad, so the whole
|
|
108
|
+
remote is usable without a mouse
|
|
109
|
+
|
|
110
|
+
## Requirements
|
|
111
|
+
|
|
112
|
+
- Python 3.10+
|
|
113
|
+
- A graphical session (X11 or Wayland)
|
|
114
|
+
|
|
115
|
+
That's all: the Qt backend ships as wheels, so there is nothing to install
|
|
116
|
+
for GTK or WebKit. On Linux the window uses Qt (PyQt6) and falls back to
|
|
117
|
+
GTK/WebKit2 when PyGObject is available.
|
|
118
|
+
|
|
119
|
+
## Usage
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
remoku # start the native window
|
|
123
|
+
remoku --ip 192.0.2.50
|
|
124
|
+
remoku --list # print Roku devices found on the network
|
|
125
|
+
remoku --apps # print apps/inputs of the last used device
|
|
126
|
+
remoku --key Home # send a single keypress
|
|
127
|
+
rokuremote --help # rokuremote is an alias for remoku
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The last used device is remembered in `~/.config/remoku/devices.json`;
|
|
131
|
+
app icons are cached in `~/.cache/remoku/icons/`.
|
|
132
|
+
|
|
133
|
+
## Keyboard shortcuts
|
|
134
|
+
|
|
135
|
+
| Key | Action |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| Arrows | Up, Down, Left, Right |
|
|
138
|
+
| Enter | Select (OK), or press the focused on-screen button |
|
|
139
|
+
| Esc | Home |
|
|
140
|
+
| Backspace | Backspace (deletes while typing) |
|
|
141
|
+
| Letters / digits / punctuation / space | Typed to the Roku |
|
|
142
|
+
| Alt + W / A / S / D | Up / Left / Down / Right |
|
|
143
|
+
| Alt + B | Back |
|
|
144
|
+
| Alt + H | Home |
|
|
145
|
+
| Alt + I | Info |
|
|
146
|
+
| Alt + O | Select (OK) |
|
|
147
|
+
| Alt + R, F or P | Rewind, Fast forward, Play/pause |
|
|
148
|
+
| Alt + `,` `.` or `/` | Rewind, Fast forward, Play/pause |
|
|
149
|
+
| Alt + `[` or `-` | Volume down |
|
|
150
|
+
| Alt + `]`, `+` or `=` | Volume up |
|
|
151
|
+
| Alt + `\` or M | Mute |
|
|
152
|
+
| Ctrl + Up / Down | Volume up / down |
|
|
153
|
+
| Tab / Shift+Tab | Move focus through the window |
|
|
154
|
+
|
|
155
|
+
Printable keys are always forwarded to the Roku as `Lit_` characters.
|
|
156
|
+
Roku ignores those unless a text field is focused, which means typing
|
|
157
|
+
simply works whenever the TV's on-screen keyboard is up. Roku's API has no
|
|
158
|
+
way to ask whether a keyboard is open (verified against
|
|
159
|
+
`/query/active-app`, `/query/device-info` and friends), which is why the
|
|
160
|
+
remote keys that used to live on letters moved to Alt combinations. When a
|
|
161
|
+
button in the window has focus, Enter presses that button instead of
|
|
162
|
+
reaching the Roku.
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git clone https://github.com/slug-enjoyer/remoku
|
|
168
|
+
cd remoku
|
|
169
|
+
|
|
170
|
+
just install # uv sync --all-extras
|
|
171
|
+
just run # launch the native window from source
|
|
172
|
+
just lint # ruff format + check, pyrefly
|
|
173
|
+
just test # pytest with coverage (gate: 80%)
|
|
174
|
+
just security # bandit
|
|
175
|
+
just build # uv build
|
|
176
|
+
just version bump patch # commitizen: bump, changelog, tag
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Every push and pull request runs lint, the test matrix (Python 3.10–3.14),
|
|
180
|
+
bandit and a build check; tagging `v*` runs the tests again and publishes
|
|
181
|
+
to PyPI with uv (trusted publishing).
|
|
182
|
+
|
|
183
|
+
The test suite is hermetic by construction: an autouse guard raises on any
|
|
184
|
+
non-loopback network access, and XDG paths are redirected into a temp
|
|
185
|
+
sandbox, so tests never touch a real device, the network or your real
|
|
186
|
+
config — `tests/test_hermeticity.py` proves it.
|
|
187
|
+
|
|
188
|
+
## Secret scanning
|
|
189
|
+
|
|
190
|
+
A pre-commit hook blocks commits that contain secrets and warns (without
|
|
191
|
+
blocking) when staged lines look like local/private values such as LAN
|
|
192
|
+
addresses, MAC addresses or home directory paths.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
just hooks # enable the hook for this clone (per-clone git config)
|
|
196
|
+
just audit # scan the whole repo, history included, any time
|
|
197
|
+
|
|
198
|
+
make hooks # same thing, if you prefer make
|
|
199
|
+
make audit
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
It runs [gitleaks](https://github.com/gitleaks/gitleaks) when installed
|
|
203
|
+
(`sudo pacman -S gitleaks`) and falls back to a small built-in check
|
|
204
|
+
otherwise. A deliberate example can be marked on its line with the comment
|
|
205
|
+
`sensitive-example`; `git commit --no-verify` bypasses the hook entirely.
|
|
206
|
+
|
|
207
|
+
## Notes and troubleshooting
|
|
208
|
+
|
|
209
|
+
- Since Roku OS 14.1, remote commands require **Settings → System →
|
|
210
|
+
Advanced system settings → Control by mobile apps → Enabled** on the
|
|
211
|
+
device. If a command is refused, the app says so in the status bar.
|
|
212
|
+
- A Roku TV in standby (`power-mode: Ready`) answers `device-info` but
|
|
213
|
+
refuses every other command with HTTP 403, which looks like the setting
|
|
214
|
+
above being off even when it is on. In that case the app shows a **Wake
|
|
215
|
+
TV** button, which sends a Wake-on-LAN magic packet and waits for the TV
|
|
216
|
+
to come up (this is what Roku's own mobile app does). If waking does not
|
|
217
|
+
work, enable **Settings → System → Power → Fast TV start** on the TV.
|
|
218
|
+
- SSDP discovery is unreliable on many Wi-Fi networks because access
|
|
219
|
+
points drop multicast between clients. When that happens the app falls
|
|
220
|
+
back to scanning: it pokes the kernel into resolving every address in
|
|
221
|
+
the local /24 via ARP (a single UDP datagram each), then TCP-probes only
|
|
222
|
+
the hosts that actually exist. If the ARP table cannot be read it falls
|
|
223
|
+
back to a low-concurrency TCP scan, and it never scans wider than a /24.
|
|
224
|
+
- Connecting to a device is retried up to three times if the network is
|
|
225
|
+
briefly busy, and the startup scan runs only after the first connection
|
|
226
|
+
attempt finishes.
|
|
227
|
+
- Power/volume buttons only appear for devices that report support for
|
|
228
|
+
them (Roku TVs, or players using TV controls over HDMI-CEC).
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
GPL-3.0. See [LICENSE](LICENSE).
|
remoku-2.0.0/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# remoku
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/remoku/)
|
|
4
|
+
[](https://github.com/slug-enjoyer/remoku/blob/main/LICENSE)
|
|
5
|
+
[](https://pypi.org/project/remoku/)
|
|
6
|
+
[](https://github.com/slug-enjoyer/remoku/actions/workflows/ci.yml)
|
|
7
|
+
|
|
8
|
+
A small native desktop remote for Roku TVs and players, built directly on
|
|
9
|
+
Roku's public
|
|
10
|
+
[External Control Protocol (ECP)](https://developer.roku.com/docs/developer-program/dev-tools/external-control-api.md).
|
|
11
|
+
|
|
12
|
+
The UI is [NiceGUI](https://nicegui.io/) running in a native window
|
|
13
|
+
(pywebview): `remoku` opens a window, full stop — no browser tab, no server
|
|
14
|
+
to manage, no browser mode.
|
|
15
|
+
|
|
16
|
+
Every Roku runs a tiny REST server on TCP port 8060. This app talks to it
|
|
17
|
+
directly — no accounts, no cloud, no companion service.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv tool install remoku
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or with [pipx](https://pipx.pypa.io/):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pipx install remoku
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That gives you the `remoku` command (plus a `rokuremote` alias) in an
|
|
32
|
+
isolated environment. To also get a desktop entry and icons, run the
|
|
33
|
+
installer:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It installs with uv (or pipx) under `~/.local` — no root, nothing
|
|
40
|
+
system-wide — and drops a launcher into your app menu. If PyPI is
|
|
41
|
+
unreachable, the installer falls back to the GitHub repository.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# install somewhere else
|
|
45
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --prefix /opt/remoku
|
|
46
|
+
|
|
47
|
+
# a specific branch or tag (also used for the desktop entry and icons)
|
|
48
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --ref v2.0.0
|
|
49
|
+
|
|
50
|
+
# just the command, no desktop entry
|
|
51
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --no-desktop
|
|
52
|
+
|
|
53
|
+
# uninstall (settings and icon cache are kept)
|
|
54
|
+
curl -fsSL https://raw.githubusercontent.com/slug-enjoyer/remoku/main/install.sh | sh -s -- --uninstall
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- Purple Roku-style remote in a native window: power, back, home, info,
|
|
60
|
+
instant replay, D-pad with OK, playback, volume
|
|
61
|
+
- App shortcuts grid, loaded from the device (`/query/apps`) with real
|
|
62
|
+
icons; click a tile to launch
|
|
63
|
+
- TV inputs in their own row, including custom names (e.g. "Nintendo
|
|
64
|
+
Switch", "Soundbar") configured on the TV
|
|
65
|
+
- A "Type to the Roku" box: each keystroke streams to the device, and
|
|
66
|
+
deleting a character sends Backspace, so you can edit as you type
|
|
67
|
+
- Device discovery: SSDP, plus an ARP-assisted scan of the local /24 that
|
|
68
|
+
stays gentle on consumer routers (broad high-concurrency scans can take
|
|
69
|
+
the whole Wi-Fi network down for ~10 seconds)
|
|
70
|
+
- Wake button: Roku TVs in standby only answer `device-info` and reply 403
|
|
71
|
+
to everything else; the button sends Wake-on-LAN and waits for the TV to
|
|
72
|
+
come up
|
|
73
|
+
- Typing: letters, digits and punctuation go straight to the Roku whenever
|
|
74
|
+
its on-screen keyboard is up, with no mode to toggle
|
|
75
|
+
- Keyboard control: the arrow keys behave like the D-pad, so the whole
|
|
76
|
+
remote is usable without a mouse
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- Python 3.10+
|
|
81
|
+
- A graphical session (X11 or Wayland)
|
|
82
|
+
|
|
83
|
+
That's all: the Qt backend ships as wheels, so there is nothing to install
|
|
84
|
+
for GTK or WebKit. On Linux the window uses Qt (PyQt6) and falls back to
|
|
85
|
+
GTK/WebKit2 when PyGObject is available.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
remoku # start the native window
|
|
91
|
+
remoku --ip 192.0.2.50
|
|
92
|
+
remoku --list # print Roku devices found on the network
|
|
93
|
+
remoku --apps # print apps/inputs of the last used device
|
|
94
|
+
remoku --key Home # send a single keypress
|
|
95
|
+
rokuremote --help # rokuremote is an alias for remoku
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The last used device is remembered in `~/.config/remoku/devices.json`;
|
|
99
|
+
app icons are cached in `~/.cache/remoku/icons/`.
|
|
100
|
+
|
|
101
|
+
## Keyboard shortcuts
|
|
102
|
+
|
|
103
|
+
| Key | Action |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| Arrows | Up, Down, Left, Right |
|
|
106
|
+
| Enter | Select (OK), or press the focused on-screen button |
|
|
107
|
+
| Esc | Home |
|
|
108
|
+
| Backspace | Backspace (deletes while typing) |
|
|
109
|
+
| Letters / digits / punctuation / space | Typed to the Roku |
|
|
110
|
+
| Alt + W / A / S / D | Up / Left / Down / Right |
|
|
111
|
+
| Alt + B | Back |
|
|
112
|
+
| Alt + H | Home |
|
|
113
|
+
| Alt + I | Info |
|
|
114
|
+
| Alt + O | Select (OK) |
|
|
115
|
+
| Alt + R, F or P | Rewind, Fast forward, Play/pause |
|
|
116
|
+
| Alt + `,` `.` or `/` | Rewind, Fast forward, Play/pause |
|
|
117
|
+
| Alt + `[` or `-` | Volume down |
|
|
118
|
+
| Alt + `]`, `+` or `=` | Volume up |
|
|
119
|
+
| Alt + `\` or M | Mute |
|
|
120
|
+
| Ctrl + Up / Down | Volume up / down |
|
|
121
|
+
| Tab / Shift+Tab | Move focus through the window |
|
|
122
|
+
|
|
123
|
+
Printable keys are always forwarded to the Roku as `Lit_` characters.
|
|
124
|
+
Roku ignores those unless a text field is focused, which means typing
|
|
125
|
+
simply works whenever the TV's on-screen keyboard is up. Roku's API has no
|
|
126
|
+
way to ask whether a keyboard is open (verified against
|
|
127
|
+
`/query/active-app`, `/query/device-info` and friends), which is why the
|
|
128
|
+
remote keys that used to live on letters moved to Alt combinations. When a
|
|
129
|
+
button in the window has focus, Enter presses that button instead of
|
|
130
|
+
reaching the Roku.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/slug-enjoyer/remoku
|
|
136
|
+
cd remoku
|
|
137
|
+
|
|
138
|
+
just install # uv sync --all-extras
|
|
139
|
+
just run # launch the native window from source
|
|
140
|
+
just lint # ruff format + check, pyrefly
|
|
141
|
+
just test # pytest with coverage (gate: 80%)
|
|
142
|
+
just security # bandit
|
|
143
|
+
just build # uv build
|
|
144
|
+
just version bump patch # commitizen: bump, changelog, tag
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Every push and pull request runs lint, the test matrix (Python 3.10–3.14),
|
|
148
|
+
bandit and a build check; tagging `v*` runs the tests again and publishes
|
|
149
|
+
to PyPI with uv (trusted publishing).
|
|
150
|
+
|
|
151
|
+
The test suite is hermetic by construction: an autouse guard raises on any
|
|
152
|
+
non-loopback network access, and XDG paths are redirected into a temp
|
|
153
|
+
sandbox, so tests never touch a real device, the network or your real
|
|
154
|
+
config — `tests/test_hermeticity.py` proves it.
|
|
155
|
+
|
|
156
|
+
## Secret scanning
|
|
157
|
+
|
|
158
|
+
A pre-commit hook blocks commits that contain secrets and warns (without
|
|
159
|
+
blocking) when staged lines look like local/private values such as LAN
|
|
160
|
+
addresses, MAC addresses or home directory paths.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
just hooks # enable the hook for this clone (per-clone git config)
|
|
164
|
+
just audit # scan the whole repo, history included, any time
|
|
165
|
+
|
|
166
|
+
make hooks # same thing, if you prefer make
|
|
167
|
+
make audit
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
It runs [gitleaks](https://github.com/gitleaks/gitleaks) when installed
|
|
171
|
+
(`sudo pacman -S gitleaks`) and falls back to a small built-in check
|
|
172
|
+
otherwise. A deliberate example can be marked on its line with the comment
|
|
173
|
+
`sensitive-example`; `git commit --no-verify` bypasses the hook entirely.
|
|
174
|
+
|
|
175
|
+
## Notes and troubleshooting
|
|
176
|
+
|
|
177
|
+
- Since Roku OS 14.1, remote commands require **Settings → System →
|
|
178
|
+
Advanced system settings → Control by mobile apps → Enabled** on the
|
|
179
|
+
device. If a command is refused, the app says so in the status bar.
|
|
180
|
+
- A Roku TV in standby (`power-mode: Ready`) answers `device-info` but
|
|
181
|
+
refuses every other command with HTTP 403, which looks like the setting
|
|
182
|
+
above being off even when it is on. In that case the app shows a **Wake
|
|
183
|
+
TV** button, which sends a Wake-on-LAN magic packet and waits for the TV
|
|
184
|
+
to come up (this is what Roku's own mobile app does). If waking does not
|
|
185
|
+
work, enable **Settings → System → Power → Fast TV start** on the TV.
|
|
186
|
+
- SSDP discovery is unreliable on many Wi-Fi networks because access
|
|
187
|
+
points drop multicast between clients. When that happens the app falls
|
|
188
|
+
back to scanning: it pokes the kernel into resolving every address in
|
|
189
|
+
the local /24 via ARP (a single UDP datagram each), then TCP-probes only
|
|
190
|
+
the hosts that actually exist. If the ARP table cannot be read it falls
|
|
191
|
+
back to a low-concurrency TCP scan, and it never scans wider than a /24.
|
|
192
|
+
- Connecting to a device is retried up to three times if the network is
|
|
193
|
+
briefly busy, and the startup scan runs only after the first connection
|
|
194
|
+
attempt finishes.
|
|
195
|
+
- Power/volume buttons only appear for devices that report support for
|
|
196
|
+
them (Roku TVs, or players using TV controls over HDMI-CEC).
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
GPL-3.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "remoku"
|
|
3
|
+
version = "2.0.0"
|
|
4
|
+
description = "A native desktop remote for Roku TVs and players, built on Roku's External Control Protocol."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "GPL-3.0-only"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"nicegui>=3.0",
|
|
10
|
+
"requests>=2.31",
|
|
11
|
+
"pywebview>=6.0; sys_platform != 'linux'",
|
|
12
|
+
"pywebview[qt]>=6.0; sys_platform == 'linux'",
|
|
13
|
+
]
|
|
14
|
+
keywords = [
|
|
15
|
+
"roku",
|
|
16
|
+
"remote-control",
|
|
17
|
+
"ecp",
|
|
18
|
+
"desktop",
|
|
19
|
+
"nicegui",
|
|
20
|
+
"home-automation",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Environment :: X11 Applications",
|
|
25
|
+
"Intended Audience :: End Users/Desktop",
|
|
26
|
+
"Natural Language :: English",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
34
|
+
"Topic :: Home Automation",
|
|
35
|
+
"Topic :: Multimedia :: Video",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[[project.authors]]
|
|
40
|
+
name = "slug-enjoyer"
|
|
41
|
+
email = "81844498+slug-enjoyer@users.noreply.github.com"
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Repository = "https://github.com/slug-enjoyer/remoku"
|
|
45
|
+
Issues = "https://github.com/slug-enjoyer/remoku/issues"
|
|
46
|
+
Changelog = "https://github.com/slug-enjoyer/remoku/blob/main/CHANGELOG.md"
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
remoku = "remoku.__main__:main"
|
|
50
|
+
rokuremote = "remoku.__main__:main"
|
|
51
|
+
|
|
52
|
+
[build-system]
|
|
53
|
+
requires = ["uv_build>=0.9,<0.13"]
|
|
54
|
+
build-backend = "uv_build"
|
|
55
|
+
|
|
56
|
+
[dependency-groups]
|
|
57
|
+
dev = [
|
|
58
|
+
"bandit>=1.8.0",
|
|
59
|
+
"commitizen>=4.0.0",
|
|
60
|
+
"pyrefly>=0.30.0",
|
|
61
|
+
"pytest>=8.0.0",
|
|
62
|
+
"pytest-asyncio>=1.0.0",
|
|
63
|
+
"pytest-cov>=6.0.0",
|
|
64
|
+
"pytest-mock>=3.14.0",
|
|
65
|
+
"ruff>=0.9.0",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 120
|
|
70
|
+
target-version = "py310"
|
|
71
|
+
src = [
|
|
72
|
+
"src/remoku",
|
|
73
|
+
"tests",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint]
|
|
77
|
+
select = [
|
|
78
|
+
"E",
|
|
79
|
+
"F",
|
|
80
|
+
"W",
|
|
81
|
+
"I",
|
|
82
|
+
"N",
|
|
83
|
+
"UP",
|
|
84
|
+
"YTT",
|
|
85
|
+
"ANN",
|
|
86
|
+
"S",
|
|
87
|
+
"B",
|
|
88
|
+
"C4",
|
|
89
|
+
"SIM",
|
|
90
|
+
"PTH",
|
|
91
|
+
"RUF",
|
|
92
|
+
"PERF",
|
|
93
|
+
"BLE",
|
|
94
|
+
"T20",
|
|
95
|
+
"ERA",
|
|
96
|
+
"TCH",
|
|
97
|
+
"ARG",
|
|
98
|
+
"FBT",
|
|
99
|
+
"RET",
|
|
100
|
+
"FURB",
|
|
101
|
+
]
|
|
102
|
+
ignore = ["ANN401"]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.lint.per-file-ignores]
|
|
105
|
+
"src/remoku/__main__.py" = ["T20"]
|
|
106
|
+
"tests/*" = [
|
|
107
|
+
"ANN",
|
|
108
|
+
"S",
|
|
109
|
+
"ARG",
|
|
110
|
+
"FBT",
|
|
111
|
+
"T20",
|
|
112
|
+
"PTH",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[tool.ruff.format]
|
|
116
|
+
quote-style = "double"
|
|
117
|
+
line-ending = "lf"
|
|
118
|
+
|
|
119
|
+
[tool.pyrefly]
|
|
120
|
+
project-includes = ["src/remoku"]
|
|
121
|
+
|
|
122
|
+
[tool.bandit]
|
|
123
|
+
exclude_dirs = ["tests"]
|
|
124
|
+
|
|
125
|
+
[tool.coverage.run]
|
|
126
|
+
source = ["remoku"]
|
|
127
|
+
branch = true
|
|
128
|
+
omit = ["src/remoku/__main__.py"]
|
|
129
|
+
|
|
130
|
+
[tool.coverage.report]
|
|
131
|
+
fail_under = 80
|
|
132
|
+
show_missing = true
|
|
133
|
+
|
|
134
|
+
[tool.pytest.ini_options]
|
|
135
|
+
asyncio_mode = "auto"
|
|
136
|
+
testpaths = ["tests"]
|
|
137
|
+
main_file = "tests/main.py"
|
|
138
|
+
|
|
139
|
+
[tool.commitizen]
|
|
140
|
+
name = "cz_conventional_commits"
|
|
141
|
+
version = "2.0.0"
|
|
142
|
+
tag_format = "v$version"
|
|
143
|
+
version_files = [
|
|
144
|
+
"pyproject.toml:version",
|
|
145
|
+
"src/remoku/__init__.py",
|
|
146
|
+
]
|
|
147
|
+
update_changelog_on_bump = true
|