[WORKAROUND] LG TV Remote

Solved

[attachment=0]2851_422_220.png[/attachment]

May I ask how you solved this one? Will it work with older non webos-based smart-TVs from LG?

Sure. This is what I did. And it works for pre-WebOS models.
Particular script I use is for models released in 2011. Also have script for 2012 models.

In configuration.yaml add
[color=#0080FF][b]
script.powerofftv:
hidden: true
script.powerontv:
hidden: true

#################################################################

Shell commands calling tv.py for LG TV

#################################################################

Exposes service shell_command.wol_bd_br

shell_command:
wol_bd_br: wakeonlan EE:BB:BB:EE:00:FF
change_to_tv: python3 /home/pi/.homeassistant/tv.py 15
power_off_tv: python3 /home/pi/.homeassistant/tv.py 8

State TV

input_boolean:
television:
name: Bedroom
initial: off
icon: mdi:television

group:
Television:

  • input_boolean.television

#################################################################

Automations Television

#################################################################

automation 3:

  • alias: ‘turn on tv’
    trigger:
    platform: state
    entity_id: input_boolean.television
    state: ‘on’
    action:
    service: script.powerontv

  • alias: ‘turn off tv’
    trigger:
    platform: state
    entity_id: input_boolean.television
    from: ‘on’
    to: ‘off’
    action:
    service: script.powerofftv

#################################################################

Scripts

#################################################################

script:

Turns on the TV and then changes input 20 seconds later

powerontv:
alias: poweron_tv
sequence:
- alias: ‘turn on tv’
service: shell_command.wol_bd_br
- delay:
seconds: 20
- alias: ‘change input to tv’
service: shell_command.change_to_tv

Turns TV off

powerofftv:
alias: poweroff_tv
sequence:
- alias: ‘turn off tv’
service: shell_command.power_off_tv[/b][/color]

In the tv.py add your pairing key and your good to go. Of course restart HASS

This is the tv.py script.

[color=#0080FF]#!/usr/bin/env python3

import http.client
from tkinter import *
import xml.etree.ElementTree as etree
import socket
import re
import sys
lgtv = {}
dialogMsg =""
headers = {“Content-Type”: “application/atom+xml”}
lgtv[“pairingKey”] = “DDDDDD

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 = “<?xml version=\"1.0\" encoding=\"utf-8\"?>AuthKeyReq”
conn.request(“POST”, “/hdcp/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 = “<?xml version=\"1.0\" encoding=\"utf-8\"?>AuthReq”
+ lgtv[“pairingKey”] + “”
conn.request(“POST”, “/hdcp/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()
root = Tk()
root.withdraw()
dialogMsg = “Please enter the pairing key\nyou see on your TV screen\n”
d = MyDialog(root, dialogMsg)
root.wait_window(d.top)
lgtv[“pairingKey”] = result
d.top.destroy()

def handleCommand(cmdcode):
conn = http.client.HTTPConnection( lgtv[“ipaddress”], port=8080)
cmdText = “<?xml version=\"1.0\" encoding=\"utf-8\"?>”
+ lgtv[“session”]
+ “HandleKeyInput”
+ cmdcode
+ “”
conn.request(“POST”, “/hdcp/api/dtv_wifirc”, 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)
[/color]

Until someone works the script into a component I’ll keep using this and add shell commands for volume , channel changing, etc.

Turning TV on and off via HASS was what I needed.

1 Like

Hi

My LG TV is a 2011 model too and I’m trying to make it work with homeassistant. I’d like to use your config, but the forum page messed with your copy/paste. May you attach the original files? Or upload them to some place? Thank you!

Continuing the discussion from [WORKAROUND] LG TV Remote:

Are you in the US?

I have an LG LW5600 and from what I’ve read, there is an alternate firmware which only applies to European models.

http://openlgtv.org.ru/wiki/index.php/Achievements

I’ve tried to telnet to my TV on port 1900 as shown in the script but the TV doesn’t respond.

Wow. Okay will do it again.

In configuration.yaml add

#################################################################
## Customize
#################################################################
  customize:
    script.powerofftv:
      hidden: true
    script.powerontv:
      hidden: true

**

Shell commands calling tv.py for LG TV

**

Exposes service shell_command.wol_bd_br

#################################################################
## Shell commands
#################################################################
shell_command:
 change_to_tv: python3 /home/pi/.homeassistant/tv.py 15
 power_off_tv: python3 /home/pi/.homeassistant/tv.py 8
 wol_bd_br: wakeonlan EE:BB:BB:EE:00:FF

**

State TV

**

input_boolean:
  television:
    name: Bedroom
    initial: off
    icon: mdi:television

group:
  Television:
   - input_boolean.television

**

Automations Television

**

#################################################################
## Automations
#################################################################
automation:
- alias: 'turn on tv'
  trigger:
    platform: state
    entity_id: input_boolean.television
    state: 'on'
  action:
    service: script.powerontv

- alias: 'turn off tv'
  trigger:
    platform: state
    entity_id: input_boolean.television
    state: 'off'
  action:
    service: script.powerofftv

**

Scripts

**

#################################################################
## Scripts
#################################################################
script:
  # Turns on the TV and then changes input 20 seconds later
  powerontv:
    alias: poweron_tv
    sequence:
      - alias: 'turn on tv'
        service: shell_command.wol_bd_br
      - delay:
          seconds: 20
      - alias: 'change input to tv'
        service: shell_command.change_to_tv
  powerofftv:
    alias: poweroff_tv
    sequence:
      - alias: 'turn off tv'
        service: shell_command.power_off_tv

In the tv.py add your pairing key instead of DDDDDD copy and paste the entire script into a file named tv.py and your good to go. Of course restart HA
This is the tv.py script.

#!/usr/bin/env python3

import http.client
from tkinter import *
import xml.etree.ElementTree as etree
import socket
import re
import sys
lgtv = {}
dialogMsg =""
headers = {"Content-Type": "application/atom+xml"}
lgtv["pairingKey"] = "DDDDDD"

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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><auth><type>AuthKeyReq</type></auth>"
    conn.request("POST", "/hdcp/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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><auth><type>AuthReq</type><value>" \
            + lgtv["pairingKey"] + "</value></auth>"
    conn.request("POST", "/hdcp/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()
    root = Tk()
    root.withdraw()
    dialogMsg = "Please enter the pairing key\nyou see on your TV screen\n"
    d = MyDialog(root, dialogMsg)
    root.wait_window(d.top)
    lgtv["pairingKey"] = result
    d.top.destroy()

def handleCommand(cmdcode):
    conn = http.client.HTTPConnection( lgtv["ipaddress"], port=8080)
    cmdText = "<?xml version=\"1.0\" encoding=\"utf-8\"?><command><session>" \
                + lgtv["session"]  \
                + "</session><type>HandleKeyInput</type><value>" \
                + cmdcode \
                + "</value></command>"
    conn.request("POST", "/hdcp/api/dtv_wifirc", 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)



If the LG remote apk works for you (the 2011 supporting one) then the script will work for you as well.

anyone get this script work with 2013 models? mine respond “Unauthorized” to pairing request:

POST http://10.182.22.5:8080/hdcp/api/auth HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/xml
Content-Length: 36
Host: 10.182.22.5:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<auth><type>AuthKeyReq</type></auth> 

and response:

<envelope>
   <ROAPError>401</ROAPError>
   <ROAPErrorDetail>Unauthorized</ROAPErrorDetail>
</envelope>

Try this one

#!/usr/bin/env python3

import http.client
from tkinter import *
import xml.etree.ElementTree as etree
import socket
import re
import sys
lgtv = {}
dialogMsg =""
headers = {"Content-Type": "application/atom+xml"}
lgtv["pairingKey"] = "DDDDDD"

class MyDialog:
    def __init__(self, parent, dialogMsg):
        top = self.top = Toplevel(parent)
        Label(top, text = dialogMsg, justify="left").pack()
        self.e = Entry(top)
        self.e.pack(padx=5)
        self.e.focus_set()
        b = Button(top, text="Ok", command=self.ok)
        b.pack(pady=5)
        top.bind("<Return>", self.ok)
        top.title("Lg 2012")
        top.geometry("410x280+10+10")
    def ok(self,dummy=None):
        global result
        result = self.e.get()
        self.top.destroy()


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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><auth><type>AuthKeyReq</type></auth>"
    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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><auth><type>AuthReq</type><value>" \
            + lgtv["pairingKey"] + "</value></auth>"
    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()
    root = Tk()
    root.withdraw()
    dialogMsg = "Please enter the pairing key\nyou see on your TV screen\n"
    d = MyDialog(root, dialogMsg)
    root.wait_window(d.top)
    lgtv["pairingKey"] = result
    d.top.destroy()

def handleCommand(cmdcode):
    conn = http.client.HTTPConnection( lgtv["ipaddress"], port=8080)
    cmdText = "<?xml version=\"1.0\" encoding=\"utf-8\"?><command>" \
                + "<name>HandleKeyInput</name><value>" \
                + cmdcode \
                + "</value></command>"
    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


dialogMsg =""
for lgkey in lgtv :
    dialogMsg += lgkey + ": " + lgtv[lgkey] + "\n"

dialogMsg += "Success in establishing command session\n"
dialogMsg += "=" * 28 + "\n"
dialogMsg += "Enter command code i.e. a number between 0 and 1024\n"
dialogMsg += "Enter a number greater than 1024 to quit.\n"
dialogMsg += "Some useful codes (not working with 2012 models):\n"
dialogMsg += "for EZ_ADJUST     menu enter   255 \n"
dialogMsg += "for IN START        menu enter   251 \n"
dialogMsg += "for Installation     menu enter   207 \n"
dialogMsg += "for POWER_ONLY mode enter   254 \n"
dialogMsg += "Warning: do not enter 254 if you \ndo not know what POWER_ONLY mode is. "


result = "91"
while int(result) < 1024:
    root = Tk()
    root.withdraw()
    d = MyDialog(root, dialogMsg)
    root.wait_window(d.top)
    handleCommand(result)

thanks, this one works for me

You’re welcome

Has anyone gotten this to work with a WebOS 2014 model?

Not sure what’s included and it doesn’t specify the year, but there are separate steps for LG WebOS

Media Player might have power options

I’ve set both of those up but no power on option. It’s just pause/play but it can turn it off. I’d like to remotely be able to turn it on though. I was thinking something to do with wake on LAN?

hi all… i followed this manual but unfortunately nothing works with it… how could i figure out whats wrong ? I have a 2011 model and remote apk works pretty good on my phone.

Yes, you need to use wake on lan to power on a Web OS tv. Make sure you enable waking the tv in the settings menu of the tv.

You could solve this without shell scripts with the native wake_on_lan component of homeassistant. I did the following:

  1. Define a media player for the webos tv. This will allow you to get the state of the tv and turn it off.
  2. Define a switch which updates itself with the current power state of the tv: tv_switch. Use this to turn the tv on or off. E.g. expose it to homebridge as ‘TV’ if you want to control the tv with Siri.
  3. Define a (hidden) switch to send the wake-on-lan packet when the tv_switch is turned on.

To switch sources, use the media_player.select_source service.

Btw, I haven’t figured out is how to detect when the tv is off but recording.

Switches config:

- platform: template
  switches:
    tv_switch:
      friendly_name: TV
      value_template: '{{ states.media_player.lg_webos_smart_tv.state != "off" }}'
      turn_on:
        service: switch.turn_on
        entity_id: switch.wol_lg_tv_eth
      turn_off:
        service: media_player.turn_off
        entity_id: media_player.lg_webos_smart_tv
      entity_id: media_player.lg_webos_smart_tv

- platform: wake_on_lan
  name: wol_lg_tv_eth
  mac_address: '00-08-00-00-00-00'
  host: 192.168.1.123 #Irrelevant, will monitor the state of host to set the switch state.
  turn_off:
    service: switch.turn_off
    entity_id: switch.tv_switch

Hello there the stuff written on top is for 2011 and 2012 models. Basically pre 2012 models.
As they don’t have WOL you would have to wake your TV via connected device like Bluray player that supports LAN (for WOL.)

You then wake the Bluray player that in turns wakes the TV through Simlink.

Then you can use the above work around to chance channels turn volume up and down or turn it off.
I posted more key commands here

You should just use the LG TV Component

Did you created the tv.py to be called?