| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- #
- # 80_xxLG7000.pm; an FHEM module for interfacing
- # with LG's Scarlet Series of LCDs (e. g. LG 47LG7000)
- #
- # Written by Kai 'wusel' Siering <wusel+fhem@uu.org> around 2010-01-20
- # $Id: 80_xxLG7000.pm 2076 2012-11-04 13:49:43Z rudolfkoenig $
- #
- # re-using code of 80_M232.pm by Dr. Boris Neubert
- ##############################################
- package main;
- use strict;
- use warnings;
- sub xxLG7000Write($$);
- sub xxLG7000GetData($$);
- sub Log($$);
- use vars qw {%attr %defs};
- my %commands = (
- "power state" => "ka %x FF\r",
- "power on" => "ka %x 01\r",
- "power off" => "ka %x 00\r",
- "input AV1" => "xb %x 20\r",
- "input AV2" => "xb %x 21\r",
- "input AV3" => "xb %x 22\r",
- "input AV4" => "xb %x 23\r",
- "input Component" => "xb %x 40\r",
- "input RGB-PC" => "xb %x 50\r",
- "input HDMI1" => "xb %x 90\r",
- "input HDMI2" => "xb %x 91\r",
- "input HDMI3" => "xb %x 92\r",
- "input HDMI4" => "xb %x 93\r",
- "input DVB-T" => "xb %x 00\r",
- "input PAL" => "xb %x 10\r",
- "selected input" => "xb %x FF\r",
- "audio mute" => "ke %x 00\r",
- "audio normal" => "ke %x 01\r",
- "audio state" => "ke %x FF\r",
- );
- my %responses = (
- "a OK00" => "power off",
- "a OK01" => "power on",
- "b OK20" => "input AV1",
- "b OK21" => "input AV2",
- "b OK22" => "input AV3",
- "b OK23" => "input AV4",
- "b OK90" => "input HDMI1",
- "b OK91" => "input HDMI2",
- "b OK92" => "input HDMI3",
- "b OK93" => "input HDMI4",
- "b OKa0" => "input HDMI1-no_link", # At least 47LG7000 returns 10100001 instead of 10010001 when
- "b OKa1" => "input HDMI2-no_link", # there is no link/signal connected to the corresponding
- "b OKa2" => "input HDMI3-no_link", # HDMI input. -wusel, 2010-01-20
- "b OKa3" => "input HDMI4-no_link",
- "b OK40" => "input Components",
- "b OK50" => "input RGB-PC",
- "b OK10" => "input PAL", # Selecting analogue (dubbed PAL here) input does not work for
- "b OK00" => "input DVB-T", # me; well, there's nothing to see anymore anyway, at least
- "e OK00" => "audio muted", # in Germany ;) (Ack, I don't have CATV.) -wusel, 2010-01-20
- "e OK01" => "audio normal",
- );
- #####################################
- sub
- xxLG7000_Initialize($)
- {
- my ($hash) = @_;
- # Provider
- $hash->{WriteFn} = "xxLG7000_Write";
- $hash->{Clients} = ":LGTV:";
- # No ReadFn as this is a purely command->response interface, in contrast
- # to e. g. CUL which send's data on it's own. -wusel
- # Consumer
- $hash->{DefFn} = "xxLG7000_Define";
- $hash->{UndefFn} = "xxLG7000_Undef";
- $hash->{AttrList}= "SetID:1,2,... loglevel:0,1,2,3,4,5";
- }
- #####################################
- sub
- xxLG7000_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- $hash->{STATE} = "Initialized";
- my $dev = $a[2];
- if($dev eq "none") {
- Log 1, "xxLG7000 device is none, commands will be echoed only";
- return undef;
- }
- Log 3, "xxLG7000 opening device $dev";
- my $po;
- if ($^O eq 'MSWin32') {
- eval ("use Win32::SerialPort;");
- if ($@) {
- $hash->{STATE} = "error using Modul Win32::SerialPort";
- Log 1,"Error using Device::SerialPort";
- return "Can't use Win32::SerialPort $@\n";
- }
- $po = new Win32::SerialPort ($dev, 1);
-
- } else {
- eval ("use Device::SerialPort;");
- if ($@) {
- $hash->{STATE} = "error using Modul Device::SerialPort";
- Log 1,"Error using Device::SerialPort";
- return "Can't Device::SerialPort $@\n";
- }
- $po = new Device::SerialPort ($dev, 1);
- }
- if (!$po) {
- $hash->{STATE} = "error opening device";
- Log 1,"Error opening Serial Device $dev";
- return "Can't open Device $dev: $^E\n";
- }
-
- Log 3, "xxLG7000 opened device $dev";
- $po->close();
- $hash->{DeviceName} = $dev;
- $attr{$a[0]}{SetID}=1;
- return undef;
- }
- #####################################
- sub
- xxLG7000_Undef($$)
- {
- my ($hash, $arg) = @_;
- my $name = $hash->{NAME};
- foreach my $d (sort keys %defs) {
- if(defined($defs{$d}) &&
- defined($defs{$d}{IODev}) &&
- $defs{$d}{IODev} == $hash)
- {
- my $lev = ($reread_active ? 4 : 2);
- Log GetLogLevel($name,$lev), "deleting port for $d";
- delete $defs{$d}{IODev};
- }
- }
- return undef;
- }
- #####################################
- # implement ReadyFn, only used for Win32
- sub
- xxLG7000_Ready($$)
- {
- my ($hash, $dev) = @_;
- my $po=$dev||$hash->{po};
- return 0 if !$po;
- my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags)=$po->status;
- return ($InBytes>0);
- }
- #####################################
- sub
- xxLG7000_Write($$)
- {
- my ($hash,$msg) = @_;
- my $dev = $hash->{DeviceName};
- my $UnitNo=1;
- my $ret;
- my $retmsg="error occured";
- my $myname=$hash->{NAME};
- if(defined($attr{$myname}{SetID})) {
- $UnitNo=$attr{$myname}{SetID};
- Log $UnitNo==1?5:4, "xxLG7000_Write: Using SetID $UnitNo for $myname.";
- }
- my $sendstring=$commands{$msg};
- if(!defined($sendstring)) {
- return "error unknown command $msg, choose one of " . join(" ", sort keys %commands);
- }
- $sendstring=sprintf($sendstring, $UnitNo);
- Log 5, "xxLG7000_Write: sending $sendstring";
- $ret=xxLG7000GetData($hash, $sendstring);
- if(!defined($ret) || length($ret)<=6) {
- Log 2, "xxLG7000_Write: error, got too short answer ($ret).";
- } else {
- Log 5, "xxLG7000_Write: wrote $msg, received $ret";
- $retmsg=sprintf("%s %s", substr($ret, 0, 1), substr($ret, 5));
- $retmsg=$responses{$retmsg};
- if(!defined($retmsg)) {
- if(substr($ret, 5, 2) eq "NG") {
- $retmsg="error message";
- Log 5, "xxLG7000_Write: error message: $ret";
- } else {
- Log 2, "xxLG7000_Write: Unknown response $ret, help me!";
- $retmsg=sprintf("error message_unknown:%s", $ret =~ s/ /_/);
- }
- } else {
- Log 5, "xxLG7000_Write: returns $retmsg";
- }
- }
- return $retmsg;
- }
- #####################################
- sub
- xxLG7000GetData($$)
- {
- my ($hash, $data) = @_;
- my $dev=$hash->{DeviceName};
- my $serport;
- my $d = $data;
- my $MSGACK= 'x';
-
- if ($^O eq 'MSWin32') {
- $serport=new Win32::SerialPort ($dev, 1);
- } else {
- $serport=new Device::SerialPort ($dev, 1);
- }
- if(!$serport) {
- Log 3, "xxLG7000: Can't open $dev: $!";
- return undef;
- }
- $serport->reset_error();
- $serport->baudrate(9800);
- $serport->databits(8);
- $serport->parity('none');
- $serport->stopbits(1);
- $serport->handshake('none');
- $serport->write_settings;
- $hash->{po}=$serport;
- Log 4, "xxLG7000: Sending $d";
-
- my $rm = "xxLG7000: ?";
-
- $serport->lookclear;
- $serport->write($d);
-
- my $retval = "";
- my $status = "";
- my $nfound=0;
- my $ret=undef;
- sleep(1);
- for(;;) {
- if ($^O eq 'MSWin32') {
- $nfound=xxLG7000_Ready($hash,undef);
- } else {
- my ($rout, $rin) = ('', '');
- vec($rin, $serport->FILENO, 1) = 1;
- $nfound = select($rin, undef, undef, 1.0); # 3 seconds timeout
- if($nfound < 0) {
- next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
- $rm="xxLG7000:Select error $nfound / $!";
- last;
- }
- }
-
- last if($nfound == 0);
-
- my $out = $serport->read(1);
- if(!defined($out) || length($out) == 0) {
- $rm = "xxLG7000 EOF on $dev";
- last;
- }
-
- if($out eq $MSGACK) {
- $rm= "xxLG7000: acknowledged";
- Log 4, "xxLG7000: return value \'" . $retval . "\'";
- $status= "ACK";
- } else {
- $retval .= $out;
- }
-
- if($status) {
- $ret=$retval;
- last;
- }
-
- }
-
- DONE:
- $serport->close();
- undef $serport;
- delete $hash->{po} if exists($hash->{po});
- Log 4, $rm;
- return $ret;
- }
- 1;
- =pod
- =begin html
- <a name="xxLG7000"></a>
- <h3>xxLG7000</h3>
- <ul>
- <br>
- <a name="xxLG7000define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> xxLG7000 <serial-device></code>
- <br><br>
- Defines a serial link to a TV set of LG's xxLG70yy (e. g. 47LG7000) series
- and similar TV sets from <a href="http://www.lge.com/">LG</a>. As of January 2010, the following TV sets should
- be compatible:<br><br>
- <ul>
- <li><code>xxLG7000</code>, e. g. 47LG7000 (tested)</li>
- <li><code>xxPG7000</code>, e. g. 50PG7000 (same Manual as 47LG7000 ;))</li>
- <li><code>PS3000/6000/7000/8000 series</code> (according to <a href="http://www.lge.com/uk/products/documents/LGSV09-LR.pdf">LG brochure</a>; no liabilities assumed)</li>
- <li><code>PQ3000/6000 series</code> (see PS3000)</li>
- <li><code>LU4000/5000 series</code> (<i>not LU7000</i>; see PS3000)</li>
- <li><code>LH2000/3000/4000/5000 series</code> (see PS3000)</li>
- <li><code>SL9500/9000/8000 series</code> (see PS3000)</li>
- </ul><br>
- These TV sets feature a serial connector which can officially be used to control
- the TV set (see your Onwer's Manual, there's an Appendix labelled "External Control
- Device setup", referening to cabling and command set). The xxLG7000 module is
- the FHEM module to actually utilize this. (BTW, those TVs run Linux internally ;))<br><br>
- To exercise control over your TV set, use the <a href="#LGTV">LGTV</a> module and
- bind it ("attr <LGTV-name> IODev <xxLG7000-name>") to xxLG7000.<br><br>
- Examples:
- <ul>
- <code>define myLG7k xxLG7000 /dev/ttyUSB1</code><br>
- </ul>
- <br>
- </ul>
- <a name="xxLG7000set"></a>
- <b>Set </b>
- <ul> Not used, nothing to set directly. </ul>
- <a name="xxLG7000get"></a>
- <b>Get</b>
- <ul> Not used, nothing to get directly. </ul>
- <a name="xxLG7000attr"></a>
- <b>Attributes</b>
- <ul>
- <li><a href="#loglevel">loglevel</a></li>
- <li>SetID (1, 2, ...; see your TV's Owner's Manual how to set it. Defaults to 1 if unset.)</li>
- </ul>
- <br>
- </ul>
- =end html
- =cut
|