/roku_remote

Python tkinter GUI application as a remote control for Roku devices

Primary LanguagePythonMIT LicenseMIT

roku

Description

roku is a GUI application that is a remote control for Roku devices.

roku is written in Python.

About

The application uses SSDP and RokuECP to discover and control Roku devices on the network and displays a user-interface resembling a remote control. This allows you to:

  • select the device to control
  • select the video input
  • navigate the on-screen user-interface
  • click the various buttons to send commands to the roku
  • use the keyboard to send keystrokes in search boxes
Usage: roku <options>

Options:
  --log-level [debug|info|error]  Log level (default: debug)
  --timeout INTEGER               length of time in seconds to keep listening
                                  for devices, default 60s
  -h, --help                      Show this message and exit.

Also part of the package is a utility called discover that performs network discovery of all devices that respond to the SSDP request.

Usage: discover

Example output from discover:

$ discover 
2023-01-25 12:55:03,876 INFO     discovering ...
2023-01-25 12:55:03,877 INFO     discovery thread running
2023-01-25 12:55:03,880 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'upnp:rootdevice', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::upnp:rootdevice', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,881 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:device:InternetGatewayDevice:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:device:InternetGatewayDevice:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,881 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:device:WANConnectionDevice:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:device:WANConnectionDevice:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,881 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:device:WANDevice:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:device:WANDevice:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,881 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,882 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:service:WANIPConnection:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:service:WANIPConnection:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,882 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:service:WANPPPConnection:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:service:WANPPPConnection:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,882 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'urn:schemas-upnp-org:service:Layer3Forwarding:1', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c::urn:schemas-upnp-org:service:Layer3Forwarding:1', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:03,883 INFO     discovered: {'CACHE-CONTROL': 'max-age=120', 'ST': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c', 'USN': 'uuid:67d10bb0-9729-4a3f-8bb6-c2491237430c', 'EXT': '', 'SERVER': 'TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8', 'LOCATION': 'http://192.168.0.1:1900/ejbqw/rootDesc.xml', 'OPT': '"http://schemas.upnp.org/upnp/1/0/"; ns=01', '01-NLS': '1', 'BOOTID.UPNP.ORG': '1', 'CONFIGID.UPNP.ORG': '1337'}
2023-01-25 12:55:17,069 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'upnp:rootdevice', 'USN': 'uuid:28000000-0000-1000-8008-44d878c2f084::upnp:rootdevice', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.172:8060/', 'WAKEUP': 'MAC=cc:a1:2b:6a:c5:cf;Timeout=10'}
2023-01-25 12:55:17,070 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'roku:ecp', 'USN': 'uuid:roku:ecp:X000008HLY9X', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.172:8060/', 'device-group.roku.com': '016D31572B73AFE542A4', 'WAKEUP': 'MAC=cc:a1:2b:6a:c5:cf;Timeout=10'}
2023-01-25 12:55:17,070 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'urn:dial-multiscreen-org:service:dial:1', 'USN': 'uuid:28000000-0000-1000-8008-44d878c2f084::urn:dial-multiscreen-org:service:dial:1', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.172:8060/dial/dd.xml', 'WAKEUP': 'MAC=cc:a1:2b:6a:c5:cf;Timeout=10'}
2023-01-25 12:55:32,161 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'upnp:rootdevice', 'USN': 'uuid:296c0018-500b-1099-8029-64e0030354d9::upnp:rootdevice', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.103:8060/', 'WAKEUP': 'MAC=64:e0:03:03:54:d9;Timeout=10'}
2023-01-25 12:55:32,162 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'roku:ecp', 'USN': 'uuid:roku:ecp:YK00HD760105', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.103:8060/', 'device-group.roku.com': '016D31572B73AFE542A4', 'WAKEUP': 'MAC=64:e0:03:03:54:d9;Timeout=10'}
2023-01-25 12:55:32,162 INFO     discovered: {'Cache-Control': 'max-age=3600', 'ST': 'urn:dial-multiscreen-org:service:dial:1', 'USN': 'uuid:296c0018-500b-1099-8029-64e0030354d9::urn:dial-multiscreen-org:service:dial:1', 'Ext': '', 'Server': 'Roku/11.5.0 UPnP/1.0 Roku/11.5.0', 'LOCATION': 'http://192.168.0.103:8060/dial/dd.xml', 'WAKEUP': 'MAC=64:e0:03:03:54:d9;Timeout=10'}
2023-01-25 12:56:03,910 INFO     stopping discovery thread

To build the application:

python -m build

To install/reinstall the wheel:

pip install dist/*.whl [--force-reinstall]

or to install as editable

pip install -e .

The application gui will start in "discovering ..." mode. After a period of time, network and device dependent, a list of Roku devices will appear. The power button dynamically reflects the current power state of the Roku device. During initial discovery and re-discovery on a communication execption, the user-interface controls are disabled. Under a periodic (re)-discovery (every App.DISCOVER_INTERVAL - 5 minutes) the user interface controls remain enabled. The discover button will start a discovery thread unless one is already running. The currently selected Roku's power state is refreshed every App.POWER_UPDATE_INTERVAL (10 seconds).

image info image info image info

Keyboard Guide

The user interface has keyboard support, so you can type in search fields etc. Other keys with functions are:

Keyboard Key Roku Function
Backspace Backspace
Delete Backspace
Cursor Up Up
Cursor Down Down
Cursor Right Right
Cursor Left Left
Pause Play/Pause
Home Home
Escape Back
Return Select

Channel Ribbon

The channel ribbon contains images for each known channel. To select a channel to be displayed on the Roku, select it by clickig the mouse. The ribbon scrolls horizontally. I determined these by brute force requesting from my roku devices using a bash script. The channel ribbon is created with the roku_remote/images/*.jpeg files. You can remove chan<id>.jpeg images from the roku_remote/images/ folder that you don't want and they will no longer appear in the ribbon when it gets created. See my example script to do that. Similarly, you can add your own chan<id>.jpeg image for a channel that you are aware of. Here's a table of all the channel ids I know:

Channel ID Name Image
12 Netflix
13 Prime Video
837 YouTube
2016 Crackle
2285 Hulu
14362 Amazon Music
15985 Play on Roku
19977 Spotify
22297 Spotify (again)
23353 PBS
27536 CBS News
31012 Roku Vudu Store
31440 Paramount +
34376 ESPN
43465 fuboTV
43508 fuboTV (again)
43735 unknown
54383 DFP Ads
55545 unknown
58484 unknown
61322 HBOmax
62675 WCNC+
72916 GO!
74519 pluto(tv)
83669 NCAA March Madness Live
98832 Roku Developers
109022 unknown
122409 kanopy
130644 KARTOON channel!
151908 Roku Channel
154157 TNT
155883 My Channel
173735 Ecosystem
194485 Survey Channel
250045 My Channel (again)
260537 Google Interactice Media Ads
262399 unknown (chromium?)
265831 Olympic Channel
272343 My Channel (again)
278897 unknown
291097 Disney+
291685 Streaming Cities
295059 Roku Developers (again)
551012 AppleTV
557456 CobaltLib
562859 My Channel (again)
586995 Airplay
594971 My Channel (again)
596080 Roku Developers (again)
596759 TCL Channel Movies, Cartoons & TV
600835 unknown
611444 RUM
611688 Roku NetPing
615685 freevee
631123 Happy Summer
631467 CLoud SDK
633458 Voice Help
639619 My Channel (again)
659410 Launcher
680626 Dynamic

User Interface Controls:

Button Function
Power off
Power on
Discover devices
Go back
Guide
Up
Down
Left
Right
Instant Replay
Info / Options
no function as yet
Rewind
Play / Pause
Fast Forward
Volume Mute
Volume Down
Volume Up
Channel Ribbon, click image of channel to open on Roku