Skip to content

Commit

Permalink
moved to redis for device configs, put device location on own thread,…
Browse files Browse the repository at this point in the history
… starting redis-server in views
  • Loading branch information
markmester committed Sep 29, 2016
1 parent 3a70fa1 commit f8449a3
Show file tree
Hide file tree
Showing 19 changed files with 9,750 additions and 101 deletions.
1 change: 0 additions & 1 deletion config.json

This file was deleted.

2 changes: 2 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
roku_config:
host: 192.168.2.12
Binary file added dump.rdb
Binary file not shown.
78 changes: 78 additions & 0 deletions locate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import socket
import re
from utils import update_config
from functools import wraps
import signal

"""
Module for locating roku device and saving device stats to redis store
"""

# raises TimeoutError after user specified timeout
class TimeoutError(Exception): pass
def timeout(seconds, error_message = 'Function call timed out'):
def decorated(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)

def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result

return wraps(func)(wrapper)

return decorated

############ Locate Roku Devices #################

# @timeout(10)
def locate_device():
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
"HOST: 239.255.255.250:1900\r\n" + \
"Man: \"ssdp:discover\"\r\n" + \
"MX: 5\r\n" + \
"ST: roku:ecp\r\n\r\n"

socket.setdefaulttimeout(10)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.sendto(ssdpRequest, ("239.255.255.250", 1900))

msg = {
'success': False,
'data': None
}

while True:
try:
resp = sock.recv(1024)
match = re.match(r'.*USN: uuid:roku:ecp:([\w\d]{12}).*LOCATION: http://([\d\.]*):(\d*)', resp, re.S)
host = match.group(2)
port = match.group(3)

# update config
update_config({'host': host, 'port': int(port), 'connected': 1})

# update resp
msg.update({'success': True, 'data': resp})

break

except socket.timeout:
update_config({'connected': 0})
continue

except KeyboardInterrupt:
print 'Exiting...'
break

return msg

if __name__ == '__main__':
locate_device()
146 changes: 46 additions & 100 deletions roku.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,8 @@
#!/usr/bin/python
import sys
import socket
import re
import json

############ Locate Roku Devices #################
def locate_device():
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
"HOST: 239.255.255.250:1900\r\n" + \
"Man: \"ssdp:discover\"\r\n" + \
"MX: 5\r\n" + \
"ST: roku:ecp\r\n\r\n"

socket.setdefaulttimeout(10)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.sendto(ssdpRequest, ("239.255.255.250", 1900))

while True:
try:
resp = sock.recv(1024)
match = re.match(r'.*USN: uuid:roku:ecp:([\w\d]{12}).*LOCATION: http://([\d\.]*):(\d*)', resp, re.S)
host = match.group(2)
port = match.group(3)

# update config
update_config({'host': host, 'port': int(port)})
success = True
break

except: # socket.timeout:
success = False
from utils import get_config, socket_request


# create api response
msg = {
'success': success,
'data': resp
}

return msg

############ Socket requests helper ##########
def socket_request(method, host, port, path):
# establish connection and send request
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
sock.send("{0} {1} HTTP/1.0\r\nHost: {2}\r\n\r\n".format(method.upper(), path, host))
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(1)

# receive resp
data = sock.recv(1024)
resp = ""
while len(data):
resp += data
data = sock.recv(1024)
sock.close()

# create api response
msg = {
"success": False,
"data": resp
}

if 'HTTP/1.1 200 OK' in resp:
msg['success'] = True

return msg

################# Helpers ####################
def update_config(updates):
with open('config.json', 'r+') as f:
try:
config = json.load(f)
except ValueError:
config = {}

config.update(updates)
f.seek(0)
f.write(json.dumps(config))
f.truncate()

def get_config():
with open('config.json', 'r') as f:
config = json.load(f)
return config['host'], config['port']

################# resources ###################
################# roku api calls ###################

def home():
host, port = get_config()
Expand All @@ -106,27 +18,61 @@ def query_apps():

return socket_request(method, host, port, path)

def find_netflix():
def active_app():
host, port = get_config()
method = 'get'
path = '/query/active-app'

return socket_request(method, host, port, path)

def find_app(app_name):
success = False
netflix_id = None
id = None
resp = query_apps()['data']
netflix_id_match = re.search('<app id="(\d*)".*Netflix</app>', resp)
app_name = app_name[0].upper() + app_name[1:]
regex = r"<app id=\"(\d*)\".*{}</app>".format(re.escape(app_name))
id_match = re.search(regex, resp)

if netflix_id_match:
if id_match:
success = True
netflix_id = netflix_id_match.group(1)
id = id_match.group(1)

# create api response
msg = {
"success": success,
"data": {'resp': resp, 'id': netflix_id}
"data": {'resp': resp, 'id': id}
}

return msg

def lauch_app(id):
host, port = get_config()
method = 'post'
path = '/launch/{}'.format(id)

############## entry -- test only ##############
if __name__ == '__main__':
return socket_request(method, host, port, path)
# todo: need to do something after this i.e. paly most recety played

locate_device()
print find_netflix()['data']['id']
# if in app, pauses/un-pauses ap
def play_pause():
host, port = get_config()
method = 'post'
path = '/keypress/Select'

return socket_request(method, host, port, path)



############## entry - test only ##############
if __name__ == '__main__':
pass
# home()
# locate_device()
# netflix_id = find_app('Netflix')['data']['id']
# print lauch_app(netflix_id)
# print play_pause()
#
# pandora_id = find_app('pandora')['data']['id']
# print lauch_app(pandora_id)

# print play_pause()
Loading

0 comments on commit f8449a3

Please sign in to comment.