Skip to content

Instantly share code, notes, and snippets.

@sentient06
Last active June 6, 2019 10:56
Show Gist options
  • Save sentient06/f542615b66304ad36c738997aa5b3664 to your computer and use it in GitHub Desktop.
Save sentient06/f542615b66304ad36c738997aa5b3664 to your computer and use it in GitHub Desktop.
Roku remote
# To run this code, use the IP address of the roku device as such:
# ruby ./RokuRemote.rb 192.168.99.99
require 'io/console'
require 'uri'
require 'net/http'
unless ARGV[0]
puts "Need IP of device"
exit 0
else
@device_ip = ARGV[0]
end
@exit_msg = "⌽"
# Sends a POST request to the roku device.
def send_http_request_with_code code
begin
uri = URI.parse("http://#{@device_ip}:8060/keypress/#{code}")
request = Net::HTTP.new(uri.host, uri.port)
request.read_timeout = 1 # seconds
req = Net::HTTP::Post.new(uri.path)
res = request.request(req)
rescue StandardError
print "E"
end
end
# Reads keypresses from the user including 2 and 3 escape character sequences.
def read_char
STDIN.echo = false
STDIN.raw!
input = STDIN.getc.chr
if input == "\e" then
input << STDIN.read_nonblock(3) rescue nil
input << STDIN.read_nonblock(2) rescue nil
end
ensure
STDIN.echo = true
STDIN.cooked!
return input
end
# Keys recognised by the roku API:
@known_keys = [
"Home",
"Rev",
"Fwd",
"Play",
"Select",
"Left",
"Right",
"Down",
"Up",
"Back",
"InstantReplay",
"Info",
"Backspace",
"Search",
"Enter"
]
# User-friendly output:
@ui_keys = {
"Home" => "H",
"Rev" => "↞",
"Fwd" => "↠",
"Play" => "P",
"Select" => "✓",
"Left" => "←",
"Right" => "→",
"Down" => "↓",
"Up" => "↑",
"Back" => "⬅",
"InstantReplay" => "↻",
"Info" => "ℹ",
"Backspace" => "⌫",
"Search" => "⌕",
"Enter" => "⏎",
"?" => "?",
"1" => "⚡",
"2" => "⚡",
"3" => "⚡",
"4" => "⚡",
"5" => "⚡",
"6" => "⚡",
"d" => "⚡",
"j" => "⚡"
}
# Character-based aliases:
@characters = {
"h" => "Home",
"i" => "Info",
"y" => "InstantReplay",
"s" => "Search",
"," => "Rev",
"<" => "Rev",
"." => "Fwd",
">" => "Fwd",
"p" => "Play"
}
# Sequences of keys:
@konami = {
"1" => ["Home", "Home", "Home", "Home", "Home", "Up", "Rev", "Rev", "Fwd", "Fwd"],
"2" => ["Home", "Home", "Home", "Home", "Home", "Fwd", "Fwd", "Fwd", "Rev", "Rev"],
"3" => ["Home", "Home", "Home", "Home", "Home", "Up", "Right", "Down", "Left", "Up"],
"4" => ["Home", "Home", "Home", "Down", "Down", "Right", "Left", "Down", "Left", "Left"],
"5" => ["Home", "Home", "Home", "Up", "Up", "Right", "Left", "Right", "Left", "Right"],
"6" => ["Home", "Home", "Home", "Up", "Up", "Left", "Right", "Left", "Right", "Left"],
"d" => ["Play", "Play", "Play"],
"j" => ["Play", "Play", "Play", "Up", "Down", "Select", "Up", "Select"],
}
# History to show on screen:
@keys_history = []
@max_chars = 88
@sleeping_time = 3
# Visibility of the history:
@limit = (@max_chars - 2) / 2
def print_empty
print "\r"
print " " * @max_chars
end
def print_dash
print "\r"
print "-" * @max_chars
end
def print_sequence
print "\r "
@keys_history.last(@limit).each {|x| print @ui_keys[x], " " }
end
# Print prompt for repeat value:
def print_repeat_value
if @sequence_number == 0
seq = ""
else
seq = @sequence_number
end
print_empty
print "\r Repeat: #{seq}"
end
# Action that triggers the request based on the key pressed:
def key_down(key)
if @repeat_sequence == true
return
end
if @known_keys.include? key
send_http_request_with_code key
@keys_history.push key
else
@keys_history.push "?"
end
print_sequence
end
# Method that enters a sequence of keys:
def punch_sequence sequence
sequence.each do |x|
key_down x
sleep @sleeping_time / 10.0
end
end
@repeat_sequence = false
@sequence_number = 0
# Method to grab the key based on its value rather than its code:
def char_down character
if @characters.include? character
key_down @characters[character]
elsif @konami.include? character
@keys_history.push character
punch_sequence @konami[character]
elsif character == "k"
@keys_history = []
@sleeping_time = 3
print_empty
print_sequence
elsif character == "q"
puts @exit_msg
exit 0
elsif character == "]"
unless @sleeping_time - 1 < 2
@sleeping_time -= 1
end
elsif character == "["
@sleeping_time += 1
elsif character == "r"
@repeat_sequence = true
print "\n"
print_repeat_value
else
key_down "?"
end
end
# Repeat sequence number as text:
def compute_number character
number_text = @sequence_number.to_s
number_text += character
@sequence_number = number_text.to_i
end
# Return behaves differently when a sequence number is being entered:
def handle_return
if @repeat_sequence == true
@repeat_sequence = false
print "\033[F\n"
print_empty
print "\033[F"
print_sequence
if @sequence_number <= @keys_history.length
repeat_this = @keys_history.last(@sequence_number)
punch_sequence repeat_this
end
@sequence_number = 0
else
key_down "Select"
end
end
# Backspace behaves differently when a sequence number is being entered:
def handle_backspace
if @repeat_sequence == true
number_text = @sequence_number.to_s
number_text.chomp!(number_text[-1])
@sequence_number = number_text.to_i
print_repeat_value
else
key_down "Back"
end
end
# Method to read the key press event and act accordingly:
def show_single_key
c = read_char
case c
when " " # Space
key_down "Select"
when "\t" # Tab
key_down "TAB"
when "\r" # Return
handle_return
when "\n" # Line feed?
key_down "LINE FEED"
when "\e" # Escape
key_down "Back"
when "\e[A" # Up
key_down "Up"
when "\e[B" # Down
key_down "Down"
when "\e[C" # Right
key_down "Right"
when "\e[D" # Left
key_down "Left"
when "\177" # Backspace
handle_backspace
when "\004" # Delete
key_down "Back"
when "\e[3~" # Alternative delete
key_down "ALT DELETE"
when "\u0003" # Control + C
puts @exit_msg
exit 0
when /^.$/ # Single character:
character = c.inspect.gsub!(/^\"|\"?$/, '')
if @repeat_sequence == false
char_down character
else
compute_number character
print_repeat_value
end
else
puts "SOMETHING ELSE: #{c.inspect}"
end
end
# "\033[F" # Back one line
print "\n"
print_dash
print "\n Device IP: #{@device_ip}\n"
print_dash
# --------------------------------------------------------------------------------------
puts %(
Sequences: Keys: Shortcuts:
1 . Reboot h . Home i . Info d . Dev Panel
2 . Secret Screen y . InstantReplay s . Search q . Quit
3 . Secret Screen 2 p . Play [Arrows] r . Repeat last [x] keys
4 . Token install screen j . Remote JS debugging
5 . Developer settings [<] [,] ........... Rewind [ . Slower sequences
6 . Channel info [>] [.] ........... Forward ] . Faster sequences
[return] [space] .. Select k . Clear history
[ESC] [backspace] . Back
)
print_dash
print "\n "
show_single_key while(true)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment