diff --git a/FHEM/70_SISPM.pm b/FHEM/70_SISPM.pm index c0fad1698..87ec3e8ec 100644 --- a/FHEM/70_SISPM.pm +++ b/FHEM/70_SISPM.pm @@ -29,7 +29,7 @@ # # Contributed by Kai 'wusel' Siering 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); diff --git a/FHEM/80_xxLG7000.pm b/FHEM/80_xxLG7000.pm new file mode 100644 index 000000000..fe7ca3ca2 --- /dev/null +++ b/FHEM/80_xxLG7000.pm @@ -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 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 [an0..an5]\n" . + "get [io0..io7]\n" . + "get octet\n" . + "get 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; diff --git a/FHEM/82_LGTV.pm b/FHEM/82_LGTV.pm new file mode 100644 index 000000000..399dfd987 --- /dev/null +++ b/FHEM/82_LGTV.pm @@ -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 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 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; diff --git a/docs/commandref.html b/docs/commandref.html index c26e26dd3..fe7840332 100644 --- a/docs/commandref.html +++ b/docs/commandref.html @@ -86,6 +86,8 @@ Weather   USF1000   X10   + xxLG7000   + LGTV   FHEMRENDERER   @@ -2916,6 +2918,121 @@ A line ending with \ will be concatenated with the next one, so long lines + +

xxLG7000

+
    +
    + + + Define +
      + define <name> xxLG7000 <serial-device> +

      + + 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 ;))

      + + Examples: +
        + define myLG7k xxLG7000 /dev/ttyUSB1
        +
      +
      +
    + + + Set +
      Not used, nothing to set directly.
    + + + Get +
      Not used, nothing to get directly.
    + + Attributes +
      +
    • loglevel
    • +
    • 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?)
    • +
    +
    +
+ + + +

LGTV

+
    + + + Define +
      + define <name> LGTV +

      + + This module is expected to work with xxLG7000 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.
      + 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.

      +
    + + + Set +
      + set <name> <what> <value> +

      + 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. + +
      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
      +
    + + Get +
      + + Not yet implemented; use "set name power state" or "set name selected input" for now. +

      +
    + + Attributes + +
    + Implementator's note +
      + The commands listed above are send 1:1 to the underlying IODev (e. g. xxLG7000); that IODev + is responsible for translation into whatever means 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. +
    +
    +
+

OWFS