#!/usr/bin/env python """ Controls LG-37Z55 TV via serial (pyserial). I expect other LG TVs will work without much problems Access via USB-to-serial gateways should work fine. Usage example: >>> import tv_control >>> tv = tv_control.tv(port=0) # also try port="/dev/ttyUSB1" for USB->serial >>> tv.on() >>> print tv.ison() True >>> tv.onoff() >>> print tv.ison() False Requires pyserial. $Header: /home/martin/bin/RCS/tv_control.py,v 2.0 2007/06/12 02:56:02 martin Exp $ Copyright 2007 Martin T. Dengler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at http://www.fsf.org/licensing/licenses/gpl.html for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import os import serial import sys import time SOURCE_TV = 10 SOURCE_AV1 = 20 SOURCE_AV2 = 21 SOURCE_AV3 = 22 SOURCE_SVIDEO = 30 SOURCE_COMPONENT = 40 SOURCE_RGB_PC = 50 SOURCE_HDMI_PC = 90 STATUS_POWER_OFF = 0 STATUS_POWER_ON = 1 CMD_POWER_ON = ["k", "a", "01"] CMD_POWER_OFF = ["k", "a", "00"] CMD_POWER_STATUS = ["k", "a", "FF"] CMD_INPUT_SELECT = ["x", "b"] CMD_MUTE_SELECT = ["k", "e"] CMD_VOLUME_SELECT = ["k", "f"] CMD_VOLUME_STATUS = ["k", "f", "FF"] PORT = "/dev/ttyUSB1" SET_ID = "01" RESPONSE_SIZE = 12 class tv: def __init__(self, port=PORT, set_id=SET_ID, response_size=RESPONSE_SIZE): self.port = port self.set_id = set_id self.response_size = response_size self.verbose = False def __input(self, source=SOURCE_HDMI_PC): sub_cmd, set_id, status = self.__tv_command(CMD_POWER_STATUS, pauseafter=False) if status == STATUS_POWER_ON: sub_cmd, set_id, status = self.__tv_command(CMD_INPUT_SELECT + [source]) return status def __log(self, s): if self.verbose: print s def __tv_command(self, cmd, pauseafter=True): assert(len(cmd) == 3) self.__log("got cmd: %s" % str(cmd)) ser = serial.Serial(self.port, 9600, timeout=1) try: ser.open() except serial.serialutil.SerialException: pass # usually ignorable ser.write("%s%s %s %s\r" % (cmd[0], cmd[1], self.set_id, cmd[2])) data = ser.read(self.response_size) assert(len(data) == self.response_size), \ "invalid length response received; should've been %s but was %s (%s)" \ % (self.response_size, len(data), data) result = [data[:1], #sub-command #space data[2:4], #set id #space #"OK" data[7:9], #(the real) data #"x" #CR #LF ] self.__log("raw data was %s; result %s" % (data, result)) result[2] = eval("0x" + result[2]) # return proper hex values ser.close() if pauseafter: self.pause() # most commands cannot be issued in rapid succession return result def ison(self): sub_cmd, set_id, status = self.__tv_command( CMD_POWER_STATUS, pauseafter=False) if status == STATUS_POWER_ON: print "On" else: print "Off" return status def off(self): sub_cmd, set_id, newstatus = self.__tv_command(CMD_POWER_OFF) return newstatus def on(self): sub_cmd, set_id, newstatus = self.__tv_command(CMD_POWER_ON) return newstatus def onoff(self): if self.ison(): sub_cmd, set_id, newstatus = self.__tv_command(CMD_POWER_OFF) else: sub_cmd, set_id, newstatus = self.__tv_command(CMD_POWER_ON) return newstatus def input_tv(self): return self.__input(source=SOURCE_TV) def input_av1(self): return self.__input(source=SOURCE_AV1) def input_av2(self): return self.__input(source=SOURCE_AV2) def input_av3(self): return self.__input(source=SOURCE_AV3) def input_svideo(self): return self.__input(source=SOURCE_SVIDEO) def input_component(self): return self.__input(source=SOURCE_COMPONENT) def input_rgb(self): return self.__input(source=SOURCE_RGB_PC) def input_hdmi(self): return self.__input(source=SOURCE_HDMI_PC) def input_dvd(self): return self.input_av1() def input_pc(self): return self.input_hdmi() def input_wii(self): return self.input_svideo() def mute(self, nosound=True): return self.__tv_command(CMD_MUTE_SELECT + [nosound and "00" or "01"], pauseafter=False)[2] def volume(self, level=10): """valid levels are between 0 and 100 (decimal)""" if not isinstance(level, int): level = int(level) return self.__tv_command(CMD_VOLUME_SELECT + ["%0x" % level], pauseafter=False)[2] def volumestatus(self): print self.__tv_command(CMD_VOLUME_STATUS, pauseafter=False)[2] def __deprecated_command(self, s, pauseafter=True): """only an example of the primitive way of doing it""" cmd = '/bin/echo -e "%s\\n" > %s' % (s, self.port) print "executing: %s" % (cmd.strip()) os.system(cmd) if pauseafter: self.pause() # most commands cannot be issued in rapid succession def pause(self, seconds=5): self.__log("pausing") time.sleep(seconds) def execute(self, cmds): lastretval = 0 for cmd in cmds: args = [] if "=" in cmd: cmd, argstring = cmd.split("=") args = argstring.split() if hasattr(self, cmd): lastretval = getattr(self, cmd)(*args) return lastretval """ Documentation from various sources. Includes unimplemented commands. Baud rate : 9600 bps (UART) * [Command 1]: First command. (k, x or m) Data length : 8 bits * [Command 2]: Second command. Parity : None * [Set ID]: You can adjust the set ID to choose desired monitor Stop bit : 1 bit ID number in Special menu. Adjustment range is 1 Communication code : ASCII code ~ 99. When selecting Set ID 0, every connected * Use a crossed (reverse) cable. TV set is controlled. Set ID is indicated as decimal (1~99) on menu and as Hexa decimal (0x0~0x63) on transmission/receiving protocol. Command Reference List * [DATA]: To transmit command data. Transmit FF data to read status of command. COMMAND COMMAND DATA * [Cr]: Carriage Return (Hexa) 2 1 ASCII code 0x0D 01. Power k a 0~1 * [ ]: ASCII code space (0x20) 02. Input Select (Main Picture) x b * OK Acknowledgement 03. Aspect Ratio k c 0~6 04. Screen Mute k d 0~1 [Command2][ ][Set ID][ ][OK][Data][x] 05. Volume Mute k e 0~1 * The Monitor transmits ACK (acknowledgement) based on 06. Volume Control k f 0 ~ 64 this format when receiving normal data. At this time, if the 07. Contrast k g 0 ~ 64 data is data read mode, it indicates present status data. If 08. Brightness k h 0 ~ 64 the data is data write mode, it returns the data of the PC computer. 09. Colour k i 0 ~ 64 10. Tint k j 0 ~ 64 Error Acknowledgement 11. Sharpness k k 0 ~ 64 12. OSD Select k l 0~1 [Command2][ ][Set ID][ ][NG][Data][x] 13. Key Lock k m 0~1 * The Monitor transmits ACK (acknowledgement) based on 14. Balance k t 0 ~ 64 this format when receiving abnormal data from non-viable 15. Programme Select (Main TV Input) m a 0 ~ 63 functions or communication errors. 16. Key m c Key Code Data 1: Illegal Code * : Refer to 02 Input Select on page 50. 2: not support function Error messages 1. Illegal ID : If Wrong or no ID in the command is written. 2. B_TokenLength !=2 : If the first argument of the command does not equals to 2. 3. Error ==TOKEN_ERROR : If some Special character or Numeric are written to the first argument of the command or wrong Space position is written to the first argument of the command. 4. Illegal Opcodes : If the Wrong Alphabet is written to the first argu- ment of the command. 5. Illegal Params : If the Wrong parameter is supplied to the end of the command line (means that value of the parameter that is not sup- ported). 01. Power On (Command:a) 04. Screen Mute (Command:d) G To select screen mute on/off. G To control Power On/Off of the TV. Transmission Transmission [k][d][ ][Set ID][ ][Data][Cr] [k][a][ ][Set ID][ ][Data][Cr] Data 0 : Screen mute off (Picture on) Data 0 : Power Off 1 : Power On 1 : Screen mute on (Picture off) Acknowledgement Acknowledgement [a][ ][Set ID][ ][OK][Data][x] [d][ ][Set ID][ ][OK][Data][x] G To show Power On/Off. 05. Volume Mute (Command:e) Transmission G To control volume mute on/off. [k][a][ ][Set ID][ ][FF][Cr] You can also adjust mute using the MUTE button on remote control. Acknowledgement Transmission [a][ ][Set ID][ ][OK][Data][x] [k][e][ ][Set ID][ ][Data][Cr] Data 0 : Power Off 1 : Power On Data 0 : Volume mute on (Volume off) 02. Input Select (Command:b) (Main Picture Input) 1 : Volume mute off (Volume on) G To select input source for the TV. Acknowledgement You can also select an input source using the INPUT but- ton on the TV's remote control. [e][ ][Set ID][ ][OK][Data][x] Transmission 06. Volume Control (Command:f) [x][b][ ][Set ID][ ][Data][Cr] G To adjust volume. Data 10 : TV 50 or 60 : RGB PC You can also adjust volume with the volume buttons 20 : AV1 21 : AV2 51 or 61 : RGB DTV on remote control. 22 : AV3 90 : HDMI PC 30 : S-Video 91 : HDMI DTV Transmission 40 : Component [k][f][ ][Set ID][ ][Data][Cr] Acknowledgement Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) [b][ ][Set ID][ ][OK][Data][x] Refer to Real data mapping 1 as shown below. Acknowledgement 03. Aspect Ratio (Command:c) (Main picture format) [f][ ][Set ID][ ][OK][Data][x] G To adjust the screen format. You can also adjust the screen format using the ARC 07. Contrast (Command:g) (Aspect Ratio Control) button on remote control or in the Screen menu. G To adjust screen contrast. Transmission You can also adjust contrast in the Picture menu. [k][c][ ][Set ID][ ][Data][Cr] Transmission Data 0 : Normal screen (4:3) 4 : Spectacle [k][g][ ][Set ID][ ][Data][Cr] 1 : Wide screen (16:9) 5 : Full Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) 2 : 14:9 6 : Original Refer to Real data mapping 1 as shown below. 3 : Zoom * In PC mode only 16:9 and 4:3 aspect ratios are available. Acknowledgement Acknowledgement [g][ ][Set ID][ ][OK][Data][x] [c][ ][Set ID][ ][OK][Data][x] * Real data mapping 0 : Step 0 A : Step 10 F : Step 15 10 : Step 16 64 : Step 100 * Tint : R50 ~ G50 * Balance : L50 ~ R50 50 ENGLISH 13. Key Lock (Command:m) 08. Brightness (Command:h) G To lock the side panel controls on the TV. G To adjust screen brightness. Transmission You can also adjust brightness in the Picture menu. Transmission [k][m][ ][Set ID][ ][Data][Cr] [k][h][ ][Set ID][ ][Data][Cr] Data 0: Lock off 1: Lock on Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) Acknowledgement Refer to Real data mapping 1. See page 50. [m][ ][Set ID][ ][OK][Data][x] Acknowledgement If youre not using the side panel controls on the TV, use [h][ ][Set ID][ ][OK][Data][x] this mode. When main power is on/off, remote control lock is released. 9. Colour (Command:i) 14. Balance (Command:t) G To adjust the screen colour. G To adjust balance. You can also adjust colour in the Picture menu. Transmission Transmission [k][t][ ][Set ID][ ][Data][Cr] [k][i][ ][Set ID][ ][Data][Cr] Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) Refer to Real data mapping 1. See page 50. Refer to Real data mapping 1. See page 50. Acknowledgement Acknowledgement [i][ ][Set ID][ ][OK][Data][x] [t][ ][Set ID][ ][OK][Data][x] 10. Tint (Command:j) G To adjust the screen tint.(NTSC M input only) 15. Programme Select (Command:m a) (Main TV Input) You can also adjust tint in the Picture menu. Transmission G Tune channel to following Physical/major/minor number. [k][j][ ][Set ID][ ][Data][Cr] Transmission Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) [m][a][ ][Set ID][ ][Data0][Cr] Refer to Real data mapping 1. See page 50. Data0: Programme Number Acknowledgement Min : 0 ~ Max : 63(transmit by Hexadecimal code) Refer to Real data mapping 1. See page 50. [j][ ][Set ID][ ][OK][Data][x] Acknowledgement 11. Sharpness (Command:k) G To adjust the screen sharpness. [a][ ][Set ID][ ][OK][Data0][x] You can also adjust sharpness in the Picture menu. [a][ ][Set ID][ ][NG][Data0][x] Transmission [k][k][ ][Set ID][ ][Data][Cr] 16. Key (Command:m c) Data Min: 0 ~ Max: 64 (transmit by Hexadecimal code) G To send IR remote key code. Refer to Real data mapping 1. See page 50. Transmission Acknowledgement [m][c][ ][Set ID][ ][Data][Cr] [k][ ][Set ID][ ][OK][Data][x] Data: Key code (Refer to P.53) 12. OSD Select (Command:l) G To select OSD (On Screen Display) mute on/off. Acknowledgement Transmission [c][ ][Set ID][ ][OK][Data][x] [k][l][ ][Set ID][ ][Data][Cr] Data 0: OSD mute on 1: OSD mute off Acknowledgement [l][ ][Set ID][ ][OK][Data][x] """ def main(cmds): t = tv() VERBOSE_FLAGS = ["-v", "--verbose"] for flag in VERBOSE_FLAGS: if flag in cmds: t.verbose = True cmds.remove(flag) return t.execute(cmds) if __name__ == "__main__": sys.exit(main(sys.argv[1:]))