From 73481cbf13828e9618710517e0539eb2278535ec Mon Sep 17 00:00:00 2001 From: mabula <> Date: Sat, 23 Nov 2019 19:34:59 +0000 Subject: [PATCH] 70_Viera: implemented crypted communication for new TVs (Forum #99994) git-svn-id: https://svn.fhem.de/fhem/trunk@20567 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/70_VIERA.pm | 1218 ++++++++++++++++++++++++++++++----------- fhem/MAINTAINER.txt | 2 +- 3 files changed, 892 insertions(+), 331 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index f0e6b1406..cabe0b80e 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 70_VIERA: crypted communication with new TVs (2019+) + DEF of the devices changed (commandref) + reading power deleted, state => initialized/on/off - bugfix: 49_SSCam: fix commandref - feature: 93_DbRep: new command set adminCredentials, get storedCredentials, attribute useAdminCredentials, diff --git a/fhem/FHEM/70_VIERA.pm b/fhem/FHEM/70_VIERA.pm index 7a9c4f3aa..0f5a53cc4 100644 --- a/fhem/FHEM/70_VIERA.pm +++ b/fhem/FHEM/70_VIERA.pm @@ -6,11 +6,19 @@ # a module to send messages or commands to a Panasonic TV # inspired by Samsung TV Module from Gabriel Bentele # written 2013 by Tobias Vaupel +# since version 1.25 modified by mabula # # -# Version = 1.24 +# Version = 1.25 # # Version History: +# - 1.25 - 2019-11-23 Dr. H-J Breymayer +# -- removed Readings "power". Redefined state -> Initialized/on/off +# -- removed spaces at remote control layout, problems with images not appearing +# -- PERL error line 481 and 516 +# -- correction of sub call "sub VIERA_GetStatus($$)" +# -- Verschlüsselte Verbindung für neue TV's ab 2019 integriert. +# # - 1.24 - 2015-07-08 # -- Using non blocking as default for status update. Use attr to use blocking mode. # -- Replaced when/given with if/elsif @@ -53,16 +61,17 @@ package main; use strict; use warnings; use IO::Socket::INET; -use feature qw/say switch/; +use MIME::Base64; +use Crypt::Mode::CBC; +use Digest::SHA qw(hmac_sha256); use Time::HiRes qw(gettimeofday sleep); -use Blocking; - +use utf8; +#use Blocking; # Forward declaration for remotecontrol module +sub VIERA_RCmakenotify($$); sub VIERA_RClayout_TV(); sub VIERA_RClayout_TV_SVG(); -sub VIERA_RCmakenotify($$); - my %VIERA_remoteControl_args = ( "NRC_CH_DOWN-ONOFF" => "Channel down", @@ -125,33 +134,44 @@ my %VIERA_remoteControl_args = ( "NRC_HDMI4" => "Switch to HDMI input 4", ); + # Initialize the module and tell FHEM name of additional functions # Param1: Hash of FHEM-Device # Return: no return code -sub VIERA_Initialize($){ +sub VIERA_Initialize($) { my ($hash) = @_; + $hash->{DefFn} = "VIERA_Define"; $hash->{SetFn} = "VIERA_Set"; $hash->{GetFn} = "VIERA_Get"; $hash->{UndefFn} = "VIERA_Undefine"; - $hash->{AttrList} = "blocking:1,0 $readingFnAttributes"; + $hash->{AttrList} = "blocking:1,0 $readingFnAttributes"; $data{RC_layout}{VIERA_TV} = "VIERA_RClayout_TV"; - $data{RC_layout}{VIERA_TV_SVG} = "VIERA_RClayout_TV_SVG"; + $data{RC_layout}{VIERA_TV_SVG} = "VIERA_RClayout_TV_SVG"; $data{RC_makenotify}{VIERA} = "VIERA_RCmakenotify"; + } # Callback when 'define' is used at FHEM # Param1: Hash of FHEM-Device # Param2: String of 'define' command # Return: Help text for FHEMWEB -sub VIERA_Define($$){ +sub VIERA_Define($$) { my ($hash, $def) = @_; my @args = split("[ \t][ \t]*", $def); - my $name = $hash->{NAME}; + my $name = $args[0]; - if(int(@args) < 3 && int(@args) > 4) { - my $msg = "wrong syntax: define VIERA []"; - Log3 $name, 2, "VIERA: $msg"; + my $nom = $hash->{TYPE}."_".$hash->{NAME}."_"; + + my $error = ""; + my $value = ""; + + $hash->{helper}{session_id} = "None"; + $hash->{helper}{session_seq_num} = "None"; + + if(int(@args) < 3 && int(@args) > 6) { + my $msg = "wrong syntax: define VIERA [] "; + Log3 $name, 2, "VIERA: \"$msg\""; return $msg; } @@ -164,17 +184,67 @@ sub VIERA_Define($$){ else { $hash->{helper}{INTERVAL}=30; } + + if(defined($args[4])) { + $hash->{helper}{pincode} = $args[4]; + } + else { + $hash->{helper}{pincode} = "0000"; + $hash->{helper}{ENCRYPTION} = "no"; + } + + if(defined($args[5])) { + $hash->{helper}{ENCRYPTION} = $args[5]; + } + else { + ($error, $value) = getKeyValue("$nom.ENCRYPTION"); + $hash->{helper}{ENCRYPTION} = $value if (defined($value)); + } + - CommandAttr(undef,$name.' webCmd off') if( !defined( AttrVal($hash->{NAME}, "webCmd", undef)) ); +# communication can start again + if ($hash->{helper}{pincode} eq "0000") { + $hash->{helper}{stop} = "no" + } + else { + ($error, $hash->{helper}{stop}) = getKeyValue("$nom.stop"); + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "PinCode", $hash->{helper}{pincode}); + readingsBulkUpdate($hash, "Encryption", $hash->{helper}{ENCRYPTION}); + readingsBulkUpdate($hash, "session_id", $hash->{helper}{session_id}); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); + readingsEndUpdate($hash, 1); + + + ($error, $hash->{helper}{app_id}) = getKeyValue("$nom.app_id"); + $hash->{helper}{app_id} = "None" if (!defined($hash->{helper}{app_id})); + + ($error, $value) = getKeyValue("$nom.session_IV"); + if (defined($value)) {$hash->{helper}{session_IV} = decode_base64($value)} + else {$hash->{helper}{session_IV} = "None"} + + ($error, $value) = getKeyValue("$nom.session_key"); + if (defined($value)) {$hash->{helper}{session_key} = decode_base64($value)} + else {$hash->{helper}{session_key} = "None"} + + ($error, $value) = getKeyValue("$nom.session_hmac_key"); + if (defined($value)) {$hash->{helper}{session_hmac_key} = decode_base64($value)} + else {$hash->{helper}{session_hmac_key} = "None"} + + + CommandAttr(undef,$name." webCmd off") if( !defined( AttrVal($hash->{NAME}, "webCmd", undef)) ); BlockingKill($hash->{helper}{RUNNING_PID_GET}) if(defined($hash->{helper}{RUNNING_PID_GET})); delete($hash->{helper}{RUNNING_PID_GET}); RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{helper}{INTERVAL}, "VIERA_GetStatus", $hash, 0); + InternalTimer(gettimeofday()+5, "VIERA_GetStatus", $hash, 0); - Log3 $name, 2, "VIERA: defined with host: $hash->{helper}{HOST} and interval: $hash->{helper}{INTERVAL}"; + Log3 $name, 2, "VIERA: defined with host: $hash->{helper}{HOST} interval: $hash->{helper}{INTERVAL} PIN: $hash->{helper}{pincode} "; readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } @@ -182,17 +252,21 @@ sub VIERA_Define($$){ # Param1: Hash of FHEM-Device # Param2: String of 'set' command # Return: Help text for FHEMWEB -sub VIERA_Set($@){ +sub VIERA_Set($@) { my ($hash, @a) = @_; + my $name = $hash->{NAME}; my $host = $hash->{helper}{HOST}; my $count = @a; my $key = ""; my $tab = ""; - my $usage = "choose one of off:noArg mute:on,off " . + my $usage = "choose one of ". + "off:noArg ". + "mute:on,off ". + "volume:slider,0,1,100 ". + "channel ". "remoteControl:" . join(",", sort keys %VIERA_remoteControl_args) . " " . - "volume:slider,0,1,100 volumeUp:noArg volumeDown:noArg ". - "channel channelUp:noArg channelDown:noArg input:hdmi1,hdmi2,hdmi3,hdmi4,sdCard,tv"; + "input:hdmi1,hdmi2,hdmi3,hdmi4,sdCard,tv"; $usage =~ s/(NRC_|-ONOFF)//g; my $what = lc($a[1]); @@ -212,34 +286,21 @@ sub VIERA_Set($@){ VIERA_connection($hash, VIERA_BuildXML_RendCtrl($hash, "Set", "Volume", $state)); VIERA_GetStatus($hash, 1); } - elsif ($what eq "volumeup"){ - return "VIERA: Volume range is too high!" if(ReadingsVal($name, "volume", "0") > 100); - Log3 $name, 3, "VIERA: Set volumeUp"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"VOLUP")); - VIERA_GetStatus($hash, 1); - } - elsif ($what eq "volumedown"){ - return "VIERA: Volume range is too low!" if(ReadingsVal($name, "volume", "0") < 1); - Log3 $name, 3, "VIERA: Set volumeDown"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"VOLDOWN")); - VIERA_GetStatus($hash, 1); + elsif ($what eq "off"){ + Log3 $name, 3, "VIERA: Set off"; + VIERA_Encrypted_Command($hash, "POWER"); + VIERA_Encrypt_Answer($hash); } elsif ($what eq "channel"){ return "VIERA: Channel is too high or low!" if($state < 1 || $state > 9999); Log3 $name, 3, "VIERA: Set channel $state"; for(my $i = 0; $i <= length($state)-1; $i++) { - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"D" . substr($state, $i, 1))); + VIERA_Encrypted_Command($hash, "D".substr($state, $i, 1)); sleep 0.1; + VIERA_Encrypt_Answer($hash); } - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"ENTER")); - } - elsif ($what eq "channelup"){ - Log3 $name, 3, "VIERA: Set channelUp"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"CH_UP")); - } - elsif ($what eq "channeldown"){ - Log3 $name, 3, "VIERA: Set channelDown"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"CH_DOWN")); + VIERA_Encrypted_Command($hash, "ENTER"); + VIERA_Encrypt_Answer($hash); } elsif ($what eq "remotecontrol"){ if($state eq "?"){ @@ -254,23 +315,21 @@ sub VIERA_Set($@){ else{ $state = uc($state); Log3 $name, 3, "VIERA: Set remoteControl $state"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,$state)); + VIERA_Encrypted_Command($hash, $state); + VIERA_Encrypt_Answer($hash); } } - elsif ($what eq "off"){ - Log3 $name, 3, "VIERA: Set off"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,"POWER")); - } - elsif ($what eq "statusrequest"){ - Log3 $name, 3, "VIERA: Set statusRequest"; - VIERA_GetStatus($hash, 1); - } elsif ($what eq "input"){ $state = uc($state); return "VIERA: Input $state isn't available." if($state ne "HDMI1" && $state ne "HDMI2" && $state ne "HDMI3" && $state ne "HDMI4" && $state ne "SDCARD" && $state ne "TV"); $state = "SD_CARD" if ($state eq "SDCARD"); Log3 $name, 3, "VIERA: Set input $state"; - VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash,$state)); + VIERA_Encrypted_Command($hash, $state); + VIERA_Encrypt_Answer($hash); + } + elsif ($what eq "statusrequest"){ + Log3 $name, 3, "VIERA: Set statusRequest"; + VIERA_GetStatus($hash, 1); } elsif ($what eq "?"){ return "$usage"; @@ -286,17 +345,18 @@ sub VIERA_Set($@){ # Param1: Hash of FHEM-Device # Param2: String of 'set' command # Return: Help text for FHEMWEB -sub VIERA_Get($@){ +sub VIERA_Get($@) { my ($hash, @a) = @_; + my $what; - my $usage = "choose one of mute:noArg volume:noArg power:noArg presence:noArg"; + my $usage = "choose one of mute:noArg volume:noArg presence:noArg"; my $name = $hash->{NAME}; return "VIERA: No argument given, $usage" if(int(@a) != 2); $what = lc($a[1]); - if($what =~ /^(volume|mute|power|presence)$/) { + if($what =~ /^(volume|mute|presence)$/) { if (defined($hash->{READINGS}{$what})) { ReadingsVal($name, $what, "undefined"); } @@ -313,12 +373,13 @@ sub VIERA_Get($@){ # Param1: Hash of FHEM-Device # Param2: Name of FHEM-Device # Return: undef -sub VIERA_Undefine($$){ +sub VIERA_Undefine($$) { my($hash, $name) = @_; - # Stop the internal GetStatus-Loop and exist + # Stop the internal GetStatus-Loop and exit RemoveInternalTimer($hash); BlockingKill($hash->{helper}{RUNNING_PID_GET}) if(defined($hash->{helper}{RUNNING_PID_GET})); + delete($hash->{helper}{RUNNING_PID_GET}); return undef; } @@ -326,8 +387,9 @@ sub VIERA_Undefine($$){ # Function is called periodically by InternalTimer and fetch informations from device. The decision if blocking or nonBlocking is used is made here. # Param1: Hash of FHEM-Device # Param2: Optional, if set to 1 fetch information from device without interrupting InternalTimer -sub VIERA_GetStatus($;$){ +sub VIERA_GetStatus($$) { my ($hash, $local) = @_; + my $name = $hash->{NAME}; my $host = $hash->{helper}{HOST}; my $blocking = AttrVal($name, "blocking", 0); #use non-blocking in standard. Just use blocking when set by attr @@ -337,11 +399,15 @@ sub VIERA_GetStatus($;$){ InternalTimer(gettimeofday()+$hash->{helper}{INTERVAL}, "VIERA_GetStatus", $hash, 0) unless($local == 1); return "" if(!defined($hash->{helper}{HOST}) or !defined($hash->{helper}{INTERVAL})); + + return if ($hash->{helper}{stop} eq "yes"); + + VIERA_CeckEncryption($hash) if ($hash->{helper}{ENCRYPTION} eq "?"); if ($blocking == 0) { Log3 $name, 4, "VIERA[VIERA_GetStatus]: Using non blocking..."; $hash->{helper}{RUNNING_PID_GET} = BlockingCall("VIERA_GetDoIt", $hash, "VIERA_GetDone", 10, "VIERA_GetAbortFn", $hash) unless(exists($hash->{helper}{RUNNING_PID_GET})); - if($hash->{helper}{RUNNING_PID_GET}) { + if($hash->{helper}{RUNNING_PID_GET}) { Log3 $name, 4, "VIERA[VIERA_GetStatus]: VIERA_GetDoIt() BlockingCall process started with PID $hash->{helper}{RUNNING_PID_GET}{pid}"; } else { @@ -354,10 +420,11 @@ sub VIERA_GetStatus($;$){ Log3 $name, 5, "VIERA[VIERA_GetStatus]: Vol-Request returned: $returnVol" if(defined($returnVol)); if(not defined($returnVol) or $returnVol eq "") { Log3 $name, 4, "VIERA[VIERA_GetStatus]: Vol-Request NO SOCKET!"; - if( ReadingsVal($name,"state","absent") ne "absent") { + if( ReadingsVal($name,"state","off") ne "off") { + $hash->{helper}{session_seq_num} = "None"; readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", "absent"); - readingsBulkUpdate($hash, "power", "off"); + readingsBulkUpdate($hash, "state", "off"); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); readingsBulkUpdate($hash, "presence", "absent"); readingsEndUpdate($hash, 1); } @@ -368,10 +435,11 @@ sub VIERA_GetStatus($;$){ Log3 $name, 5, "VIERA[VIERA_GetStatus]: Mute-Request returned: $returnMute" if(defined($returnMute)); if(not defined($returnMute) or $returnMute eq "") { Log3 $name, 4, "VIERA[VIERA_GetStatus]: Mute-Request NO SOCKET!"; - if( ReadingsVal($name,"state","absent") ne "absent") { + if( ReadingsVal($name,"state","off") ne "off") { + $hash->{helper}{session_seq_num} = "None"; readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", "absent"); - readingsBulkUpdate($hash, "power", "off"); + readingsBulkUpdate($hash, "state", "off"); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); readingsBulkUpdate($hash, "presence", "absent"); readingsEndUpdate($hash, 1); } @@ -390,9 +458,8 @@ sub VIERA_GetStatus($;$){ Log3 $name, 4, "VIERA[VIERA_GetStatus]: Set reading mute to $myMute"; if( $myMute ne ReadingsVal($name, "mute", "0") ) {readingsBulkUpdate($hash, "mute", $myMute);} } - if( ReadingsVal($name,"state","absent") ne "on") { + if( ReadingsVal($name,"state","off") ne "on") { readingsBulkUpdate($hash, "state", "on"); - readingsBulkUpdate($hash, "power", "on"); readingsBulkUpdate($hash, "presence", "present"); } readingsEndUpdate($hash, 1); @@ -404,18 +471,19 @@ sub VIERA_GetStatus($;$){ # param1: Hash of FHEM-Device # param2: RAW Data #return: RAW answer when successful or undef if no socket is available. -sub VIERA_connection($$){ +sub VIERA_connection($$) { my ($hash, $data) = @_; + my $name = $hash->{NAME}; my $buffer = ""; my $buff = ""; my $blocking = "NonBlocking-VIERA_connection()" if (AttrVal($name, "blocking", 0) == 0); - $blocking = "Blocking-VIERA_connection()" if (AttrVal($name, "blocking", 0) == 1); + $blocking = "Blocking-VIERA_connection()" if (AttrVal($name, "blocking", 0) == 1); my $sock = new IO::Socket::INET ( PeerAddr => $hash->{helper}{HOST}, PeerPort => $hash->{helper}{PORT}, - Proto => 'tcp', + Proto => "tcp", Timeout => 2 ); @@ -428,7 +496,8 @@ sub VIERA_connection($$){ } Log3 $hash, 5, "VIERA[$blocking]: $hash->{helper}{HOST} buffer response:\n$buffer"; - $sock->close(); + close($sock); + $hash->{helper}{BUFFER} = $buffer; return $buffer; } else { @@ -437,142 +506,72 @@ sub VIERA_connection($$){ } } -# Callback from 95_remotecontrol for command makenotify. -# Param1: Name of remoteControl device -# Param2: Name of target FHEM device -sub VIERA_RCmakenotify($$) { - my ($nam, $ndev) = @_; - my $nname="notify_$nam"; - - fhem("define $nname notify $nam set $ndev remoteControl ".'$EVENT',1); - Log3 undef, 2, "[remoteControl:VIERA] Notify created: $nname"; - return "Notify created by VIERA: $nname"; -} - -# Callback from 95_remotecontrol for command layout. Creates non svg layout -sub VIERA_RClayout_TV() { - my @row; - my $i = 0; - - $row[$i++]="power:POWEROFF2, TV, CHG_INPUT:HDMI"; - $row[$i++]="MENU, disp_mode:ASPECT, epg:GUIDE"; - $row[$i++]="VIERA_LINK, VTOOLS, INTERNET"; - $row[$i++]=":blank, :blank, :blank"; - $row[$i++]="INFO:INFO2, UP, cancel:EXIT"; - $row[$i++]="LEFT, ENTER, RIGHT"; - $row[$i++]="SUBMENU, DOWN, RETURN"; - $row[$i++]="red:RED, :blank, green:GREEN"; - $row[$i++]="yellow:YELLOW, :blank, blue:BLUE"; - $row[$i++]="d1:1, d2:2, d3:3"; - $row[$i++]="d4:4, d5:5, d6:6"; - $row[$i++]="d7:7, d8:8, d9:9"; - $row[$i++]="MUTE, d0:0, r_tune:PRECH"; - $row[$i++]=":blank, :blank, :blank"; - $row[$i++]="VOLUP, :blank, ch_up:CHUP"; - $row[$i++]=":VOL, :blank, :PROG"; - $row[$i++]="VOLDOWN, :blank, ch_down:CHDOWN"; - $row[$i++]=":blank, :blank, :blank"; - $row[$i++]="rew:REWIND, PLAY, FF"; - $row[$i++]="STOP, PAUSE, REC"; - - #Replace two or more spaces with one space - for (@row) {s/\s+/ /g} - - $row[$i++]="attr rc_iconpath icons/remotecontrol"; - $row[$i++]="attr rc_iconprefix black_btn_"; - - return @row; -} - -# Callback from 95_remotecontrol for command layout. Creates svg layout -sub VIERA_RClayout_TV_SVG() { - my @row; - my $i = 0; - - $row[$i++]="power:rc_POWER.svg, TV:rc_TV2.svg, CHG_INPUT:rc_AV.svg"; - $row[$i++]="MENU:rc_MENU.svg, disp_mode:rc_ASPECT.svg, epg:rc_EPG.svg"; - $row[$i++]="VIERA_LINK:rc_VIERA_LINK.svg, VTOOLS:rc_VIERA_TOOLS.svg, INTERNET:rc_WEB.svg"; - $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; - $row[$i++]="INFO:rc_INFO2.svg, UP:rc_UP.svg, cancel:rc_EXIT.svg"; - $row[$i++]="LEFT:rc_LEFT.svg, ENTER:rc_dot.svg, RIGHT:rc_RIGHT.svg"; - $row[$i++]="SUBMENU:rc_OPTIONS.svg, DOWN:rc_DOWN.svg, RETURN:rc_BACK.svg"; - $row[$i++]="red:rc_RED.svg, :rc_BLANK.svg, green:rc_GREEN.svg"; - $row[$i++]="yellow:rc_YELLOW.svg, :rc_BLANK.svg, blue:rc_BLUE.svg"; - $row[$i++]="d1:rc_1.svg, d2:rc_2.svg, d3:rc_3.svg"; - $row[$i++]="d4:rc_4.svg, d5:rc_5.svg, d6:rc_6.svg"; - $row[$i++]="d7:rc_7.svg, d8:rc_8.svg, d9:rc_9.svg"; - $row[$i++]="MUTE:rc_MUTE.svg, d0:rc_0.svg, r_tune:rc_BACK.svg"; - $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; - $row[$i++]="VOLUP:rc_UP.svg, :rc_BLANK.svg, ch_up:rc_UP.svg"; - $row[$i++]=":rc_VOL.svg, :rc_BLANK.svg, :rc_PROG.svg"; - $row[$i++]="VOLDOWN:rc_DOWN.svg, :rc_BLANK.svg, ch_down:rc_DOWN.svg"; - $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; - $row[$i++]="rew:rc_REW.svg, PLAY:rc_PLAY.svg, FF:rc_FF.svg"; - $row[$i++]="STOP:rc_STOP.svg, PAUSE:rc_PAUSE.svg, REC:rc_REC.svg"; - - #Replace two or more spaces with one space - for (@row) {s/\s+/ /g} - return @row; -} # Create RAW Data to sent pressed keys of remoteControl to device. # Param1: Hash of FHEM-Device # Param2: Name of key to send # Return: RAW html request for xml soap -sub VIERA_BuildXML_NetCtrl($$){ +sub VIERA_BuildXML_NetCtrl($$) { my ($hash, $command) = @_; + my $host = $hash->{helper}{HOST}; my $port = $hash->{helper}{PORT}; my $callsoap = ""; my $message = ""; my $head = ""; - my $size = ""; + my $blen = ""; - $callsoap .= ""; - $callsoap .= ""; - $callsoap .= ""; - $callsoap .= ""; - $callsoap .= "NRC_$command-ONOFF"; - $callsoap .= ""; - $callsoap .= ""; - $callsoap .= ""; + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; - $size = length($callsoap); + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + $callsoap .= "NRC_$command-ONOFF\r\n"; + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + + $blen = length($callsoap); $head .= "POST /nrc/control_0 HTTP/1.1\r\n"; $head .= "Host: $host:$port\r\n"; - $head .= "SOAPACTION: \"urn:panasonic-com:service:p00NetworkControl:1#X_SendKey\"\r\n"; $head .= "Content-Type: text/xml; charset=\"utf-8\"\r\n"; - $head .= "Content-Length: $size\r\n"; + $head .= "SOAPAction: \"urn:panasonic-com:service:p00NetworkControl:1#X_SendKey\"\r\n"; + $head .= "Content-Length: $blen\r\n"; $head .= "\r\n"; $message .= $head; $message .= $callsoap; - #Log3 $hash, 5, "VIERA: Building XML SOAP (NetworkControl) for command $command to host $host:\n$message"; +# Log3 $hash, 5, "VIERA: Building XML SOAP (NetworkControl) for command $command to host $host:\n$message"; return $message; } + # Create RAW Data to send or get volume/mute state # Param1: Hash of FHEM-Device # Param2: get|set # Param3: volume|mute # Param4: value for set command # Return: RAW html request for xml soap -sub VIERA_BuildXML_RendCtrl($$$$){ +sub VIERA_BuildXML_RendCtrl($$$$) { my ($hash, $methode, $command, $value) = @_; + my $host = $hash->{helper}{HOST}; my $port = $hash->{helper}{PORT}; my $callsoap = ""; my $message = ""; my $head = ""; - my $size = ""; + my $blen = ""; - #Log3 $hash, 5, "VIERA: $command with $value to $host"; +# Log3 $hash, 5, "VIERA: $command with $value to $host"; $callsoap .= "\r\n"; - $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; $callsoap .= "\r\n"; $callsoap .= "0\r\n"; @@ -582,18 +581,111 @@ sub VIERA_BuildXML_RendCtrl($$$$){ $callsoap .= "\r\n"; $callsoap .= "\r\n"; - $size = length($callsoap); + $blen = length($callsoap); - $head .= "POST /dmr/control_0 HTTP/1.0\r\n"; + $head .= "POST /dmr/control_0 HTTP/1.1\r\n"; $head .= "Host: $host:$port\r\n"; - $head .= "SOAPACTION: \"urn:schemas-upnp-org:service:RenderingControl:1#$methode$command\"\r\n"; $head .= "Content-Type: text/xml; charset=\"utf-8\"\r\n"; - $head .= "Content-Length: $size\r\n"; + $head .= "SOAPAction: \"urn:schemas-upnp-org:service:RenderingControl:1#$methode$command\"\r\n"; + $head .= "Content-Length: $blen\r\n"; $head .= "\r\n"; $message .= $head; $message .= $callsoap; - #Log3 $hash, 5, "VIERA: Building XML SOAP (RenderingControl) for command $command with value $value to host $host:\n$message"; + Log3 $hash, 5, "VIERA: Building XML SOAP (RenderingControl) for command $command with value $value to host $host:\n$message"; + return $message; +} + + +# ####################### create any SOAP Request message encrypted or not ################################# +sub VIERA_Build_soap_message_Encrypt($$$$) { + my ($hash, $action, $params, $body_elem) = @_; + + + my $host = $hash->{helper}{HOST}; + my $port = $hash->{helper}{PORT}; + my $urn = "panasonic-com:service:p00NetworkControl:1"; + + my $callsoap = ""; + my $soapbody = ""; + my $encrypted_command = ""; + my $message = ""; + my $head = ""; + my $blen = ""; + + my $session_id = $hash->{helper}{session_id}; + my $session_seq_num = $hash->{helper}{session_seq_num}; + my $session_hmac_key = $hash->{helper}{session_hmac_key}; + my $session_IV = $hash->{helper}{session_IV}; + my $session_key = $hash->{helper}{session_key}; + + my $is_encrypted = 0; + + if ($action ne "X_GetEncryptSessionId" and $action ne "X_DisplayPinCode" and $action ne "X_RequestAuth") { + if ($session_key ne "None" and $session_IV ne "None" and $session_hmac_key ne "None" and + $session_id ne "None" and $session_seq_num ne "None" ) { + + $is_encrypted = 1; + +# Encapsulate URN_REMOTE_CONTROL command in an X_EncryptedCommand if we're using encryption + + $hash->{helper}{session_seq_num} += 1; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); + readingsEndUpdate($hash, 1); + + $session_seq_num = sprintf("%08d", $session_seq_num); + + $encrypted_command .= "$session_id\r\n"; + $encrypted_command .= "$session_seq_num\r\n"; + $encrypted_command .= "\r\n"; + $encrypted_command .= "<$body_elem:$action xmlns:$body_elem=\"urn:$urn\">\r\n"; + $encrypted_command .= "$params\r\n"; + $encrypted_command .= "\r\n"; + $encrypted_command .= "\r\n"; + + $encrypted_command = VIERA_encrypt_soap_payload($encrypted_command, $session_key, $session_IV, $session_hmac_key); + + $action = "X_EncryptedCommand"; + my $app_id = $hash->{helper}{app_id}; + $params = "$app_id\r\n"; + $params .= "$encrypted_command"; + + $body_elem = "u"; + } + } + +# Construct SOAP request + + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + + $callsoap .= "\r\n"; + + $callsoap .= "<$body_elem:$action xmlns:$body_elem=\"urn:$urn\">\r\n"; + $callsoap .= "$params\r\n"; + $callsoap .= "\r\n"; + + $callsoap .= "\r\n"; + $callsoap .= "\r\n"; + + $blen = length($callsoap); + + $head .= "POST /nrc/control_0 HTTP/1.1\r\n"; + $head .= "Host: $host:$port\r\n"; + $head .= "Content-Type: text/xml; charset=\"utf-8\"\r\n"; + $head .= "SOAPAction: \"urn:panasonic-com:service:p00NetworkControl:1#$action\"\r\n"; + $head .= "Content-Length: $blen\r\n"; + $head .= "\r\n"; + + $message .= $head; + $message .= $callsoap; +# Log3 $hash, 5, "VIERA: Building XML SOAP (NetworkControl) for command $command to host $host:\n$message"; + + $hash->{helper}{is_encrypted} = $is_encrypted; + return $message; } @@ -602,6 +694,7 @@ sub VIERA_BuildXML_RendCtrl($$$$){ # Return: || sub VIERA_GetDoIt($) { my ($hash) = @_; + my $myVol = ""; my $myMute = ""; @@ -660,6 +753,7 @@ sub VIERA_GetDoIt($) { #################################################### sub VIERA_GetDone($) { my ($string) = @_; + return unless(defined($string)); my @a = split("\\|",$string); @@ -673,10 +767,11 @@ sub VIERA_GetDone($) { if ($myVol =~ /error-(.*)/ || $myMute =~ /error-(.*)/) { if ($1 eq "noSocket") { Log3 $name, 4, "VIERA[NonBlocking-VIERA_GetDone()]: Seems to be there is no socket available. Guessing TV is off!"; - if (ReadingsVal($name,"state","absent") ne "absent") { + if (ReadingsVal($name,"state","off") ne "off") { + $hash->{helper}{session_seq_num} = "None"; readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", "absent"); - readingsBulkUpdate($hash, "power", "off"); + readingsBulkUpdate($hash, "state", "off"); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); readingsBulkUpdate($hash, "presence", "absent"); readingsEndUpdate($hash, 1); } @@ -685,6 +780,7 @@ sub VIERA_GetDone($) { } else { Log3 $name, 3, "VIERA[NonBlocking-VIERA_GetDone()]: TV answered with $1. Seems to be on but delivering no data"; + delete($hash->{helper}{RUNNING_PID_GET}); return; } } @@ -700,9 +796,8 @@ sub VIERA_GetDone($) { readingsBulkUpdate($hash, "mute", $myMute); } - if (ReadingsVal($name,"state","absent") ne "on") { + if (ReadingsVal($name,"state","off") ne "on") { readingsBulkUpdate($hash, "state", "on"); - readingsBulkUpdate($hash, "power", "on"); readingsBulkUpdate($hash, "presence", "present"); } readingsEndUpdate($hash, 1); @@ -713,16 +808,480 @@ sub VIERA_GetDone($) { # Callback of non blocking when function VIERA_GetDoIt runs into timeout. # Param1: Hash of FHEM-Device # Return: no return code -sub VIERA_GetAbortFn($) { +sub VIERA_GetAbortFn($) { my ($hash) = @_; delete($hash->{helper}{RUNNING_PID_GET}); Log3 $hash, 2, "VIERA[NonBlocking-VIERA_GetAbortFn()]: BlockingCall for $hash->{NAME} was aborted, timeout reached"; + return; } +################ special encryption ########################################################### + +###################################################################################################################### + + +sub VIERA_Encrypted_Command($$) { + my ($hash, $command) = @_; + + if ($hash->{helper}{ENCRYPTION} eq "yes") { + + if ($hash->{helper}{pincode} eq "0000") {return VIERA_request_pin_code($hash)}; + + if ($hash->{helper}{stop} eq "yes") {return 0 if (!VIERA_authorize_pin_code($hash))}; + + if ($hash->{helper}{session_seq_num} eq "None") {return 0 if (!VIERA_request_session_id($hash))}; + + + my $params = "NRC_$command-ONOFF"; + my $message = VIERA_Build_soap_message_Encrypt($hash, "X_SendKey", $params, "u"); + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, $message); + } + else { + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash, $command)); + } + + return; +} + + +sub VIERA_Encrypt_Answer($) { + my ($hash) = @_; + + my $answer = ""; + $answer = $hash->{helper}{BUFFER} if ($hash->{helper}{BUFFER} ne ""); + + if (index($answer, "HTTP/1.1 200 OK") == -1) { + if ($hash->{helper}{session_seq_num} ne "None") { + $hash->{helper}{session_seq_num} -= 1; + } + Log3 $hash, 3, "wrong encrypted VIERA command: \r\n\"$answer\""; + return undef; + } + + if ($hash->{helper}{is_encrypted}) { + my $iS = index($answer, ""); + my $iE = index($answer, ""); + $answer = substr($answer, $iS+13, $iE-$iS-13); + + $answer = VIERA_decrypt_soap_payload($answer, $hash->{helper}{session_key}, $hash->{helper}{session_IV}); + } + + return $answer; +} + +#check if TV is encrypted +sub VIERA_CeckEncryption($) { + my ($hash) = @_; + + my $nom = $hash->{TYPE}."_".$hash->{NAME}."_"; + + my $answer = ""; + my $iS = -1; + + + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, VIERA_BuildXML_NetCtrl($hash, "INFO")); + + + if ($hash->{helper}{BUFFER} ne "") { + $answer = $hash->{helper}{BUFFER} ; + $iS = index($answer, "401"); + if ( $iS != -1) { + $hash->{helper}{ENCRYPTION} = "yes"; + } + else { + $hash->{helper}{ENCRYPTION} = "no"; + } + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "Encryption", $hash->{helper}{ENCRYPTION}); + readingsEndUpdate($hash, 1); + + my $error = setKeyValue("$nom.ENCRYPTION", $hash->{helper}{ENCRYPTION}); + + return; +} + + +# Calculate encryption + +sub VIERA_derive_session_keys($) { + my ($hash) = @_; + + my $iv =""; + my @iv_vals ; + my @key_vals ; + my $session_key = ""; + my $session_hmac_key = ""; + + my $nom = $hash->{TYPE}."_".$hash->{NAME}."_"; + + my $i = ""; + +# decode ChallengeKey + $iv = decode_base64($hash->{helper}{enc_key}); + $hash->{helper}{session_IV} = $iv; + + for($i = 0; $i < 16; $i++) {$iv_vals[$i] = ord(substr($iv, $i, 1))}; # get unicode for characters + + for($i = 0; $i < 16; $i++) {$key_vals[$i] = 0}; +# Derive key from IV + $i = 0 ; + while ($i < 16) { + $key_vals[$i] = $iv_vals[$i + 2]; + $key_vals[$i + 1] = $iv_vals[$i + 3]; + $key_vals[$i + 2] = $iv_vals[$i + 0]; + $key_vals[$i + 3] = $iv_vals[$i + 1]; + $i += 4; + } + +# Convert key character codes to bytes + $session_key = ""; + for($i = 0; $i < 16; $i++) {$session_key .= chr($key_vals[$i] & 0xFF)}; + $hash->{helper}{session_key} = $session_key; + + $i = length ($session_key); + +# HMAC key for comms is just the IV repeated twice + $session_hmac_key = $iv.$iv; + $hash->{helper}{session_hmac_key} = $session_hmac_key; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "app_id", $hash->{helper}{app_id}); + readingsEndUpdate($hash, 1); + + my $error = ""; + my $value = ""; + $error = setKeyValue("$nom.app_id", $hash->{helper}{app_id}); + + $value = encode_base64($hash->{helper}{session_IV}, ""); + $error = setKeyValue("$nom.session_IV", $value); + + $value = encode_base64($hash->{helper}{session_key}, ""); + $error = setKeyValue("$nom.session_key", $value); + + $value = encode_base64($hash->{helper}{session_hmac_key}, ""); + $error = setKeyValue("$nom.session_hmac_key", $value); + + + return; +} + +# The encrypted payload must begin with a 16-byte header (12 random bytes, and 4 bytes for the payload length in big endian) +# Note: the server does not appear to ever send back valid payload lengths in bytes 13-16, so I would assume these can also +# be randomized by the client, but we'll set them anyway to be safe. + +sub VIERA_encrypt_soap_payload($$$$) { + my ($message, $key, $IV, $hmac_key) = @_; + + my $i = ""; + my $len = ""; + my $range = 255; + my $payload = ""; + for($i = 0; $i < 12; $i++) {$payload .= chr( int(rand($range)) )}; +# for($i = 0; $i < 12; $i++) {$payload .= "A"}; + + $len = length($message); + $payload .= pack("N", $len); # make big endian +# $payload .= pack("NA*", $len, $message); +# $len = (($len & 0x000000FF) + ($len & 0x0000FF00) * 2**8 + ($len & 0x00FF0000) * 2**16 + ($len & 0xFF000000) * 2**24); + $payload .= $message; + + +# Initialize AES + my $cbc = Crypt::Mode::CBC->new("AES", 4); + + my $ciphertext = $cbc->encrypt($payload, $key, $IV); + + my $sig = hmac_sha256($ciphertext, $hmac_key); + +# Concat HMAC with AES-encrypted payload + + my $encrypttext = $ciphertext.$sig; + $encrypttext = encode_base64($encrypttext, ""); + + return $encrypttext; + + +} + +sub VIERA_decrypt_soap_payload($$$) { + my ($message, $key, $IV) = @_; + +# Initialize AES + my $cbc = Crypt::Mode::CBC->new("AES", 4); + + $message = decode_base64($message); + +# Decrypt + my $decrypted = $cbc->decrypt($message, $key, $IV); + +# Unpad and return + my $i = length($decrypted) ; + $decrypted = substr($decrypted, 16, $i-16); + $i = index($decrypted, chr(0x00)); + $decrypted = substr($decrypted, 0, $i); + + return $decrypted ; + +} + +sub VIERA_request_pin_code($) { + my ($hash) = @_; + + my $nom = $hash->{TYPE}."_".$hash->{NAME}."_"; + +# Stop communication with TV until PIN authorized + $hash->{helper}{stop} = "yes"; + my $error = setKeyValue("$nom.stop", $hash->{helper}{stop}); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "stop", $hash->{helper}{stop}); + readingsEndUpdate($hash, 1); + +# First let's ask for a pin code and get a challenge key back + my $params = "FHEMremote"; + my $message = VIERA_Build_soap_message_Encrypt($hash, "X_DisplayPinCode", $params, "u"); + + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, $message); + + my $answer = VIERA_Encrypt_Answer($hash); + if (!defined ($answer)) {return 0}; + + my $iS = index($answer, ""); + my $iE = index($answer, ""); + if ($iS == -1) {return 0}; + $answer = substr($answer, $iS+16, $iE-$iS-16); + + $hash->{helper}{challenge} = decode_base64($answer); + + return 1; +} + + +sub VIERA_authorize_pin_code($) { + my ($hash) = @_; + + + my $i = 0; + my $iS = 0; + my $iE = 0; + my $pincode = $hash->{helper}{pincode}; + + my $nom = $hash->{TYPE}."_".$hash->{NAME}."_"; + +# Second, let's encrypt the pin code using the challenge key and send it back to authenticate + +# Derive key from IV + my $iv = $hash->{helper}{challenge}; + + my $key = ""; + $i = 0; + while ($i < 16) { + $key .= chr(~ord(substr($iv, $i+3, 1)) & 0xFF); + $key .= chr(~ord(substr($iv, $i+2, 1)) & 0xFF); + $key .= chr(~ord(substr($iv, $i+1, 1)) & 0xFF); + $key .= chr(~ord(substr($iv, $i , 1)) & 0xFF); + $i += 4; + } + +# Derive HMAC key from IV & HMAC key mask (taken from libtvconnect.so) + my @hmac_key_mask_vals = (0x15,0xC9,0x5A,0xC2,0xB0,0x8A,0xA7,0xEB,0x4E,0x22,0x8F,0x81,0x1E,0x34,0xD0,0x4F, + 0xA5,0x4B,0xA7,0xDC,0xAC,0x98,0x79,0xFA,0x8A,0xCD,0xA3,0xFC,0x24,0x4F,0x38,0x54); + + my $hmac_key = ""; + $i = 0; + while ($i < 32) { + $hmac_key .= chr($hmac_key_mask_vals[$i] ^ ord(substr($iv, (($i + 2) & 0x0F), 1))); + $hmac_key .= chr($hmac_key_mask_vals[$i + 1] ^ ord(substr($iv, (($i + 3) & 0x0F), 1))); + $hmac_key .= chr($hmac_key_mask_vals[$i + 2] ^ ord(substr($iv, (($i ) & 0x0F), 1))); + $hmac_key .= chr($hmac_key_mask_vals[$i + 3] ^ ord(substr($iv, (($i + 1) & 0x0F), 1))); + $i += 4; + } + +# Encrypt X_PinCode argument and send it within an X_AuthInfo tag + my $params = ""; + $params .= VIERA_encrypt_soap_payload("$pincode", $key, $iv, $hmac_key); + $params .= ""; + my $message = VIERA_Build_soap_message_Encrypt($hash, "X_RequestAuth", $params, "u"); + + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, $message); + + # communication can start again + $hash->{helper}{stop} = "no"; + my $error = setKeyValue("$nom.stop", $hash->{helper}{stop}); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "stop", $hash->{helper}{stop}); + readingsEndUpdate($hash, 1); + + my $answer = VIERA_Encrypt_Answer($hash); + if (!defined ($answer)) {return 0}; + +# Parse and decrypt X_AuthResult + + $iS = index($answer, ""); + $iE = index($answer, ""); + $answer = substr($answer, $iS+14, $iE-$iS-14); + + $answer = VIERA_decrypt_soap_payload($answer, $key, $iv); + + # Set session application ID and encryption key + $iS = index($answer, ""); + $iE = index($answer, ""); + $hash->{helper}{app_id} = substr($answer, $iS+17, $iE-$iS-17); + + $iS = index($answer, ""); + $iE = index($answer, ""); + $hash->{helper}{enc_key} = substr($answer, $iS+11, $iE-$iS-11); + +# Derive AES & HMAC keys from X_Keyword + VIERA_derive_session_keys($hash); + + return 1; +} + +sub VIERA_request_session_id($) { + my ($hash) = @_; + +# Thirdly, let's ask for a session. We'll need to use a valid session ID for encrypted NRC commands. + +# We need to send an encrypted version of X_ApplicationId + my $app_id = $hash->{helper}{app_id}; + my $session_key = $hash->{helper}{session_key}; + my $session_IV = $hash->{helper}{session_IV}; + my $session_hmac_key = $hash->{helper}{session_hmac_key}; + + my $encinfo = VIERA_encrypt_soap_payload("$app_id", $session_key, $session_IV, $session_hmac_key); + +# Send the encrypted SOAP request along with plain text X_ApplicationId + my $params = "$app_id$encinfo"; + + my $message = VIERA_Build_soap_message_Encrypt($hash, "X_GetEncryptSessionId", $params, "u"); + + $hash->{helper}{BUFFER} = ""; + VIERA_connection($hash, $message); + + my $answer = VIERA_Encrypt_Answer($hash); + if (!defined ($answer)) {return 0}; + + my $iS = index($answer, ""); + my $iE = index($answer, ""); + $answer = substr($answer, $iS+13, $iE-$iS-13); + + $answer = VIERA_decrypt_soap_payload($answer, $session_key, $session_IV); + +# Set session ID and begin sequence number at 1. We have to increment the sequence number upon each successful NRC command. + $iS = index($answer, ""); + $iE = index($answer, ""); + $hash->{helper}{session_id} = substr($answer, $iS+13, $iE-$iS-13); + + $hash->{helper}{session_seq_num} = 1; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "session_id", $hash->{helper}{session_id}); + readingsBulkUpdate($hash, "Sequence", $hash->{helper}{session_seq_num}); + readingsEndUpdate($hash, 1); + + return 1; +} + +###################################### end of special encrytion commands ############################# + + +# Callback from 95_remotecontrol for command makenotify. +# Param1: Name of remoteControl device +# Param2: Name of target FHEM device +sub VIERA_RCmakenotify($$) { + my ($nam, $ndev) = @_; + + my $nname="notify_$nam"; + + fhem("define $nname notify $nam set $ndev remoteControl ".'$EVENT',1); + Log3 undef, 2, "[remoteControl:VIERA] Notify created: $nname"; + return "Notify created by VIERA: $nname"; +} + +# Callback from 95_remotecontrol for command layout. Creates non svg layout +sub VIERA_RClayout_TV() { + my @row; + my $i = 0; + + $row[$i++]="power:POWEROFF2, TV, CHG_INPUT:HDMI"; + $row[$i++]="MENU, disp_mode:ASPECT, epg:GUIDE"; + $row[$i++]="VIERA_LINK, VTOOLS, INTERNET"; + $row[$i++]=":blank, :blank, :blank"; + $row[$i++]="INFO:INFO2, UP, cancel:EXIT"; + $row[$i++]="LEFT, ENTER, RIGHT"; + $row[$i++]="SUBMENU, DOWN, RETURN"; + $row[$i++]="red:RED, :blank, green:GREEN"; + $row[$i++]="yellow:YELLOW, :blank, blue:BLUE"; + $row[$i++]="d1:1, d2:2, d3:3"; + $row[$i++]="d4:4, d5:5, d6:6"; + $row[$i++]="d7:7, d8:8, d9:9"; + $row[$i++]="MUTE, d0:0, r_tune:PRECH"; + $row[$i++]=":blank, :blank, :blank"; + $row[$i++]="VOLUP, :blank, ch_up:CHUP"; + $row[$i++]=":VOL, :blank, :PROG"; + $row[$i++]="VOLDOWN, :blank, ch_down:CHDOWN"; + $row[$i++]=":blank, :blank, :blank"; + $row[$i++]="rew:REWIND, PLAY, FF"; + $row[$i++]="STOP, PAUSE, REC"; + +# Replace spaces with no space + for (@row) {tr/ //d} + + $row[$i++]="attr rc_iconpath icons/remotecontrol"; + $row[$i++]="attr rc_iconprefix black_btn_"; + + return @row; +} + +# Callback from 95_remotecontrol for command layout. Creates svg layout +sub VIERA_RClayout_TV_SVG() { + my @row; + my $i = 0; + + $row[$i++]="power:rc_POWER.svg, TV:rc_TV2.svg, CHG_INPUT:rc_AV.svg"; + $row[$i++]="MENU:rc_MENU.svg, disp_mode:rc_ASPECT.svg, epg:rc_EPG.svg"; + $row[$i++]="VIERA_LINK:rc_VIERA_LINK.svg, VTOOLS:rc_VIERA_TOOLS.svg, INTERNET:rc_WEB.svg"; + $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; + $row[$i++]="INFO:rc_INFO2.svg, UP:rc_UP.svg, cancel:rc_EXIT.svg"; + $row[$i++]="LEFT:rc_LEFT.svg, ENTER:rc_dot.svg, RIGHT:rc_RIGHT.svg"; + $row[$i++]="SUBMENU:rc_OPTIONS.svg, DOWN:rc_DOWN.svg, RETURN:rc_BACK.svg"; + $row[$i++]="red:rc_RED.svg, :rc_BLANK.svg, green:rc_GREEN.svg"; + $row[$i++]="yellow:rc_YELLOW.svg, :rc_BLANK.svg, blue:rc_BLUE.svg"; + $row[$i++]="d1:rc_1.svg, d2:rc_2.svg, d3:rc_3.svg"; + $row[$i++]="d4:rc_4.svg, d5:rc_5.svg, d6:rc_6.svg"; + $row[$i++]="d7:rc_7.svg, d8:rc_8.svg, d9:rc_9.svg"; + $row[$i++]="MUTE:rc_MUTE.svg, d0:rc_0.svg, r_tune:rc_BACK.svg"; + $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; + $row[$i++]="VOLUP:rc_UP.svg, :rc_BLANK.svg, ch_up:rc_UP.svg"; + $row[$i++]=":rc_VOL.svg, :rc_BLANK.svg, :rc_PROG.svg"; + $row[$i++]="VOLDOWN:rc_DOWN.svg, :rc_BLANK.svg, ch_down:rc_DOWN.svg"; + $row[$i++]=":rc_BLANK.svg, :rc_BLANK.svg, :rc_BLANK.svg"; + $row[$i++]="rew:rc_REW.svg, PLAY:rc_PLAY.svg, FF:rc_FF.svg"; + $row[$i++]="STOP:rc_STOP.svg, PAUSE:rc_PAUSE.svg, REC:rc_REC.svg"; + +# Replace spaces with no space + for (@row) {tr/ //d} + + return @row; +} + + + 1; =pod +=item summary VIERA control Panasonic TV via network +=item summary_DE Steuerung von Panasonic TV über Netzwerk =begin html @@ -731,25 +1290,32 @@ sub VIERA_GetAbortFn($) { Define
    - define <name> VIERA <host> [<interval>] + define <name> VIERA <host> <interval> <pin code> <?>

    - This module controls Panasonic TV device over ethernet. It's possible to - power down the tv, change volume or mute/unmute the TV. Also this modul is simulating - the remote control and you are able to send different command buttons actions of remote control. - The module is tested with Panasonic plasma TV tx-p50vt30e + This module controls Panasonic TV device over ethernet, old TV's and new TV's with crypted communication. + It's possible to change volume, switch it off, mute/unmute the TV or send commands like the ones on the remote control.

    - Defining a VIERA device will schedule an internal task (interval can be set - with optional parameter <interval> in seconds, if not set, the value is 30 - seconds), which periodically reads the status of volume and mute status and triggers - notify/filelog commands. + Defining a VIERA device will schedule an internal task, + which periodically reads the status of volume and mute status and triggers notify/filelog commands. +

    + To implement the module several steps may be needed. + First define the TV with PinCode <0000>, ? for the encryption and any time interval you like (60 is ok). + Switch TV on and wait until the module detects the encyption mode yes/no. If encryption is equal no, you are done. + If encryption is yes execute any command like "set myTV1 off". A PinCode should be displayed on the TV. Edit the definition + delete the "?" and replace 0000 with the PinCode. Execute the command again while the PinCode is still displayed on TV. + You are done.

    Notes:
      Activate volume remotecontrol by DLNA: Menu -> Setup -> Network Setup -> Network Link Settings -> DLNA RemoteVolume -> On

    Example:
      - define myTV1 VIERA 192.168.178.20

      - define myTV1 VIERA 192.168.178.20 60 #with custom interval of 60 seconds + define myTV1 VIERA 192.168.178.20 ## PinCode ? +
      + define myTV1 VIERA 192.168.178.20 60 0000 ? (with custom interval of 60 seconds and start PinCode) +
      + define myTV1 VIERA 192.168.178.20 60 1234 (changed definition with PinCode transfered from TV) +
@@ -765,75 +1331,69 @@ sub VIERA_GetAbortFn($) { off
mute [on|off]
volume [0-100]
- volumeUp
- volumeDown
channel [1-9999]
- channelUp
- channelDown
- statusRequest
- remoteControl <command>
input [hdmi1|hdmi2|hdmi3|hdmi4|sdCard|tv]

    - Remote control (depending on your model, maybe)
    - For this application the following commands are available:
    + Remote control commands, depending on your TV
    + For this application the following commands are available:
      - 3D => 3D button
      - BLUE => Blue
      - CANCEL => Cancel / Exit
      - CHG_INPUT => AV
      - CH_DOWN => Channel down
      - CH_UP => Channel up
      - D0 => Digit 0
      - D1 => Digit 1
      - D2 => Digit 2
      - D3 => Digit 3
      - D4 => Digit 4
      - D5 => Digit 5
      - D6 => Digit 6
      - D7 => Digit 7
      - D8 => Digit 8
      - D9 => Digit 9
      - DISP_MODE => Display mode / Aspect ratio
      - DOWN => Control DOWN
      - ENTER => Control Center click / enter
      - EPG => Guide / EPG
      - FF => Fast forward
      - GREEN => Green
      - HOLD => TTV hold / image freeze
      - INDEX => TTV index
      - INFO => Info
      - INTERNET => VIERA connect
      - LEFT => Control LEFT
      - MENU => Menu
      - MUTE => Mute
      - PAUSE => Pause
      - PLAY => Play
      - POWER => Power off
      - P_NR => P-NR (Noise reduction)
      - REC => Record
      - RED => Red
      - RETURN => Return
      - REW => Rewind
      - RIGHT => Control RIGHT
      - R_TUNE => Seems to do the same as INFO
      - SD_CARD => SD-card
      - SKIP_NEXT => Skip next
      - SKIP_PREV => Skip previous
      - STOP => Stop
      - STTL => STTL / Subtitles
      - SUBMENU => Option
      - TEXT => Text / TTV
      - TV => TV
      - UP => Control UP
      - VIERA_LINK => VIERA link
      - VOLDOWN => Volume down
      - VOLUP => Volume up
      - VTOOLS => VIERA tools
      - YELLOW => Yellow
      + 3D => 3D button
      + BLUE => Blue
      + CANCEL => Cancel / Exit
      + CHG_INPUT => AV
      + CH_DOWN => Channel down
      + CH_UP => Channel up
      + D0 => Digit 0
      + D1 => Digit 1
      + D2 => Digit 2
      + D3 => Digit 3
      + D4 => Digit 4
      + D5 => Digit 5
      + D6 => Digit 6
      + D7 => Digit 7
      + D8 => Digit 8
      + D9 => Digit 9
      + DISP_MODE => Display mode / Aspect ratio
      + DOWN => Control DOWN
      + ENTER => Control Center click / enter
      + EPG => Guide / EPG
      + FF => Fast forward
      + GREEN => Green
      + HOLD => TTV hold / image freeze
      + INDEX => TTV index
      + INFO => Info
      + INTERNET => VIERA connect
      + LEFT => Control LEFT
      + MENU => Menu
      + MUTE => Mute
      + PAUSE => Pause
      + PLAY => Play
      + POWER => Power off
      + P_NR => P-NR (Noise reduction)
      + REC => Record
      + RED => Red
      + RETURN => Return
      + REW => Rewind
      + RIGHT => Control RIGHT
      + R_TUNE => Seems to do the same as INFO
      + SD_CARD => SD-card
      + SKIP_NEXT => Skip next
      + SKIP_PREV => Skip previous
      + STOP => Stop
      + STTL => STTL / Subtitles
      + SUBMENU => Option
      + TEXT => Text / TTV
      + TV => TV
      + UP => Control UP
      + VIERA_LINK => VIERA link
      + VOLDOWN => Volume down
      + VOLUP => Volume up
      + VTOOLS => VIERA tools
      + YELLOW => Yellow

    @@ -841,7 +1401,6 @@ sub VIERA_GetAbortFn($) {
      set <name> mute on
      set <name> volume 20
      - set <name> remoteControl CH_DOWN
@@ -855,7 +1414,6 @@ sub VIERA_GetAbortFn($) {
    mute
    volume
    - power
    presence
@@ -872,7 +1430,6 @@ sub VIERA_GetAbortFn($) {
  • volume
  • mute
  • presence
  • -
  • power
  • state
  • @@ -888,24 +1445,34 @@ sub VIERA_GetAbortFn($) { Define
      - define <name> VIERA <host> [<interval>] + define <name> VIERA <host> <interval> <pin code> <?>

      - Dieses Modul steuert einen Panasonic Fernseher über das Netzwerk. Es ist möglich den Fernseher - auszuschalten, die Lautstärke zu ändern oder zu muten bzw. unmuten. Dieses Modul kann zusätzlich - die Fernbedienung simulieren. Somit können also die Schaltaktionen einer Fernbedienung simuliert werden. - Getestet wurde das Modul mit einem Panasonic Plasma TV tx-p50vt30e + Dieses Modul steuert einen Panasonic Fernseher (unverschlüsselt oder verschlüsselt) über das Netzwerk. + Es ist möglich den Fernseher auszuschalten, die Lautstärke zu ändern oder zu muten bzw. unmuten + oder Befehle wie auf der Fernbedinung zu senden,

      - Beim definieren des Gerätes in FHEM wird ein interner Timer gestartet, welcher zyklisch alle 30 Sekunden + Beim definieren des Gerätes in FHEM wird ein interner Timer gestartet, welcher zyklisch den Status der Lautstärke und des Mute-Zustand ausliest. Das Intervall des Timer kann über den Parameter <interval> - geändert werden. Wird kein Interval angegeben, liest das Modul alle 30 Sekunden die Werte aus und triggert ein notify. + geändert werden und ein notify wird eingerichtet. +

      + Um das Modul einzurichten können mehrere Schritte notwendig sein. + Zuerst wird das Modul definiert mit dem PinCode <0000>, ? für die Abfrage der Verschlüsselung und einem + beliebigen Zeitinterval (60 ist ok). Dann den TV einschalten und warten bis die Verschlüsselung yes/no erkannt wird. + Wenn der TV nicht verschlüsselt ist, ist die Einrichtung abgeschlossen. Ist der TV verschlüsselt, dann bitte ein Kommando + ausführen (set myTV1 off), danach wird ein PinCode am TV angezeigt. Die Definition editieren den PinCode eintragen und das ? löschen. + Das Kommando nochmals ausführen, solange der PinCode angezeigt wird. Das wars.

      Anmerkung:
        Aktivieren von Fernbedienung der Lautstärke per DLNA: Menü -> Setup -> Netzwerk-Setup -> Netzwerkverbindungsein. -> DLNA-Fernbed. Lautst. -> Ein

      Beispiel:
        - define myTV1 VIERA 192.168.178.20

        - define myTV1 VIERA 192.168.178.20 60 #Mit einem Interval von 60 Sekunden + define myTV1 VIERA 192.168.178.20 ## PinCode ? +
        + define myTV1 VIERA 192.168.178.20 60 0000 ? (mit einem Interval von 60 Sekunden und dem PinCode für den 1. Schritt) +
        + define myTV1 VIERA 192.168.178.20 60 1234 (mit geändertem PinCode wie am TV angezeigt) +
    @@ -917,77 +1484,71 @@ sub VIERA_GetAbortFn($) {

    Zur Zeit sind die folgenden Befehle implementiert:
      - off
      - mute [on|off]
      - volume [0-100]
      - volumeUp
      - volumeDown
      - channel [1-9999]
      - channelUp
      - channelDown
      - statusRequest
      - remoteControl <command>
      - input [hdmi1|hdmi2|hdmi3|hdmi4|sdCard|tv]
      + off
      + mute [on|off]
      + volume [0-100]
      + channel [1-9999]
      + input [hdmi1|hdmi2|hdmi3|hdmi4|sdCard|tv]

      - Fernbedienung (Kann vielleicht nach Modell variieren)
      - Das Modul hat die folgenden Fernbedienbefehle implementiert:
      + Fernbedienung (Kann vielleicht nach Modell variieren).
      + Das Modul hat die folgenden Fernbedienbefehle implementiert:
        - 3D => 3D Knopf
        - BLUE => Blau
        - CANCEL => Cancel / Exit
        - CHG_INPUT => AV
        - CH_DOWN => Kanal runter
        - CH_UP => Kanal hoch
        - D0 => Ziffer 0
        - D1 => Ziffer 1
        - D2 => Ziffer 2
        - D3 => Ziffer 3
        - D4 => Ziffer 4
        - D5 => Ziffer 5
        - D6 => Ziffer 6
        - D7 => Ziffer 7
        - D8 => Ziffer 8
        - D9 => Ziffer 9
        - DISP_MODE => Anzeigemodus / Seitenverhältnis
        - DOWN => Navigieren runter
        - ENTER => Navigieren enter
        - EPG => Guide / EPG
        - FF => Vorspulen
        - GREEN => Grün
        - HOLD => Bild einfrieren
        - INDEX => TTV index
        - INFO => Info
        - INTERNET => VIERA connect
        - LEFT => Navigieren links
        - MENU => Menü
        - MUTE => Mute
        - PAUSE => Pause
        - PLAY => Play
        - POWER => Power off
        - P_NR => P-NR (Geräuschreduzierung)
        - REC => Aufnehmen
        - RED => Rot
        - RETURN => Enter
        - REW => Zurückspulen
        - RIGHT => Navigieren Rechts
        - R_TUNE => Vermutlich die selbe Funktion wie INFO
        - SD_CARD => SD-card
        - SKIP_NEXT => Skip next
        - SKIP_PREV => Skip previous
        - STOP => Stop
        - STTL => Untertitel
        - SUBMENU => Option
        - TEXT => TeleText
        - TV => TV
        - UP => Navigieren Hoch
        - VIERA_LINK => VIERA link
        - VOLDOWN => Lauter
        - VOLUP => Leiser
        - VTOOLS => VIERA tools
        - YELLOW => Gelb
        + 3D => 3D Knopf
        + BLUE => Blau
        + CANCEL => Cancel / Exit
        + CHG_INPUT => AV
        + CH_DOWN => Kanal runter
        + CH_UP => Kanal hoch
        + D0 => Ziffer 0
        + D1 => Ziffer 1
        + D2 => Ziffer 2
        + D3 => Ziffer 3
        + D4 => Ziffer 4
        + D5 => Ziffer 5
        + D6 => Ziffer 6
        + D7 => Ziffer 7
        + D8 => Ziffer 8
        + D9 => Ziffer 9
        + DISP_MODE => Anzeigemodus / Seitenverhältnis
        + DOWN => Navigieren runter
        + ENTER => Navigieren enter
        + EPG => Guide / EPG
        + FF => Vorspulen
        + GREEN => Grün
        + HOLD => Bild einfrieren
        + INDEX => TTV index
        + INFO => Info
        + INTERNET => VIERA connect
        + LEFT => Navigieren links
        + MENU => Menü
        + MUTE => Mute
        + PAUSE => Pause
        + PLAY => Play
        + POWER => Power off
        + P_NR => P-NR (Geräuschreduzierung)
        + REC => Aufnehmen
        + RED => Rot
        + RETURN => Enter
        + REW => Zurückspulen
        + RIGHT => Navigieren Rechts
        + R_TUNE => Vermutlich die selbe Funktion wie INFO
        + SD_CARD => SD-card
        + SKIP_NEXT => Skip next
        + SKIP_PREV => Skip previous
        + STOP => Stop
        + STTL => Untertitel
        + SUBMENU => Option
        + TEXT => TeleText
        + TV => TV
        + UP => Navigieren Hoch
        + VIERA_LINK => VIERA link
        + VOLDOWN => Lauter
        + VOLUP => Leiser
        + VTOOLS => VIERA tools
        + YELLOW => Gelb

      @@ -995,7 +1556,6 @@ sub VIERA_GetAbortFn($) {
        set <name> mute on
        set <name> volume 20
        - set <name> remoteControl CH_DOWN
    @@ -1009,7 +1569,6 @@ sub VIERA_GetAbortFn($) {
      mute
      volume
      - power
      presence
    @@ -1026,7 +1585,6 @@ sub VIERA_GetAbortFn($) {
  • volume
  • mute
  • presence
  • -
  • power
  • state
  • diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index f2b7fcf00..bb79dad53 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -341,7 +341,7 @@ FHEM/70_SolarEdgeAPI.pm pizmus Solaranlagen FHEM/70_STV.pm bentele Sonstiges FHEM/70_TellStick.pm real-wusel Sonstiges FHEM/70_USBWX.pm wherzig Sonstiges -FHEM/70_VIERA.pm teevau Sonstiges +FHEM/70_VIERA.pm mabula Multimedia FHEM/70_VolumeLink.pm rapster Multimedia FHEM/70_WINCONNECT.pm michael.winkler Sonstige Systeme FHEM/70_WS3600.pm Josch Sonstiges