mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 18:59:33 +00:00
3ab9aff027
git-svn-id: https://svn.fhem.de/fhem/trunk@16374 2b470e98-0d58-463d-a4d8-8e2adae1ed80
367 lines
9.7 KiB
Perl
367 lines
9.7 KiB
Perl
#
|
|
# 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$
|
|
#
|
|
# 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
|