102 lines
2.7 KiB
Python
102 lines
2.7 KiB
Python
from roku import Roku
|
|
import os
|
|
import json
|
|
import threading
|
|
|
|
ROKU_IP = os.environ.get("ROKU_IP")
|
|
if not ROKU_IP:
|
|
print("Roku IP not found in env!")
|
|
|
|
COMMANDS = {
|
|
# Standard Keys
|
|
"home": "Home",
|
|
"reverse": "Rev",
|
|
"forward": "Fwd",
|
|
"play": "Play",
|
|
"select": "Select",
|
|
"left": "Left",
|
|
"right": "Right",
|
|
"down": "Down",
|
|
"up": "Up",
|
|
"back": "Back",
|
|
"replay": "InstantReplay",
|
|
"info": "Info",
|
|
"backspace": "Backspace",
|
|
"search": "Search",
|
|
"enter": "Enter",
|
|
"literal": "Lit",
|
|
# For devices that support "Find Remote"
|
|
"find_remote": "FindRemote",
|
|
# For Roku TV
|
|
"volume_down": "VolumeDown",
|
|
"volume_up": "VolumeUp",
|
|
"volume_mute": "VolumeMute",
|
|
# For Roku TV while on TV tuner channel
|
|
"channel_up": "ChannelUp",
|
|
"channel_down": "ChannelDown",
|
|
# For Roku TV current input
|
|
"input_tuner": "InputTuner",
|
|
"input_hdmi1": "InputHDMI1",
|
|
"input_hdmi2": "InputHDMI2",
|
|
"input_hdmi3": "InputHDMI3",
|
|
"input_hdmi4": "InputHDMI4",
|
|
"input_av1": "InputAV1",
|
|
# For devices that support being turned on/off
|
|
"power": "Power",
|
|
"poweroff": "PowerOff",
|
|
"poweron": "PowerOn",
|
|
}
|
|
|
|
class RokuWrapper():
|
|
def _send_msg(self, code: int, msg: object):
|
|
try:
|
|
content = json.dumps(msg).encode()
|
|
self.parent._send(code, content)
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
def __init__(self, parent):
|
|
self.parent = parent
|
|
|
|
# eh maybe close the connection? Not really needed
|
|
self.timer: threading.Timer | None = None
|
|
|
|
try:
|
|
self.rapp = Roku(ROKU_IP)
|
|
print(f"Roku client connected to {ROKU_IP}", flush=True)
|
|
except Exception as e:
|
|
self.parent.log_error("Failed to create Roku client: %s", e)
|
|
return self._send_msg(500, {"error": "roku connection failed"})
|
|
|
|
def run_command(self, command: str):
|
|
if not ROKU_IP:
|
|
return self._send_msg(500, {"error": "Roku IP not found in env! Do not attempt further Roku requests"})
|
|
|
|
if not command:
|
|
return self._send_msg(404, {"error": "command not given"})
|
|
command = command.split('/')[0]
|
|
|
|
# If command is not known, nothing to do
|
|
if command not in COMMANDS:
|
|
return self._send_msg(404, {"error": f"unknown command. Available commands: {", ".join(COMMANDS.keys())}"})
|
|
|
|
if hasattr(self.rapp, command):
|
|
attr = getattr(self.rapp, command)
|
|
if callable(attr):
|
|
attr()
|
|
return self._send_msg(200, {"result": f"{command} executed"})
|
|
|
|
# Fallback: send the Roku key name using common API methods
|
|
key_name = COMMANDS[command]
|
|
# common method names in Roku libs
|
|
for send_fn in ("press", "keypress", "send_key", "send_keypress", "button"):
|
|
if hasattr(self.rapp, send_fn):
|
|
fn = getattr(self.rapp, send_fn)
|
|
try:
|
|
fn(key_name)
|
|
return self._send_msg(200, {"result": f"{command} sent as {key_name} via {send_fn}"})
|
|
except TypeError:
|
|
continue
|
|
|
|
self._send_msg(404, {"error": "unknown endpoint"})
|