Skip to content

Instantly share code, notes, and snippets.

@evadne
Last active December 29, 2022 22:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evadne/409ef043b35dfd58a0cc3b933d78067c to your computer and use it in GitHub Desktop.
Save evadne/409ef043b35dfd58a0cc3b933d78067c to your computer and use it in GitHub Desktop.
LG Digital Signage External Control
#!/usr/bin/env python3
import re
import select
import socket
import sys
class WakeOnLan:
def __init__(self, mac, broadcast_ip = "255.255.255.255", broadcast_port = 9):
self.mac_bytes = bytes.fromhex(re.sub('[^a-z0-9]', '', mac))
self.broadcast_ip = broadcast_ip
self.broadcast_port = 9
def send(self):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as wakeup_socket:
wakeup_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
wakeup_socket.connect((self.broadcast_ip, self.broadcast_port))
wakeup_packet = 6 * b'\xff' + 16 * self.mac_bytes
wakeup_socket.send(wakeup_packet)
class Display:
def __init__(self, host, port):
self.host = host
self.port = port
self.timeout = 2
def wake(self, mac):
tries = 0
max_tries = 6
while tries < max_tries:
WakeOnLan(mac).send()
display_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
display_socket.settimeout(5)
try:
display_socket.connect((self.host, self.port))
return True
except:
tries = tries + 1
return False
def send(self, command, result_bytes):
display_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
display_socket.settimeout(self.timeout)
display_socket.connect((self.host, self.port))
display_socket.setblocking(self.timeout)
display_socket.sendall(command)
if select.select([display_socket], [], [], self.timeout)[0]:
data = display_socket.recv(result_bytes)
display_socket.close()
return data
else:
raise Exception("timeout")
Source = {
b'20': 'AV',
b'40': 'COMPONENT',
b'60': 'RGB',
b'70': 'DVI-D (PC)',
b'80': 'DVI-D (DTV)',
b'90': 'HDMI1 (DTV)',
b'91': 'HDMI2 (DTV)',
b'92': 'OPS/HDMI3/DVI-D (DTV)',
b'95': 'OPS/DVI-D (DTV)',
b'96': 'HDMI3/DVI-D (DTV)',
b'97': 'HDMI3/HDMI2/DVI-D (DTV)',
b'98': 'OPS (DTV)',
b'99': 'HDMI2/OPS (DTV)',
b'A0': 'HDMI1 (PC)',
b'A1': 'HDMI2 (PC)',
b'A2': 'OPS/HDMI3/DVI-D (PC)',
b'A5': 'OPS/DVI-D (PC)',
b'A6': 'HDMI3/DVI-D (PC)',
b'A7': 'HDMI3/HDMI2/DVI-D (PC)',
b'A8': 'OPS (PC)',
b'A9': 'HDMI2/OPS (PC)',
b'C0': 'DISPLAYPORT (DTV)',
b'C1': 'DISPLAYPORT/USB-C (DTV)',
b'C2': 'HDMI3 (DTV)',
b'C3': 'HDBaseT (DTV)',
b'D0': 'DISPLAYPORT (PC)',
b'D1': 'DISPLAYPORT/USB-C (PC)',
b'D2': 'HDMI3 (PC)',
b'D3': 'HDBaseT (PC)',
b'E0': 'SuperSign webOS Player',
b'E1': 'Others',
b'E2': 'Multi Screen',
b'E3': 'Play via URL'
}
SourceShorthand = {
'HDMI 1': 'HDMI1 (PC)',
'HDMI 2': 'HDMI2 (DTV)',
'HDMI 3': 'OPS/HDMI3/DVI-D (PC)',
'DisplayPort': 'DISPLAYPORT (PC)'
}
display = Display(sys.argv[1], 9761)
if sys.argv[2] == 'turn_on':
try:
display.send(b'ka 0 01\r', 9)
except OSError:
result = display.wake(sys.argv[3])
if result == False:
raise Exception("Unable to wake up display via Wake-on-LAN")
display.send(b'ka 0 01\r', 9)
display.send(b'kd 0 00\r', 9)
elif sys.argv[2] == 'turn_off':
display.send(b'ka 0 00\r', 9)
elif sys.argv[2] == 'get_power_status':
try:
response = display.send(b'kd 0 ff\r', 9)
if response == b'd 01 OK00':
print('on')
elif response == b'd 01 OK01':
print('standby')
except Exception as e:
print('off')
elif sys.argv[2] == 'get_source':
try:
response = display.send(b'xb 0 ff\r', 9)
code = response[-2::1]
try:
source = Source[code]
except KeyError:
source = Source[code.decode("utf-8").upper().encode()]
if source in SourceShorthand.values():
source = dict((v, k) for k, v in SourceShorthand.items()).get(source)
print(source)
except:
print('off')
elif sys.argv[2] == 'get_signal_status':
try:
response = display.send(b'sv 0 02 ff\r', 11)
response = response[-4::1]
if response == b'0201':
print('active')
elif response == b'0200':
print('inactive')
except:
print('off')
elif sys.argv[2] == 'set_power_status':
value = sys.argv[3].strip()
if value == 'on':
display.send(b'ka 0 01\r', 9)
display.send(b'kd 0 00\r', 9)
elif value == 'standby':
display.send(b'kd 0 01\r', 9)
elif value == 'off':
display.send(b'ka 0 00\r', 9)
elif sys.argv[2] == 'set_source':
value = sys.argv[3].strip()
if value in SourceShorthand.keys():
value = SourceShorthand.get(value)
code = dict((v, k) for k, v in Source.items()).get(value)
command = b'xb 0 ' + code + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_image_contrast':
code = "{:02x}".format(int(sys.argv[3]))
command = b'kg 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_image_brightness':
code = "{:02x}".format(int(sys.argv[3]))
command = b'kh 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_image_sharpness':
code = "{:02x}".format(int(sys.argv[3]))
command = b'kk 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_image_colour':
code = "{:02x}".format(int(sys.argv[3]))
command = b'ki 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_image_tint':
code = "{:02x}".format(int(sys.argv[3]))
command = b'kj 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_colour_temperature':
code = "{:02x}".format(int(112 + ((int(sys.argv[3]) - 3200) / 100)))
command = b'xu 0 ' + code.encode() + b'\r'
display.send(command, 9)
elif sys.argv[2] == 'set_backlight':
code = "{:02x}".format(int(sys.argv[3]))
command = b'mg 0 ' + code.encode() + b'\r'
display.send(command, 9)
else:
raise Exception("bad_command")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment