From 64290034f70d264e66da46e5d969b0dd02b2f0f1 Mon Sep 17 00:00:00 2001 From: markusbloch <> Date: Sun, 17 Aug 2014 21:35:34 +0000 Subject: [PATCH] Adding 75_LGTV_RS232.pm to contrib (controls LG SmartTV as described at http://www.lgrs232.com) see http://forum.fhem.de/index.php/topic,23712.0.html git-svn-id: https://svn.fhem.de/fhem/trunk@6414 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/75_LGTV_RS232.pm | 295 ++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100755 fhem/contrib/75_LGTV_RS232.pm diff --git a/fhem/contrib/75_LGTV_RS232.pm b/fhem/contrib/75_LGTV_RS232.pm new file mode 100755 index 000000000..80022eb39 --- /dev/null +++ b/fhem/contrib/75_LGTV_RS232.pm @@ -0,0 +1,295 @@ +############################################## +# $Id$ +# +# created by Markus Bloch (Notausstieg0309@googlemail.com) +# +# This modules controls LG Smart TV's which are connected via +# a USB to Serial converter to FHEM. +# +# Detailed Information about the hardware setup and more possible control commands: http://www.lgrs232.com/ +# +# This modules is able to switch the input channels, which +# is not possible via the ethernet interface. +# +# Define: define TV LGTV_RS232 /dev/ttyUSB0 +# +# Set: statusRequest input:hmdi1,hdmi2, ... +# +# +# + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +my %LGTV_RS232_response = ( + +"a" => { + "power" => {"00" => "off", + "01" => "on" + }, + "state" => {"00" => "off", + "01" => "on" + } + }, +"b" => { + "input" => {"00" => "digitalTV", + "01" => "analogueTV", + "02" => "video1", + "03" => "video2", + "04" => "component1", + "05" => "component2", + "06" => "rgbDTV", + "07" => "rgbPC", + "08" => "hdmi1", + "09" => "hdmi2" + } + + }, +"c" => { + "aspectRatio" => { "01" => "normal", + "02" => "wide", + "03" => "horizon", + "04" => "zoom1", + "05" => "zoom2", + "06" => "auto", + "07" => "14:9", + "08" => "full", + "09" => "justScan", + "0a" => "zoom3", + "0b" => "fullWide", + "10" => "cinemaZoom1", + "11" => "cinemaZoom16" + } + }, +"d" => { + "screenMute" => {"00" => "off", + "01" => "on" + }, + "videoOutMute" => {"00" => "off", + "10" => "on" + } + }, +"e" => { + "volumeMute" => {"00" => "on", + "01" => "off" + } + } +); + +my %LGTV_RS232_set = ( +"input" => { + "digitalTV" => "kb 01 00", + "video1" => "kb 01 02", + "video2" => "kb 01 03", + "component" => "kb 01 04", + "hdmi1" => "kb 01 08", + "hdmi2" => "kb 01 09", + } +); +##################################### +sub +LGTV_RS232_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "LGTV_RS232_Define"; + $hash->{UndefFn} = "LGTV_RS232_Undef"; + $hash->{SetFn} = "LGTV_RS232_Set"; + $hash->{ReadFn} = "LGTV_RS232_Read"; + $hash->{ReadyFn} = "LGTV_RS232_Ready"; + $hash->{AttrList} = " ".$readingFnAttributes; +} + +##################################### +sub +LGTV_RS232_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + + + my $name = $a[0]; + my $dev = $a[2]; + + + + $hash->{helper}{RECEIVE_BUFFER} = ""; + + $dev .= "\@9600" if(not $dev =~ m/\@\d+/); + + $hash->{DeviceName} = $dev; + + DevIo_CloseDev($hash); + + my $ret = DevIo_OpenDev($hash, 0, undef); + + delete($hash->{PARTIAL}); + RemoveInternalTimer($hash); + LGTV_RS232_GetStatus($hash); + return undef; +} + +##################################### +sub +LGTV_RS232_Undef($$) +{ + my ($hash, $arg) = @_; + + DevIo_CloseDev($hash); + return undef; +} + + +##################################### +sub +LGTV_RS232_Set($@) +{ + my ($hash, @a) = @_; + + my $what = $a[1]; + my $usage = "Unknown argument $what, choose one of statusRequest"; + + foreach my $cmd (sort keys %LGTV_RS232_set) + { + $usage .= " $cmd:".join(",", sort keys %{$LGTV_RS232_set{$cmd}}); + } + + if($what eq "statusRequest") + { + LGTV_RS232_GetStatus($hash, 1); + } + elsif(exists($LGTV_RS232_set{$what}) and exists($LGTV_RS232_set{$what}{$a[2]})) + { + DevIo_SimpleWrite($hash, $LGTV_RS232_set{$what}{$a[2]}."\n", 0); + } + else + { + return $usage; + } + +} + + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +LGTV_RS232_Read($) +{ + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + my $name = $hash->{NAME}; + + my $partial = $hash->{helper}{RECEIVE_BUFFER}; + + Log3 $name, 5, "LGTV_RS232 ($name) - ".($partial ne "" ? "(buffer contains: $partial) " : "")."received: $buf"; + + $partial .= $buf; + + while($partial =~ /(\w\s\d{2}\s[0-9a-zA-Z]+?x)(.*)/) + { + my $msg = $1; + $partial = $2; + $msg =~ s/x$//; + + LGTV_RS232_ParseResponse($hash, $msg); + } + + $hash->{helper}{RECEIVE_BUFFER} = $partial; + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+15, "LGTV_RS232_GetStatus", $hash, 0); +} + + +##################################### +# receives incoming data +sub +LGTV_RS232_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, undef) if($hash->{STATE} eq "disconnected"); +} + +sub +LGTV_RS232_ParseResponse($$) +{ + my ($hash, $msg) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 4, "LGTV_RS232 ($name) - processing response: ".$msg; + + my ($code, $glue, $result, $val) = unpack("A2 A3 A2 A*", $msg); + + Log3 $name, 5, "LGTV_RS232 ($name) - processed code: $code - glue: $glue - result: $result - val: $val"; + + readingsBeginUpdate($hash); + + if($result eq "OK") + { + readingsBulkUpdate($hash, "CommandAccepted", "yes"); + } + elsif($result eq "NG") + { + readingsBulkUpdate($hash, "CommandAccepted", "no"); + return; + + } + + foreach my $reading (keys %{$LGTV_RS232_response{$code}}) + { + if(exists($LGTV_RS232_response{$code}{$reading}{$val})) + { + readingsBulkUpdate($hash, $reading, $LGTV_RS232_response{$code}{$reading}{$val}); + } + } + + if($code eq "f") + { + readingsBulkUpdate($hash, "volume", hex($val)." %"); + } + readingsBulkUpdate($hash, "presence", "present"); + readingsEndUpdate($hash, 1); + +} + +##################################### +# request the current state +sub +LGTV_RS232_GetStatus($;$) +{ + my ($hash, $local) = @_; + + DevIo_SimpleWrite($hash, "ka 01 ff\n", 0); + DevIo_SimpleWrite($hash, "kb 01 ff\n", 0); + DevIo_SimpleWrite($hash, "kc 01 ff\n", 0); + DevIo_SimpleWrite($hash, "kd 01 ff\n", 0); + DevIo_SimpleWrite($hash, "kf 01 ff\n", 0); + DevIo_SimpleWrite($hash, "ke 01 ff\n", 0); + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+2, "LGTV_RS232_TimeOut", $hash, 0); + +} + +##################################### +# Is executed when a request via serial connection was not answered +sub +LGTV_RS232_TimeOut($) +{ + my ($hash) = @_; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "presence", "absent"); + readingsBulkUpdate($hash, "state", "off"); + readingsEndUpdate($hash, 1); + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+15, "LGTV_RS232_GetStatus", $hash, 0); +} + +1; \ No newline at end of file