Created
February 18, 2018 19:32
-
-
Save GlennPegden2/44ff23085b1a5c3b785b38b5149c598c to your computer and use it in GitHub Desktop.
My handler code that sits between fauxmo and LGTV / XBox One / SkyQ to enabled Alexa to control the devices.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
#Must be python2 as lgtv currently doesn't work with python3 | |
#Uses the following (make sure they are installed and set up | |
#Sky-Q - https://github.com/dalhundal/sky-remote-cli (NodeJS) - I also ported his node sample for the getPower code | |
#LGTV - https://github.com/klattimer/LGWebOSRemote (Python) | |
# | |
#Credit to Schamper for the code the xbox remote-wake code is based on - https://github.com/Schamper/xbox-remote-power | |
from types import FunctionType | |
from inspect import getargspec | |
from optparse import OptionParser | |
import json | |
import time | |
import socket | |
import subprocess | |
import re | |
import os | |
import sys | |
import urllib | |
import lgtv | |
import select | |
ip = {'tv': '192.168.95.9' ,'skyQ': '192.168.95.10', 'xboxOne': '192.168.95.6'} | |
#See Schampers link above for how to find this | |
xboxLiveId = 'YOURIDHERE' | |
LGWebOSRemoteFolder = "/home/pi/amazon-alexa-lg-tv" | |
TVChannelNames = { | |
'plex': 'startApp=cdp-30', | |
'prime': 'startApp=lovefilm', | |
'iplayer': 'startApp=bbc.iplayer.3.0', | |
'skyq': 'setInput=HDMI_1', | |
'xboxone': 'setInput=HDMI_2', | |
'pc': 'setInput=HDMI_3', | |
'steam': 'setInput=HDMI_3', | |
'wii': 'setInput=AV_1', | |
'youtube': 'startApp=xxxx', | |
'youtubekids': 'startApp=xxxx', | |
'bbc1': 'sky=1 0 1', | |
'bbc2': 'sky=1 0 2', | |
'itv': 'sky=1 0 3', | |
'channel4': 'sky=1 0 4', | |
'channel5': 'sky=1 0 5', | |
'sky1': 'sky=1 0 6', | |
'skyatlantic': 'sky=1 0 8', | |
'syfi': 'sky=1 1 4', | |
'dave': 'sky=1 1 1', | |
'home': 'sky=home' | |
} | |
def runExternal(cmd): | |
print ("Running cmd " +cmd) | |
child = subprocess.Popen(cmd,stdout=subprocess.PIPE,shell=True) | |
streamdata = child.communicate()[0] | |
rc = True if child.returncode is 0 else False | |
return rc | |
def setXboxOn(): | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
s.setblocking(0) | |
s.bind(("", 0)) | |
s.connect((ip['xboxOne'], 5050)) | |
power_payload = b'\x00' + chr(len(xboxLiveId)).encode() + xboxLiveId + b'\x00' | |
power_header = b'\xdd\x02\x00' + chr(len(power_payload)).encode() + b'\x00\x00' | |
power_packet = power_header + power_payload | |
#print("Sending " + str(power_packet) + " to " + ip['xboxOne'] + ":5050)") | |
for x in range(0, 10): | |
s.send(power_packet) | |
if getXboxPower() == True: | |
break | |
print(".") | |
s.close() | |
def getXboxPower(): | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
s.setblocking(0) | |
s.bind(("", 0)) | |
s.connect((ip['xboxOne'], 5050)) | |
s.send(bytearray.fromhex("dd00000a000000000000000400000002")) | |
ping_result = select.select([s], [], [], 5)[0] | |
s.close() | |
return True if ping_result else False | |
def getSkyQAwake(): | |
# with urllib.request.urlopen("http://" + ip["skyQ"] + ":9006/as/system/information") as url: | |
# data = json.loads(url.read().decode()) | |
response = urllib.urlopen("http://" + ip["skyQ"] + ":9006/as/system/information") | |
data = json.loads(response.read()) | |
rc = not data["activeStandby"] | |
return rc | |
def getPower(device): | |
print("Getting " + device + " power state ") | |
if device.lower() == 'tv': | |
shellCMD = "ping -c 1 " + ip[device] + " 2>&1 >/dev/null" | |
rc = True if os.system(shellCMD) is 0 else False | |
elif device.lower() == 'xboxone': | |
rc = getXboxPower() | |
elif device.lower() == 'skyq': | |
rc = getSkyQAwake() | |
else: | |
print("Unknown device " + device) | |
rc = -1 | |
print(device + " power status is " +str(rc)) | |
return rc | |
def setPower(device,state): | |
print("Setting " + device + " power to " + state) | |
powerState = getPower(device) | |
currentState = "on" if powerState == True else "off" | |
if currentState != state: | |
if device.lower() == 'tv': | |
rc = runExternal("python "+LGWebOSRemoteFolder+"/lgtv.py " + state) | |
elif device.lower() == 'skyq': | |
rc = runExternal("$(npm bin)/sky-remote-cli " + ip[device] + " power") | |
elif device.lower() == 'xboxone': | |
# cmd = "python ./xbox-remote-power/xbox-remote-power.py -a " + ip['xboxOne'] + " -i " + xboxLiveId | |
if state.lower() == 'on': | |
rc = setXboxOn() | |
#Stupid router won't allow raw socket to connection to something not in the arp table | |
#No longer needed as getPower pings it which has the same effect | |
# os.system("sudo ./addXBOXarp.sh" | |
else: | |
print("xbox one does not support remote turn off") | |
else: | |
print("Unknown Device " + device) | |
return -1 | |
else: | |
print (device + " is already " + state) | |
return powerState | |
wantedStatus = True if state == 'on' else False | |
chkCntr = 0 | |
while True: | |
rc = getPower(device) | |
# print("power status is " + str(rc) + " wantedStatus is " + str(wantedStatus) + " state is " + state) | |
if rc == wantedStatus: | |
break | |
elif chkCntr >= 5: | |
break | |
else: | |
time.sleep(2) | |
print ("Power status has not yet changed from " + currentState + " to " + state ) | |
chkCntr += 1 | |
if rc != wantedStatus: | |
print("Cant seem to set " + device + " to " + state) | |
print(device + " status is now " + str(rc)) | |
return rc | |
def getTVChannel(): | |
#Actually it seems like the LGTV will only tell you the current tuner channel, not input source, or running app | |
bob = subprocess.check_output(["python", "./lgtv.py", "listApps"]) | |
print(bob) | |
def setTVChannel(param): | |
if param in TVChannelNames: | |
channelType,channel = TVChannelNames[param].split("=") | |
print("Channel name found in lookup table. Converting " + param + " to " + channelType + " " + channel) | |
elif '=' in param: | |
channelType,channel = param.split("=") | |
else: | |
print("Parameter must me a configured channel name or of the format <channelType>=<channelID>, e.g. setInput=HDMI_1") | |
return -1 | |
if getPower('tv') == False: | |
setPower('tv','on') | |
print("Turning TV to Channel " + channel + " using " + channelType + ")" ) | |
if channelType.lower() == 'sky': | |
child = subprocess.Popen("$(npm bin)/sky-remote-cli " + ip[device] + " " + channel,stdout=subprocess.PIPE,shell=True) | |
else: | |
child = subprocess.Popen("python " + LGWebOSRemoteFolder +"/lgtv.py " + channelType + " " + channel,stdout=subprocess.PIPE,shell=True) | |
streamdata = child.communicate()[0] | |
rc = child.returncode | |
return rc | |
parser = OptionParser(usage="%prog -c <command> -d <device> -s <param>", version="%prog 1.0") | |
parser.add_option("-c", "--command", dest="command", help="command to execute") | |
parser.add_option("-d", "--device",action="store", dest="device",help="device to send action to") | |
parser.add_option("-p", "--param",action="store", dest="param",help="param to pass") | |
(options, args) = parser.parse_args() | |
if options.command.lower() == 'setpower': | |
setPower(options.device,options.param) | |
elif options.command.lower() =='getpower': | |
getPower(options.device) | |
#elif options.command == 'getTVChannel': | |
# getTVChannel() | |
elif options.command.lower() == 'settvchannel': | |
setTVChannel(options.param) | |
elif options.command: | |
print("Unknown command" + options.command) | |
else: | |
print("No command given, try python ghc.py -help") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
From memory, this was designed to be triggerd by fauxmo