Martin's corner on the web

Control LG smart TV over the Internet using a Raspberry Pi

Here is a quick home automation project: controlling a LG smart TV over the Internet using a Raspberry Pi computer. There is a Python script already developed for that, so getting all this working was a 5 minute effort. The original script uses graphical UI (hence requires a display attached, or running the script in X), while I wanted to run the commands from the shell. I stripped out the GUI part of the script and was left with this:

#!/usr/bin/env python3
#Full list of commands
#http://developer.lgappstv.com/TV_HELP/index.jsp?topic=%2Flge.tvsdk.references.book%2Fhtml%2FUDAP%2FUDAP%2FAnnex+A+Table+of+virtual+key+codes+on+remote+Controller.htm
import http.client
import xml.etree.ElementTree as etree
import socket
import re
import sys
lgtv = {}
dialogMsg =""
headers = {"Content-Type": "application/atom+xml"}
lgtv["pairingKey"] = "939781"
def getip():
    strngtoXmit =   'M-SEARCH * HTTP/1.1' + '\r\n' + \
                    'HOST: 239.255.255.250:1900'  + '\r\n' + \
                    'MAN: "ssdp:discover"'  + '\r\n' + \
                    'MX: 2'  + '\r\n' + \
                    'ST: urn:schemas-upnp-org:device:MediaRenderer:1'  + '\r\n' +  '\r\n'
    bytestoXmit = strngtoXmit.encode()
    sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
    sock.settimeout(3)
    found = False
    gotstr = 'notyet'
    i = 0
    ipaddress = None
    sock.sendto( bytestoXmit,  ('239.255.255.250', 1900 ) )
    while not found and i <= 5 and gotstr == 'notyet':
        try:
            gotbytes, addressport = sock.recvfrom(512)
            gotstr = gotbytes.decode()
        except:
            i += 1
            sock.sendto( bytestoXmit, ( '239.255.255.250', 1900 ) )
        if re.search('LG', gotstr):
            ipaddress, _ = addressport
            found = True
        else:
            gotstr = 'notyet'
        i += 1
    sock.close()
    if not found : sys.exit("Lg TV not found")
    return ipaddress
def displayKey():
    conn = http.client.HTTPConnection( lgtv["ipaddress"], port=8080)
    reqKey = "AuthKeyReq"
    conn.request("POST", "/roap/api/auth", reqKey, headers=headers)
    httpResponse = conn.getresponse()
    if httpResponse.reason != "OK" : sys.exit("Network error")
    return httpResponse.reason
def getSessionid():
    conn = http.client.HTTPConnection( lgtv["ipaddress"], port=8080)
    pairCmd = "AuthReq" \
            + lgtv["pairingKey"] + ""
    conn.request("POST", "/roap/api/auth", pairCmd, headers=headers)
    httpResponse = conn.getresponse()
    if httpResponse.reason != "OK" : return httpResponse.reason
    tree = etree.XML(httpResponse.read())
    return tree.find('session').text
def getPairingKey():
    displayKey()
def handleCommand(cmdcode):
    conn = http.client.HTTPConnection( lgtv["ipaddress"], port=8080)
    cmdText = "" \
                + "HandleKeyInput" \
                + cmdcode \
                + ""
    conn.request("POST", "/roap/api/command", cmdText, headers=headers)
    httpResponse = conn.getresponse()
#main()
lgtv["ipaddress"] = getip()
theSessionid = getSessionid()
while theSessionid == "Unauthorized" :
    getPairingKey()
    theSessionid = getSessionid()
if len(theSessionid) < 8 : sys.exit("Could not get Session Id: " + theSessionid)
lgtv["session"] = theSessionid
#displayKey()
result = str(sys.argv[1])
handleCommand(result)

Both the TV and the Raspberry Pi need to be on the same network for this to work. You can then SSH to your Pi from anywhere and remotely control the TV. To pair the Raspberry Pi with the TV, you should run the script:

python3 lg.py

The pairing key will be displayed on the TV , write it in the line that reads:

lgtv["pairingKey"] = "939781"

That’s basically it, now you can use the script by passing a command number as a parameter, the below will switch off the TV:

python3 lg.py 1

Few popular codes:

    POWER_OFF=1
    3D=400
    ARROW_DOWN=2
    ARROW_LEFT=3
    ARROW_RIGHT=4
    BACK=23
    BLUE=29
    BTN_1=5
    BTN_2=6
    BTN_3=7
    BTN_4=8
    CH_DOWN=28
    CH_UP=27
    ENTER=20
    EXIT=412
    EXTERNAL_INPUT=47
    GREEN=30
    HOME=21
    MUTE=26
    MYAPPS=417
    NETCAST=408
    PAUSE=34
    PLAY=33
    PREV_CHANNEL=403
    RED=31
    STOP=35
    VOL_DOWN=25
    VOL_UP=24
    YELLOW=32

A full list of the command codes is available here

My TV is a 2012 model, the keys may be different for older models. Check the original script for older key codes.

A possible use of this would be to briefly switch TV’s input to the Raspberry Pi upon certain events like showing the picture of the person ringing my doorbell or displaying home automation alerts when my my attention is needed. I can imagine people using the script for pranks as well 🙂

 

15 thoughts on “Control LG smart TV over the Internet using a Raspberry Pi

  1. Paul

    I’m having problems writing this file to /usr/bin/env on my Raspberry Pi, I get
    -bash: cd: env: Not a directory
    If I put the file in /usr/bin/ and try to call the command ‘python3 lg.py’ I get;
    -bash: /usr/bin/python3: Permission denied
    despite having made the file executable – ‘sudo chmod +x python3’

    Paul

    1. drJeckyll

      Change shebang from:
      #!/usr/bin/env python3
      to
      #!/usr/bin/python3
      or full path to python3 executable.
      Then you need to make script executable, not python3 which you did.
      chmod 755 lg.py
      or
      chmod a+x lg.py
      Then you can run script like this: ./lg.py

      If you choose to run script with ‘python3 lg.py’ then you can discard shebang (remove first line). This line is used just to show what follows (python3 script in your case) and what interpreter to be used. Also in this case you don’t need to make it executable just readable – mode 644 for example.

      Cheers!

  2. Paul

    I’m running python v2.7.3 on my Rasp Pi and it’s installed at /usr/bin/python so I’ve followed the advice above, but when I run $ python lg.py I get the error;

    File “lg.py”, line 41
    found = True
    ^
    IndentationError: unexpected indent

    Does this only run with python v3??

    Paul

    1. Martin Post author

      Paul, when copying the code over to the post, I accidentally inserted two extra spaces before line #40 ipaddress, _ = addressport, it should be the same indent as the next line.

      I have corrected that in my post now and tested it working.

      It is probably possible to get this running on earlier versions of Python, that may include installing libraries that are included by default in Python 3.

  3. Pingback: LG TV Script | Slipsystem

    1. Martin Post author

      Central home automation node works better for me, I have way too many arduinos all over the house already and am trying to consolidate them

  4. Hannes

    I have a little problem with your code.
    i want to type the commends over the shell, but when I use your script, it says “Network error”. if I use the original one with GUI, it is working.

    PS: I want to start the skript out of another Python skript i wrote, but i can´t write a parameter at the end of the command. (python3 lg.py 1 #power of).

    Thank you
    Hannes

  5. Pingback: You watch too much TV | Martin's corner on the web

  6. Txerra

    Hi, there are two mistakes in the script:

    Line 42: if not found : sys.exit(“Lg TV not found”B)
    To:
    Line 42: if not found : sys.exit(“Lg TV not found”)

    And:
    Lines 64-67:

    cmdText = “” \
    + “HandleKeyInput” \
    + cmdcode \
    + “”

    TO:

    cmdText = “” \
    + “HandleKeyInput” \
    + cmdcode \
    + “”