2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

82_LGTV.pm & 80_xxLG7000.pm, Initial Release.

git-svn-id: https://svn.fhem.de/fhem/trunk@552 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
painseeker 2010-01-20 01:04:01 +00:00
parent aeea6033a8
commit 5a5a579d14
4 changed files with 725 additions and 14 deletions

View File

@ -29,7 +29,7 @@
#
# Contributed by Kai 'wusel' Siering <wusel+fhem@uu.org> in 2010
# Based in part on work for FHEM by other authors ...
# $Id: 70_SISPM.pm,v 1.3 2010-01-19 19:33:01 painseeker Exp $
# $Id: 70_SISPM.pm,v 1.4 2010-01-20 01:04:01 painseeker Exp $
###########################
package main;
@ -37,18 +37,6 @@ package main;
use strict;
use warnings;
my %sets = (
"cmd" => "",
"freq" => "",
);
my %TranslatedCodes = (
"Date" => "Date",
);
my %WantedCodesForStatus = (
"Ti" => "Ti:",
);
#####################################
sub
@ -326,7 +314,7 @@ SISPM_Read($)
if($inputline =~ /^Status of outlet (\d):\s+(.*)/) {
if($currentserial ne "none") {
Log 3, "SISPM found socket $1 on $currentserial, state $2";
Log 5, "SISPM found socket $1 on $currentserial, state $2";
my $dmsg="socket " . $currentserial . " $1 state " . $2;
my %addvals;
Dispatch($hash, $dmsg, \%addvals);

411
fhem/FHEM/80_xxLG7000.pm Normal file
View File

@ -0,0 +1,411 @@
#
# 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
#
# 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 %d FF\r",
"power on" => "ka %d 01\r",
"power off" => "ka %d 00\r",
"input AV1" => "xb %d 20\r",
"input AV2" => "xb %d 21\r",
"input AV3" => "xb %d 22\r",
"input AV4" => "xb %d 23\r",
"input Component" => "xb %d 40\r",
"input RGB-PC" => "xb %d 50\r",
"input HDMI1" => "xb %d 90\r",
"input HDMI2" => "xb %d 91\r",
"input HDMI3" => "xb %d 92\r",
"input HDMI4" => "xb %d 93\r",
"input DVBT" => "xb %d 00\r",
"input PAL" => "xb %d 10\r",
"selected input" => "xb %d FF\r",
"audio mute" => "ke %d 00\r",
"audio normal" => "ke %d 01\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 OK40" => "input Components",
"b OK50" => "input RGB-PC",
"b OK10" => "input PAL",
"b OK00" => "input DVB-T",
"e OK00" => "audio muted",
"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->{GetFn} = "xxLG7000_Get";
# $hash->{SetFn} = "xxLG7000_Set";
$hash->{AttrList}= "SetID:01,02, 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;
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_Set($@)
{
my ($hash, @a) = @_;
my $u1 = "Usage: see commandref.html for details\n";
return $u1 if(int(@a) < 2);
my $msg;
my $reading= $a[1];
my $value;
my @legal;
if($reading eq "auto") {
return $u1 if(int(@a) !=3);
$value= $a[2];
@legal= (0..5,"none");
if(!grep($value eq $_, @legal)) {
return "Illegal value $value, possible values: @legal";
}
if($value eq "none") { $value= 0; } else { $value+=1; }
$msg= "M" . $value;
}
elsif($reading eq "start") {
return $u1 if(int(@a) !=2);
$msg= "Z1";
}
elsif($reading eq "stop") {
return $u1 if(int(@a) !=2);
$msg= "Z0";
}
elsif($reading eq "octet") {
return $u1 if(int(@a) !=3);
$value= $a[2];
@legal= (0..255);
if(!grep($value eq $_, @legal)) {
return "Illegal value $value, possible values: 0..255";
}
$msg= sprintf("W%02X", $value);
}
elsif($reading =~ /^io[0-7]$/) {
return $u1 if(int(@a) !=3);
$value= $a[2];
return $u1 unless($value eq "0" || $value eq "1");
$msg= "D" . substr($reading,2,1) . $value;
}
else { return $u1; }
my $d = xxLG7000GetData($hash, $msg);
return "Read error" if(!defined($d));
return $d;
}
#####################################
sub
xxLG7000_Get($@)
{
my ($hash, @a) = @_;
my $u1 = "Usage: get <name> [an0..an5]\n" .
"get <name> [io0..io7]\n" .
"get <name> octet\n" .
"get <name> counter";
return $u1 if(int(@a) != 2);
my $name= $a[0];
my $reading= $a[1];
my $msg;
my $retval;
my ($count,$d,$state,$iscurrent,$voltage);
if($reading eq "counter") {
$msg= "z";
$d = xxLG7000GetData($hash, $msg);
return "Read error" if(!defined($d));
$count= hex $d;
$retval= $count;
}
elsif($reading =~ /^an[0-5]$/) {
$msg= "a" . substr($reading,2,1);
$d = xxLG7000GetData($hash, $msg);
return "Read error" if(!defined($d));
$voltage= (hex substr($d,0,3))*5.00/1024.0;
$iscurrent= substr($d,3,1);
$retval= $voltage; # . " " . $iscurrent;
}
elsif($reading =~ /^io[0-7]$/) {
$msg= "d" . substr($reading,2,1);
$d = xxLG7000GetData($hash, $msg);
return "Read error" if(!defined($d));
$state= hex $d;
$retval= $state;
}
elsif($reading eq "octet") {
$msg= "w";
$d = xxLG7000GetData($hash, $msg);
return "Read error" if(!defined($d));
$state= hex $d;
$retval= $state;
}
else { return $u1; }
$hash->{READINGS}{$reading}{VAL}= $retval;
$hash->{READINGS}{$reading}{TIME}= TimeNow();
return "$name $reading => $retval";
}
#####################################
sub
xxLG7000_Write($$)
{
my ($hash,$msg) = @_;
my $dev = $hash->{DeviceName};
my $UnitNo=1;
my $ret;
my $retmsg;
my $sendstring=$commands{$msg};
if(!defined($sendstring)) {
return "Unknown command $msg, choose one of " . join(" ", sort keys %commands);
}
$sendstring=sprintf($sendstring, $UnitNo); # FIXME! This needs to become a settable attribut!
$ret=xxLG7000GetData($hash, $sendstring);
Log 3, "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 3, "xxLG7000_Write: error message: $ret";
} else {
$retmsg=sprintf("Unknown response %s, help me!");
Log 3, "xxLG7000_Write: $retmsg";
}
} else {
Log 3, "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;

195
fhem/FHEM/82_LGTV.pm Normal file
View File

@ -0,0 +1,195 @@
# 82_LGTV.pm; an FHEM high level module for interfacing
# with LG's Scarlet Series of LCDs (e. g. LG 47LG7000)
# Trying to implement a generic command set so that is
# is re-usable with other low-level drivers besides my
# 80_xxLG7000.pm for a serial connection.
#
# Written by Kai 'wusel' Siering <wusel+fhem@uu.org> around 2010-01-20
#
# re-using code of 82_M232Voltage.pm
# written by Dr. Boris Neubert 2007-12-24
# e-mail: omega at online dot de
#
##############################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
sub LGTV_Get($@);
sub LGTV_Define($$);
sub LGTV_GetStatus($);
my @commandlist = (
"power state",
"power on",
"power off",
"input AV1",
"input AV2",
"input AV3",
"input AV3",
"input Component",
"input RGB",
"input HDMI1",
"input HDMI2",
"input HDMI3",
"input HDMI4",
"input DVBT",
"input PAL",
"audio mute",
"audio normal",
"selected input"
);
###################################
sub
LGTV_Initialize($)
{
my ($hash) = @_;
# $hash->{GetFn} = "LGTV_Get";
$hash->{SetFn} = "LGTV_Set";
$hash->{DefFn} = "LGTV_Define";
$hash->{AttrList} = "dummy:1,0 model:LGTV loglevel:0,1,2,3,4,5";
}
###################################
sub
LGTV_GetStatus($)
{
my ($hash) = @_;
if(!$hash->{LOCAL}) {
InternalTimer(gettimeofday()+60, "LGTV_GetStatus", $hash, 1);
}
my $name = $hash->{NAME};
my $d = IOWrite($hash, "power state");
if(!defined($d)) {
my $msg = "LGTV $name read error";
Log GetLogLevel($name,2), $msg;
return $msg;
}
my $tn = TimeNow();
my ($value, $state)=split(" ", $d);
if($value eq "power") {
$hash->{READINGS}{$value}{TIME} = $tn;
$hash->{READINGS}{$value}{VAL} = $state;
$hash->{CHANGED}[0]= "$value: $state";
$hash->{STATE} = $state;
if($state eq "on") {
$d = IOWrite($hash, "selected input");
if(!defined($d)) {
my $msg = "LGTV $name read error";
Log GetLogLevel($name,2), $msg;
return $msg;
}
$tn = TimeNow();
($value, $state)=split(" ", $d);
$hash->{READINGS}{$value}{TIME} = $tn;
$hash->{READINGS}{$value}{VAL} = $state;
$hash->{CHANGED}[1]= "$value: $state";
$hash->{STATE} = $hash->{STATE} . ", " . $state;
}
}
DoTrigger($name, undef);
Log GetLogLevel($name,4), "LGTV $name: $hash->{STATE}";
return $hash->{STATE};
}
###################################
sub
LGTV_Get($@)
{
my ($hash, @a) = @_;
return "argument is missing" if(int(@a) != 2);
my $msg;
if($a[1] ne "status") {
return "unknown get value, valid is status";
}
$hash->{LOCAL} = 1;
my $v = LGTV_GetStatus($hash);
delete $hash->{LOCAL};
return "$a[0] $a[1] => $v";
}
###################################
sub
LGTV_Set($@)
{
my ($hash, @a) = @_;
my $ret = undef;
my $na = int(@a);
my $ncmds=int(@commandlist);
my $i;
my $known_cmd=0;
my $what = "";
$what=$a[1];
if($na>1) {
for($i=2; $i<$na; $i++) {
$what=$what . " " . $a[$i];
}
}
for($i=0; $i<$ncmds; $i++ && $known_cmd==0) {
if($commandlist[$i] eq $what) {
$known_cmd+=1;
}
}
if($known_cmd==0) {
return "Unknown argument $what, choose one of power input audio";
}
$ret=IOWrite($hash, $what, "");
return $ret;
}
#############################
sub
LGTV_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
# return "syntax: define <name> LGTV an0..an5 [unit [factor]]"
# if(int(@a) < 3 && int(@a) > 5);
#
# my $reading= $a[2];
# return "$reading is not an analog input, valid: an0..an5"
# if($reading !~ /^an[0-5]$/) ;
#
# my $unit= ((int(@a) > 3) ? $a[3] : "volts");
# my $factor= ((int(@a) > 4) ? $a[4] : 1.0);
#
# $hash->{INPUT}= substr($reading,2);
# $hash->{UNIT}= $unit;
# $hash->{FACTOR}= $factor;
AssignIoPort($hash);
if(!$hash->{LOCAL}) {
InternalTimer(gettimeofday()+60, "LGTV_GetStatus", $hash, 0);
}
return undef;
}
1;

View File

@ -86,6 +86,8 @@
<a href="#Weather">Weather</a> &nbsp;
<a href="#USF1000">USF1000</a> &nbsp;
<a href="#X10">X10</a> &nbsp;
<a href="#xxLG7000">xxLG7000</a> &nbsp;
<a href="#LGTV">LGTV</a> &nbsp;
<a href="#FHEMRENDERER">FHEMRENDERER</a> &nbsp;
</ul>
@ -2916,6 +2918,121 @@ A line ending with \ will be concatenated with the next one, so long lines
</ul>
<a name="xxLG7000"></a>
<h3>xxLG7000</h3>
<ul>
<br>
<a name="xxLG7000define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; xxLG7000 &lt;serial-device&gt;</code>
<br><br>
Defines a serial link to a TV set of LG's xxLG70yy series; these LCD TVs
(as well as their Plasma cousins, xxPG70yy) 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"). And, well, xxLG7000 is
the FHEM module to actually do this. (BTW, they run Linux internally ;))<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>
<b>Attributes</b>
<ul>
<li><a href="#loglevel">loglevel</a></li>
<li>SetID (1, 2, ...; see Owner's Manual. Currently, only SetID 1 is addressed, regardless of this setting. Anyone who onws more than one of these?)</li>
</ul>
<br>
</ul>
<a name="LGTV"></a>
<h3>LGTV</h3>
<ul>
<a name="LGTVdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; LGTV</code>
<br><br>
This module is expected to work with <a href="#xxLG7000">xxLG7000</a> as it's
IODev. With LGTV and a compatible hardware module (currently, there's only
xxLG7000), you are able to power your TV set on and off, query it's power state,
select the input (AV, RGB, Composites, analogue TV, DVB-T, HDMI) or mute/unmute
the volume.<br>
Defining a LGTV device will schedule an internal task, which periodically reads
the status of the TV set (power state; if power is on, query the selected input)
and triggers notify/filelog commands.<br><br>
</ul>
<a name="LGTVset"></a>
<b>Set </b>
<ul>
<code>set &lt;name&gt; &lt;what&gt; &lt;value&gt;</code>
<br><br>
Currently, the following commands are defined; not all may be available on a
given TV set. An error messages should be recorded if e. g. the input in question
is not usable.
<pre>power state
power on
power off
input AV1
input AV2
input AV3
input AV3
input Component
input RGB
input HDMI1
input HDMI2
input HDMI3
input HDMI4
input DVBT
input PAL
audio mute
audio normal
selected input</pre>
</ul>
<a name="LGTVget"></a>
<b>Get</b>
<ul>
<!-- <code>get &lt;name&gt; status</code>
<br><br> -->
Not yet implemented; use "set name power state" or "set name selected input" for now.
<br><br>
</ul>
<b>Attributes</b>
<ul>
<li><a href="#attrdummy">dummy</a></li><br>
<li><a href="#loglevel">loglevel</a></li>
<!-- <li><a href="#model">model</a> (M232Counter)</li> -->
</ul>
<br>
<b>Implementator's note</b>
<ul>
The commands listed above are send 1:1 to the underlying IODev (e. g. xxLG7000); that IODev
is responsible for translation into <i>whatever means</i> to invoke the function on the TV.
It is my hope that other's will adopt this idea and write compatible low level drivers for other
TV sets, to make this module (even ;)) more useful.
</ul>
<br>
</ul>
<a name="OWFS"></a>
<h3>OWFS</h3>
<ul>