From 4c6ed2e3e4d5a69cbca88e8f76d1153d70c4566a Mon Sep 17 00:00:00 2001 From: michaellampert <34814815+michaellampert@users.noreply.github.com> Date: Thu, 20 Jun 2019 19:51:26 +0200 Subject: [PATCH] wakeupcmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wakeupcmd hinzugefügt für das anschalten über ein Fhembefehl --- 82_LGTV_WebOS.pm | 2027 +++++++++++++++++++++------------------------- 1 file changed, 932 insertions(+), 1095 deletions(-) diff --git a/82_LGTV_WebOS.pm b/82_LGTV_WebOS.pm index ba97ac4..b8e28c8 100644 --- a/82_LGTV_WebOS.pm +++ b/82_LGTV_WebOS.pm @@ -1,5 +1,5 @@ ############################################################################### -# +# # Developed with Kate # # (c) 2017-2018 Copyright: Marko Oldenburg (leongaultier at gmail dot com) @@ -25,7 +25,7 @@ # GNU General Public License for more details. # # -# $Id$ +# $Id: 82_LGTV_WebOS.pm 18305 2019-01-17 20:44:42Z CoolTux $ # ############################################################################### @@ -37,7 +37,7 @@ # # if($@){ # Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); -# +# # readingsSingleUpdate($hash, "state", "error", 1); # # return; @@ -47,8 +47,10 @@ ## # + ################################ + package main; my $missingModul = ""; @@ -56,14 +58,22 @@ my $missingModul = ""; use strict; use warnings; -eval "use MIME::Base64;1" or $missingModul .= "MIME::Base64 "; -eval "use IO::Socket::INET;1" or $missingModul .= "IO::Socket::INET "; +eval "use MIME::Base64;1" or $missingModul .= "MIME::Base64 "; +eval "use IO::Socket::INET;1" or $missingModul .= "IO::Socket::INET "; eval "use Digest::SHA qw(sha1_hex);1" or $missingModul .= "Digest::SHA "; -eval "use JSON qw(decode_json encode_json);1" or $missingModul .= "JSON "; +eval "use JSON qw(decode_json encode_json);1" or $missingModul .= "JSON "; eval "use Encode qw(encode_utf8 decode_utf8);1" or $missingModul .= "Encode "; -eval "use Blocking;1" or $missingModul .= "Blocking "; +eval "use Blocking;1" or $missingModul .= "Blocking "; + + + + + +my $version = "2.0.9"; + + + -my $version = "2.0.11"; # Declare functions sub LGTV_WebOS_Initialize($); @@ -98,68 +108,70 @@ sub LGTV_WebOS_PresenceDone($); sub LGTV_WebOS_PresenceAborted($); sub LGTV_WebOS_WakeUp_Udp($@); + + + my %lgCommands = ( - "getServiceList" => ["ssap://api/getServiceList"], - "getChannelList" => ["ssap://tv/getChannelList"], - "getVolume" => ["ssap://audio/getVolume"], - "getAudioStatus" => ["ssap://audio/getStatus"], - "getCurrentChannel" => ["ssap://tv/getCurrentChannel"], - "getChannelProgramInfo" => ["ssap://tv/getChannelProgramInfo"], - "getForegroundAppInfo" => - ["ssap://com.webos.applicationManager/getForegroundAppInfo"], - "getAppList" => ["ssap://com.webos.applicationManager/listApps"], - "getAppStatus" => ["ssap://com.webos.service.appstatus/getAppStatus"], - "getExternalInputList" => ["ssap://tv/getExternalInputList"], - "get3DStatus" => ["ssap://com.webos.service.tv.display/get3DStatus"], - "powerOff" => ["ssap://system/turnOff"], - "powerOn" => ["ssap://system/turnOn"], - "3DOn" => ["ssap://com.webos.service.tv.display/set3DOn"], - "3DOff" => ["ssap://com.webos.service.tv.display/set3DOff"], - "volumeUp" => ["ssap://audio/volumeUp"], - "volumeDown" => ["ssap://audio/volumeDown"], - "channelDown" => ["ssap://tv/channelDown"], - "channelUp" => ["ssap://tv/channelUp"], - "play" => ["ssap://media.controls/play"], - "stop" => ["ssap://media.controls/stop"], - "pause" => ["ssap://media.controls/pause"], - "rewind" => ["ssap://media.controls/rewind"], - "fastForward" => ["ssap://media.controls/fastForward"], - "closeViewer" => ["ssap://media.viewer/close"], - "closeApp" => ["ssap://system.launcher/close"], - "openApp" => ["ssap://system.launcher/open"], - "closeWebApp" => ["ssap://webapp/closeWebApp"], - "openChannel" => [ "ssap://tv/openChannel", "channelNumber" ], - "launchApp" => [ "ssap://system.launcher/launch", "id" ], - "screenMsg" => [ "ssap://system.notifications/createToast", "message" ], - "mute" => [ "ssap://audio/setMute", "mute" ], - "volume" => [ "ssap://audio/setVolume", "volume" ], - "switchInput" => [ "ssap://tv/switchInput", "input" ], + "getServiceList" => ["ssap://api/getServiceList"], + "getChannelList" => ["ssap://tv/getChannelList"], + "getVolume" => ["ssap://audio/getVolume"], + "getAudioStatus" => ["ssap://audio/getStatus"], + "getCurrentChannel" => ["ssap://tv/getCurrentChannel"], + "getChannelProgramInfo" => ["ssap://tv/getChannelProgramInfo"], + "getForegroundAppInfo" => ["ssap://com.webos.applicationManager/getForegroundAppInfo"], + "getAppList" => ["ssap://com.webos.applicationManager/listApps"], + "getAppStatus" => ["ssap://com.webos.service.appstatus/getAppStatus"], + "getExternalInputList" => ["ssap://tv/getExternalInputList"], + "get3DStatus" => ["ssap://com.webos.service.tv.display/get3DStatus"], + "powerOff" => ["ssap://system/turnOff"], + "powerOn" => ["ssap://system/turnOn"], + "3DOn" => ["ssap://com.webos.service.tv.display/set3DOn"], + "3DOff" => ["ssap://com.webos.service.tv.display/set3DOff"], + "volumeUp" => ["ssap://audio/volumeUp"], + "volumeDown" => ["ssap://audio/volumeDown"], + "channelDown" => ["ssap://tv/channelDown"], + "channelUp" => ["ssap://tv/channelUp"], + "play" => ["ssap://media.controls/play"], + "stop" => ["ssap://media.controls/stop"], + "pause" => ["ssap://media.controls/pause"], + "rewind" => ["ssap://media.controls/rewind"], + "fastForward" => ["ssap://media.controls/fastForward"], + "closeViewer" => ["ssap://media.viewer/close"], + "closeApp" => ["ssap://system.launcher/close"], + "openApp" => ["ssap://system.launcher/open"], + "closeWebApp" => ["ssap://webapp/closeWebApp"], + "openChannel" => ["ssap://tv/openChannel", "channelNumber"], + "launchApp" => ["ssap://system.launcher/launch", "id"], + "screenMsg" => ["ssap://system.notifications/createToast", "message"], + "mute" => ["ssap://audio/setMute", "mute"], + "volume" => ["ssap://audio/setVolume", "volume"], + "switchInput" => ["ssap://tv/switchInput", "input"], ); my %openApps = ( - 'Maxdome' => 'maxdome', - 'AmazonLovefilm' => 'lovefilm.de', - 'AmazonVideo' => 'amazon', - 'YouTube' => 'youtube.leanback.v4', - 'Netflix' => 'netflix', - 'TV' => 'com.webos.app.livetv', - 'GooglePlay' => 'googleplaymovieswebos', - 'Browser' => 'com.webos.app.browser', - 'Chili.tv' => 'Chilieu', - 'TVCast' => 'de.2kit.castbrowsing', - 'Smartshare' => 'com.webos.app.smartshare', - 'Scheduler' => 'com.webos.app.scheduler', - 'Miracast' => 'com.webos.app.miracast', - 'TVGuide' => 'com.webos.app.tvguide', - 'Timemachine' => 'com.webos.app.timemachine', - 'ARDMediathek' => 'ard.mediathek', - 'Arte' => 'com.3827031.168353', - 'WetterMeteo' => 'meteonews', - 'Notificationcenter' => 'com.webos.app.notificationcenter', - 'Plex' => 'cdp-30', - 'SkyOnline' => 'de.sky.skyonline', - 'Smart-IPTV' => 'com.1827622.109556' + + 'Maxdome' => 'maxdome', + 'AmazonLovefilm' => 'lovefilm.de', + 'AmazonVideo' => 'amazon', + 'YouTube' => 'youtube.leanback.v4', + 'Netflix' => 'netflix', + 'TV' => 'com.webos.app.livetv', + 'GooglePlay' => 'googleplaymovieswebos', + 'Browser' => 'com.webos.app.browser', + 'Chili.tv' => 'Chilieu', + 'TVCast' => 'de.2kit.castbrowsing', + 'Smartshare' => 'com.webos.app.smartshare', + 'Scheduler' => 'com.webos.app.scheduler', + 'Miracast' => 'com.webos.app.miracast', + 'TVGuide' => 'com.webos.app.tvguide', + 'Timemachine' => 'com.webos.app.timemachine', + 'ARDMediathek' => 'ard.mediathek', + 'Arte' => 'com.3827031.168353', + 'WetterMeteo' => 'meteonews', + 'Notificationcenter' => 'com.webos.app.notificationcenter', + 'Plex' => 'cdp-30', + 'SkyOnline' => 'de.sky.skyonline' ); my %openAppsPackageName = reverse %openApps; @@ -167,86 +179,88 @@ my %openAppsPackageName = reverse %openApps; sub LGTV_WebOS_Initialize($) { my ($hash) = @_; - + # Provider - $hash->{ReadFn} = "LGTV_WebOS_Read"; - $hash->{WriteFn} = "LGTV_WebOS_Write"; + $hash->{ReadFn} = "LGTV_WebOS_Read"; + $hash->{WriteFn} = "LGTV_WebOS_Write"; + # Consumer - $hash->{SetFn} = "LGTV_WebOS_Set"; - $hash->{DefFn} = "LGTV_WebOS_Define"; - $hash->{UndefFn} = "LGTV_WebOS_Undef"; - $hash->{AttrFn} = "LGTV_WebOS_Attr"; - $hash->{AttrList} = - "disable:1 " - . "channelGuide:1 " - . "pingPresence:1 " - . "wakeOnLanMAC " - . "wakeOnLanBroadcast " - . $readingFnAttributes; + $hash->{SetFn} = "LGTV_WebOS_Set"; + $hash->{DefFn} = "LGTV_WebOS_Define"; + $hash->{UndefFn} = "LGTV_WebOS_Undef"; + $hash->{AttrFn} = "LGTV_WebOS_Attr"; + $hash->{AttrList} = "disable:1 ". + "channelGuide:1 ". + "pingPresence:1 ". + "wakeOnLanMAC ". + "wakeOnLanBroadcast ". + "wakeupCmd ". + $readingFnAttributes; - foreach my $d ( sort keys %{ $modules{LGTV_WebOS}{defptr} } ) { + + foreach my $d(sort keys %{$modules{LGTV_WebOS}{defptr}}) { my $hash = $modules{LGTV_WebOS}{defptr}{$d}; - $hash->{VERSION} = $version; + $hash->{VERSION} = $version; } } sub LGTV_WebOS_Define($$) { my ( $hash, $def ) = @_; - + my @a = split( "[ \t][ \t]*", $def ); + - return "too few parameters: define LGTV_WebOS " if ( @a != 3 ); - return - "Cannot define LGTV_WebOS device. Perl modul ${missingModul} is missing." - if ($missingModul); + return "too few parameters: define LGTV_WebOS " if( @a != 3 ); + return "Cannot define LGTV_WebOS device. Perl modul ${missingModul} is missing." if ( $missingModul ); + - my $name = $a[0]; - my $host = $a[2]; - $hash->{HOST} = $host; - $hash->{VERSION} = $version; - $hash->{PARTIAL} = ''; - $hash->{helper}{device}{channelguide}{counter} = 0; - $hash->{helper}{device}{registered} = 0; - $hash->{helper}{device}{runsetcmd} = 0; + my $name = $a[0]; + my $host = $a[2]; + + $hash->{HOST} = $host; + $hash->{VERSION} = $version; + $hash->{PARTIAL} = ''; + $hash->{helper}{device}{channelguide}{counter} = 0; + $hash->{helper}{device}{registered} = 0; + $hash->{helper}{device}{runsetcmd} = 0; + Log3 $name, 3, "LGTV_WebOS ($name) - defined with host $host"; - $attr{$name}{devStateIcon} = 'on:10px-kreis-gruen:off off:10px-kreis-rot:on' - if ( !defined( $attr{$name}{devStateIcon} ) ); - $attr{$name}{room} = 'LGTV' if ( !defined( $attr{$name}{room} ) ); - CommandDeleteReading( undef, $name . ' presence' ) - if ( AttrVal( $name, 'pingPresence', 0 ) == 0 ); - - $modules{LGTV_WebOS}{defptr}{ $hash->{HOST} } = $hash; - - if ($init_done) { + $attr{$name}{devStateIcon} = 'on:10px-kreis-gruen:off off:10px-kreis-rot:on' if( !defined( $attr{$name}{devStateIcon} ) ); + $attr{$name}{room} = 'LGTV' if( !defined( $attr{$name}{room} ) ); + CommandDeleteReading(undef,$name . ' presence') if( AttrVal($name,'pingPresence', 0) == 0 ); + + + $modules{LGTV_WebOS}{defptr}{$hash->{HOST}} = $hash; + + if( $init_done ) { LGTV_WebOS_TimerStatusRequest($hash); + } else { + InternalTimer( gettimeofday()+15, "LGTV_WebOS_TimerStatusRequest", $hash ); } - else { - InternalTimer( gettimeofday() + 15, - "LGTV_WebOS_TimerStatusRequest", $hash ); - } - + return undef; } sub LGTV_WebOS_Undef($$) { my ( $hash, $arg ) = @_; - + my $host = $hash->{HOST}; my $name = $hash->{NAME}; - + + RemoveInternalTimer($hash); - + LGTV_WebOS_Close($hash); - delete $modules{LGTV_WebOS}{defptr}{ $hash->{HOST} }; - + delete $modules{LGTV_WebOS}{defptr}{$hash->{HOST}}; + Log3 $name, 3, "LGTV_WebOS ($name) - device $name deleted"; - + return undef; } @@ -254,32 +268,33 @@ sub LGTV_WebOS_Attr(@) { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; - + my $orig = $attrVal; - if ( $attrName eq "disable" ) { - if ( $cmd eq "set" and $attrVal eq "1" ) { + + if( $attrName eq "disable" ) { + if( $cmd eq "set" and $attrVal eq "1" ) { RemoveInternalTimer($hash); - readingsSingleUpdate( $hash, "state", "disabled", 1 ); + readingsSingleUpdate ( $hash, "state", "disabled", 1 ); $hash->{PARTIAL} = ''; Log3 $name, 3, "LGTV_WebOS ($name) - disabled"; } - elsif ( $cmd eq "del" ) { - readingsSingleUpdate( $hash, "state", "active", 1 ); + elsif( $cmd eq "del" ) { + readingsSingleUpdate ( $hash, "state", "active", 1 ); Log3 $name, 3, "LGTV_WebOS ($name) - enabled"; LGTV_WebOS_TimerStatusRequest($hash); } } - - if ( $attrName eq "disabledForIntervals" ) { - if ( $cmd eq "set" ) { + + if( $attrName eq "disabledForIntervals" ) { + if( $cmd eq "set" ) { Log3 $name, 3, "LGTV_WebOS ($name) - enable disabledForIntervals"; - readingsSingleUpdate( $hash, "state", "Unknown", 1 ); + readingsSingleUpdate ( $hash, "state", "Unknown", 1 ); } - elsif ( $cmd eq "del" ) { - readingsSingleUpdate( $hash, "state", "active", 1 ); + elsif( $cmd eq "del" ) { + readingsSingleUpdate ( $hash, "state", "active", 1 ); Log3 $name, 3, "LGTV_WebOS ($name) - delete disabledForIntervals"; } } @@ -289,300 +304,262 @@ sub LGTV_WebOS_Attr(@) { sub LGTV_WebOS_TimerStatusRequest($) { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer( $hash, 'LGTV_WebOS_TimerStatusRequest' ); - + my $hash = shift; + my $name = $hash->{NAME}; + + + RemoveInternalTimer($hash,'LGTV_WebOS_TimerStatusRequest'); + readingsBeginUpdate($hash); - - if ( !IsDisabled($name) - and $hash->{CD} - and $hash->{helper}{device}{registered} == 1 ) - { - + + if( !IsDisabled($name) and $hash->{CD} and $hash->{helper}{device}{registered} == 1 ) { + Log3 $name, 4, "LGTV_WebOS ($name) - run get functions"; - LGTV_WebOS_Presence($hash) - if ( AttrVal( $name, 'pingPresence', 0 ) == 1 ); - - if ( $hash->{helper}{device}{channelguide}{counter} > 2 - and AttrVal( $name, 'channelGuide', 0 ) == 1 - and ReadingsVal( $name, 'launchApp', 'TV' ) eq 'TV' ) - { - + LGTV_WebOS_Presence($hash) if( AttrVal($name,'pingPresence', 0) == 1 ); + + if($hash->{helper}{device}{channelguide}{counter} > 2 and AttrVal($name,'channelGuide', 0) == 1 and ReadingsVal($name,'launchApp', 'TV') eq 'TV' ) { + LGTV_WebOS_GetChannelProgramInfo($hash); - $hash->{helper}{device}{channelguide}{counter} = 0; - - } - else { + $hash->{helper}{device}{channelguide}{counter} = 0; + + } else { LGTV_WebOS_GetAudioStatus($hash); - InternalTimer( gettimeofday() + 2, - 'LGTV_WebOS_GetCurrentChannel', $hash ) - if ( ReadingsVal( $name, 'launchApp', 'TV' ) eq 'TV' ); - InternalTimer( gettimeofday() + 4, - 'LGTV_WebOS_GetForgroundAppInfo', $hash ); - InternalTimer( gettimeofday() + 6, 'LGTV_WebOS_Get3DStatus', - $hash ); - InternalTimer( gettimeofday() + 8, - 'LGTV_WebOS_GetExternalInputList', $hash ); + InternalTimer( gettimeofday()+2, 'LGTV_WebOS_GetCurrentChannel', $hash ) if( ReadingsVal($name,'launchApp', 'TV') eq 'TV' ); + InternalTimer( gettimeofday()+4, 'LGTV_WebOS_GetForgroundAppInfo', $hash ); + InternalTimer( gettimeofday()+6, 'LGTV_WebOS_Get3DStatus', $hash ); + InternalTimer( gettimeofday()+8, 'LGTV_WebOS_GetExternalInputList', $hash ); } - - } - elsif ( IsDisabled($name) ) { - + + } elsif( IsDisabled($name) ) { + LGTV_WebOS_Close($hash); - LGTV_WebOS_Presence($hash) - if ( AttrVal( $name, 'pingPresence', 0 ) == 1 ); - $hash->{helper}{device}{runsetcmd} = 0; - readingsBulkUpdateIfChanged( $hash, 'state', 'disabled' ); - + LGTV_WebOS_Presence($hash) if( AttrVal($name,'pingPresence', 0) == 1 ); + $hash->{helper}{device}{runsetcmd} = 0; + readingsBulkUpdateIfChanged($hash, 'state', 'disabled'); + + } else { + + LGTV_WebOS_Presence($hash) if( AttrVal($name,'pingPresence', 0) == 1 ); + + readingsBulkUpdateIfChanged($hash,'channel','-'); + readingsBulkUpdateIfChanged($hash,'channelName','-'); + readingsBulkUpdateIfChanged($hash,'channelMedia','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentTitle','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentStartTime','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentEndTime','-'); + readingsBulkUpdateIfChanged($hash,'channelNextTitle','-'); + readingsBulkUpdateIfChanged($hash,'channelNextStartTime','-'); + readingsBulkUpdateIfChanged($hash,'channelNextEndTime','-'); + + $hash->{helper}{device}{runsetcmd} = 0; } - else { - - LGTV_WebOS_Presence($hash) - if ( AttrVal( $name, 'pingPresence', 0 ) == 1 ); - - readingsBulkUpdateIfChanged( $hash, 'channel', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelName', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelMedia', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentTitle', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentStartTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentEndTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextTitle', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextStartTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextEndTime', '-' ); - - $hash->{helper}{device}{runsetcmd} = 0; - } - - readingsEndUpdate( $hash, 1 ); - - LGTV_WebOS_Open($hash) if ( !IsDisabled($name) and not $hash->{CD} ); - - $hash->{helper}{device}{channelguide}{counter} = - $hash->{helper}{device}{channelguide}{counter} + 1; - InternalTimer( gettimeofday() + 10, "LGTV_WebOS_TimerStatusRequest", - $hash ); + + readingsEndUpdate($hash, 1); + + LGTV_WebOS_Open($hash) if( !IsDisabled($name) and not $hash->{CD} ); + + $hash->{helper}{device}{channelguide}{counter} = $hash->{helper}{device}{channelguide}{counter} +1; + InternalTimer( gettimeofday()+10,"LGTV_WebOS_TimerStatusRequest", $hash ); } sub LGTV_WebOS_Set($@) { - my ( $hash, $name, $cmd, @args ) = @_; - my ( $arg, @params ) = @args; + my ($hash, $name, $cmd, @args) = @_; + my ($arg, @params) = @args; my $uri; my %payload; my $inputs; my @inputs; - - if ( $cmd eq 'connect' ) { - return "usage: connect" if ( @args != 0 ); + + + if ( defined( $hash->{helper}{device}{inputs} ) and ref( $hash->{helper}{device}{inputs} ) eq "HASH" ) { + + @inputs = keys %{ $hash->{helper}{device}{inputs} }; + } + + @inputs = sort(@inputs); + $inputs = join(",", @inputs); + + if($cmd eq 'connect') { + return "usage: connect" if( @args != 0 ); LGTV_WebOS_Open($hash); return undef; - - } - elsif ( $cmd eq 'clearInputList' ) { - return "usage: clearInputList" if ( @args != 0 ); + + } elsif($cmd eq 'clearInputList') { + return "usage: clearInputList" if( @args != 0 ); delete $hash->{helper}{device}{inputs}; delete $hash->{helper}{device}{inputapps}; return undef; - } - elsif ( $cmd eq 'pairing' ) { - return "usage: pairing" if ( @args != 0 ); + } elsif($cmd eq 'pairing') { + return "usage: pairing" if( @args != 0 ); LGTV_WebOS_Pairing($hash); return undef; + + } elsif($cmd eq 'screenMsg') { + return "usage: screenMsg " if( @args < 1 ); - } - elsif ( $cmd eq 'screenMsg' ) { - return "usage: screenMsg " if ( @args < 1 ); + my $msg = join(" ", @args); + $payload{$lgCommands{$cmd}->[1]} = decode_utf8($msg); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'on' or $cmd eq 'off') { + return "usage: on/off" if( @args != 0 ); - my $msg = join( " ", @args ); - $payload{ $lgCommands{$cmd}->[1] } = decode_utf8($msg); - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'on' or $cmd eq 'off' ) { - return "usage: on/off" if ( @args != 0 ); - - if ( $cmd eq 'off' ) { - $uri = $lgCommands{powerOff}; - } - elsif ( $cmd eq 'on' ) { - if ( AttrVal( $name, 'wakeOnLanMAC', 'none' ) ne 'none' ) { - LGTV_WebOS_WakeUp_Udp( - $hash, - AttrVal( $name, 'wakeOnLanMAC', 0 ), - AttrVal( $name, 'wakeOnLanBroadcast', '255.255.255.255' ) - ); + if($cmd eq 'off') { + $uri = $lgCommands{powerOff}; + } elsif ($cmd eq 'on') { + if( AttrVal($name,'wakeOnLanMAC','none') ne 'none' ) { + LGTV_WebOS_WakeUp_Udp($hash,AttrVal($name,'wakeOnLanMAC',0),AttrVal($name,'wakeOnLanBroadcast','255.255.255.255')); return; - } - else { - $uri = $lgCommands{powerOn}; + } elsif( AttrVal($name,'wakeupCmd','none') ne 'none' ) { + my $wakeupCmd = AttrVal($name,'wakeupCmd','none'); + if ( $wakeupCmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) { + Log3 $name, 4, + "LGTV_WebOS executing wake-up command (Perl): $wakeupCmd"; + eval $wakeupCmd; + return; + } + else { + Log3 $name, 4, + "LGTV_WebOS executing wake-up command (fhem): $wakeupCmd"; + fhem $wakeupCmd; + return; + } + } else { + $uri = $lgCommands{powerOn}; } } + + } elsif($cmd eq '3D') { + return "usage: 3D on/off" if( @args != 1 ); - } - elsif ( $cmd eq '3D' ) { - return "usage: 3D on/off" if ( @args != 1 ); - - if ( $args[0] eq 'off' ) { - $uri = $lgCommands{'3DOff'}; + if($args[0] eq 'off') { + $uri = $lgCommands{'3DOff'}; + } elsif ($args[0] eq 'on') { + $uri = $lgCommands{'3DOn'}; } - elsif ( $args[0] eq 'on' ) { - $uri = $lgCommands{'3DOn'}; + + } elsif($cmd eq 'mute') { + return "usage: mute" if( @args != 1 ); + + if($args[0] eq 'off') { + + $uri = $lgCommands{volumeDown}->[0]; + + } elsif($args[0] eq 'on') { + + $payload{$lgCommands{$cmd}->[1]} = 'true'; + $uri = $lgCommands{$cmd}->[0]; } - } - elsif ( $cmd eq 'mute' ) { - return "usage: mute" if ( @args != 1 ); + } elsif($cmd eq 'volume') { + return "usage: volume" if( @args != 1 ); - if ( $args[0] eq 'off' ) { + $payload{$lgCommands{$cmd}->[1]} = int(join(" ", @args)); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'launchApp') { + return "usage: launchApp" if( @args != 1 ); - $uri = $lgCommands{volumeDown}->[0]; + $payload{$lgCommands{$cmd}->[1]} = $openApps{join(" ", @args)}; + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'input') { + return "usage: input" if( @args != 1 ); - } - elsif ( $args[0] eq 'on' ) { + my $inputLabel = join(" ", @args); + $payload{$lgCommands{launchApp}->[1]} = $hash->{helper}{device}{inputs}{$inputLabel}; + $uri = $lgCommands{launchApp}->[0]; + + } elsif($cmd eq 'volumeUp') { + return "usage: volumeUp" if( @args != 0 ); - $payload{ $lgCommands{$cmd}->[1] } = 'true'; - $uri = $lgCommands{$cmd}->[0]; - } + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'volumeDown') { + return "usage: volumeDown" if( @args != 0 ); - } - elsif ( $cmd eq 'volume' ) { - return "usage: volume" if ( @args != 1 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'channelDown') { + return "usage: channelDown" if( @args != 0 ); - $payload{ $lgCommands{$cmd}->[1] } = int( join( " ", @args ) ); - $uri = $lgCommands{$cmd}->[0]; + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'channelUp') { + return "usage: channelUp" if( @args != 0 ); - } - elsif ( $cmd eq 'launchApp' ) { - return "usage: launchApp" if ( @args != 1 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'channel') { + return "usage: channel" if( @args != 1 ); - $payload{ $lgCommands{$cmd}->[1] } = $openApps{ join( " ", @args ) }; - $uri = $lgCommands{$cmd}->[0]; + $payload{$lgCommands{openChannel}->[1]} = join(" ", @args); + $uri = $lgCommands{openChannel}->[0]; + + } elsif($cmd eq 'getServiceList') { + return "usage: getServiceList" if( @args != 0 ); - } - elsif ( $cmd eq 'input' ) { - return "usage: input" if ( @args != 1 ); + $uri = $lgCommands{$cmd}->[0]; - my $inputLabel = join( " ", @args ); - $payload{ $lgCommands{launchApp}->[1] } = - $hash->{helper}{device}{inputs}{$inputLabel}; - $uri = $lgCommands{launchApp}->[0]; + } elsif($cmd eq 'getChannelList') { + return "usage: getChannelList" if( @args != 0 ); - } - elsif ( $cmd eq 'volumeUp' ) { - return "usage: volumeUp" if ( @args != 0 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'getAppList') { + return "usage: getAppList" if( @args != 0 ); - $uri = $lgCommands{$cmd}->[0]; + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'getExternalInputList') { + return "usage: getExternalInputList" if( @args != 0 ); - } - elsif ( $cmd eq 'volumeDown' ) { - return "usage: volumeDown" if ( @args != 0 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'play') { + return "usage: play" if( @args != 0 ); - $uri = $lgCommands{$cmd}->[0]; + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'stop') { + return "usage: stop" if( @args != 0 ); - } - elsif ( $cmd eq 'channelDown' ) { - return "usage: channelDown" if ( @args != 0 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'fastForward') { + return "usage: fastForward" if( @args != 0 ); - $uri = $lgCommands{$cmd}->[0]; + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'rewind') { + return "usage: rewind" if( @args != 0 ); - } - elsif ( $cmd eq 'channelUp' ) { - return "usage: channelUp" if ( @args != 0 ); + $uri = $lgCommands{$cmd}->[0]; + + } elsif($cmd eq 'pause') { + return "usage: pause" if( @args != 0 ); - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'channel' ) { - return "usage: channel" if ( @args != 1 ); - - $payload{ $lgCommands{openChannel}->[1] } = join( " ", @args ); - $uri = $lgCommands{openChannel}->[0]; - - } - elsif ( $cmd eq 'getServiceList' ) { - return "usage: getServiceList" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'getChannelList' ) { - return "usage: getChannelList" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'getAppList' ) { - return "usage: getAppList" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'getExternalInputList' ) { - return "usage: getExternalInputList" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'play' ) { - return "usage: play" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'stop' ) { - return "usage: stop" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'fastForward' ) { - return "usage: fastForward" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'rewind' ) { - return "usage: rewind" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - elsif ( $cmd eq 'pause' ) { - return "usage: pause" if ( @args != 0 ); - - $uri = $lgCommands{$cmd}->[0]; - - } - else { - my $list = ""; - $list .= -'connect:noArg pairing:noArg screenMsg mute:on,off volume:slider,0,1,100 volumeUp:noArg volumeDown:noArg channelDown:noArg channelUp:noArg getServiceList:noArg on:noArg off:noArg'; - $list .= -' 3D:on,off stop:noArg play:noArg pause:noArg rewind:noArg fastForward:noArg clearInputList:noArg channel'; - $list .= ' launchApp:' . join( ',', => map qq{$_} => keys %openApps ); - $list .= ' input:' - . join( ',', - => map qq{$_} => keys %{ $hash->{helper}{device}{inputs} } ) - if ( defined( $hash->{helper}{device}{inputs} ) - and ref( $hash->{helper}{device}{inputs} ) eq "HASH" ); + $uri = $lgCommands{$cmd}->[0]; + } else { + my $list = ""; + $list .= "connect:noArg pairing:noArg screenMsg mute:on,off volume:slider,0,1,100 volumeUp:noArg volumeDown:noArg channelDown:noArg channelUp:noArg getServiceList:noArg on:noArg off:noArg launchApp:Maxdome,AmazonLovefilm,AmazonVideo,YouTube,Netflix,TV,GooglePlay,Browser,Chilieu,TVCast,Smartshare,Scheduler,Miracast,TVGuide,Timemachine,ARDMediathek,Arte,WetterMeteo,Notificationcenter,Plex,SkyOnline 3D:on,off stop:noArg play:noArg pause:noArg rewind:noArg fastForward:noArg clearInputList:noArg input:$inputs channel"; return "Unknown argument $cmd, choose one of $list"; } - $hash->{helper}{device}{runsetcmd} = $hash->{helper}{device}{runsetcmd} + 1; - LGTV_WebOS_CreateSendCommand( $hash, $uri, \%payload ); + $hash->{helper}{device}{runsetcmd} = $hash->{helper}{device}{runsetcmd} + 1; + LGTV_WebOS_CreateSendCommand($hash,$uri,\%payload); } sub LGTV_WebOS_Open($) { @@ -592,58 +569,59 @@ sub LGTV_WebOS_Open($) { my $host = $hash->{HOST}; my $port = 3000; my $timeout = 0.1; - + + Log3 $name, 4, "LGTV_WebOS ($name) - Baue Socket Verbindung auf"; + - my $socket = new IO::Socket::INET( - PeerHost => $host, - PeerPort => $port, - Proto => 'tcp', - Timeout => $timeout - ) - or return Log3 $name, 4, - "LGTV_WebOS ($name) Couldn't connect to $host:$port"; # open Socket - - $hash->{FD} = $socket->fileno(); - $hash->{CD} = $socket; # sysread / close won't work on fileno + my $socket = new IO::Socket::INET ( PeerHost => $host, + PeerPort => $port, + Proto => 'tcp', + Timeout => $timeout + ) + or return Log3 $name, 4, "LGTV_WebOS ($name) Couldn't connect to $host:$port"; # open Socket + + $hash->{FD} = $socket->fileno(); + $hash->{CD} = $socket; # sysread / close won't work on fileno $selectlist{$name} = $hash; - + Log3 $name, 4, "LGTV_WebOS ($name) - Socket Connected"; - + LGTV_WebOS_Handshake($hash); Log3 $name, 4, "LGTV_WebOS ($name) - start Handshake"; - + } sub LGTV_WebOS_Close($) { - my $hash = shift; - my $name = $hash->{NAME}; - - return if ( !$hash->{CD} ); - - close( $hash->{CD} ) if ( $hash->{CD} ); - delete( $hash->{FD} ); - delete( $hash->{CD} ); - delete( $selectlist{$name} ); - - readingsSingleUpdate( $hash, 'state', 'off', 1 ); + my $hash = shift; + my $name = $hash->{NAME}; + + return if( !$hash->{CD} ); + close($hash->{CD}) if($hash->{CD}); + delete($hash->{FD}); + delete($hash->{CD}); + delete($selectlist{$name}); + + readingsSingleUpdate($hash,'state','off',1); + Log3 $name, 4, "LGTV_WebOS ($name) - Socket Disconnected"; } sub LGTV_WebOS_Write($@) { - my ( $hash, $string ) = @_; - my $name = $hash->{NAME}; - + my ($hash,$string) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 4, "LGTV_WebOS ($name) - WriteFn called"; - + return Log3 $name, 4, "LGTV_WebOS ($name) - socket not connected" - unless ( $hash->{CD} ); + unless($hash->{CD}); Log3 $name, 4, "LGTV_WebOS ($name) - $string"; - syswrite( $hash->{CD}, $string ); + syswrite($hash->{CD}, $string); return undef; } @@ -651,1001 +629,846 @@ sub LGTV_WebOS_Read($) { my $hash = shift; my $name = $hash->{NAME}; - + my $len; my $buf; - + + Log3 $name, 4, "LGTV_WebOS ($name) - ReadFn started"; - $len = sysread( $hash->{CD}, $buf, 10240 ); - - if ( !defined($len) or !$len ) { + $len = sysread($hash->{CD},$buf,10240); + + if( !defined($len) or !$len ) { LGTV_WebOS_Close($hash); return; } - - unless ( defined $buf ) { + + unless( defined $buf) { Log3 $name, 3, "LGTV_WebOS ($name) - no data received"; - return; + return; } - - if ( $buf =~ /(\{"type":".+}}$)/ ) { - - $buf =~ /(\{"type":".+}}$)/; + + + if( $buf =~ /({"type":".+}}$)/ ) { + + $buf =~ /({"type":".+}}$)/; $buf = $1; - - Log3 $name, 4, -"LGTV_WebOS ($name) - received correct JSON string, start response processing: $buf"; - LGTV_WebOS_ResponseProcessing( $hash, $buf ); - - } - elsif ( $buf =~ /HTTP\/1.1 101 Switching Protocols/ ) { - - Log3 $name, 4, -"LGTV_WebOS ($name) - received HTTP data string, start response processing: $buf"; - LGTV_WebOS_ResponseProcessing( $hash, $buf ); - - } - else { - - Log3 $name, 4, -"LGTV_WebOS ($name) - coruppted data found, run LGTV_WebOS_ProcessRead: $buf"; - LGTV_WebOS_ProcessRead( $hash, $buf ); + + Log3 $name, 4, "LGTV_WebOS ($name) - received correct JSON string, start response processing: $buf"; + LGTV_WebOS_ResponseProcessing($hash,$buf); + + } elsif( $buf =~ /HTTP\/1.1 101 Switching Protocols/ ) { + + Log3 $name, 4, "LGTV_WebOS ($name) - received HTTP data string, start response processing: $buf"; + LGTV_WebOS_ResponseProcessing($hash,$buf); + + } else { + + Log3 $name, 4, "LGTV_WebOS ($name) - coruppted data found, run LGTV_WebOS_ProcessRead: $buf"; + LGTV_WebOS_ProcessRead($hash,$buf); } } sub LGTV_WebOS_ProcessRead($$) { - my ( $hash, $data ) = @_; + my ($hash, $data) = @_; my $name = $hash->{NAME}; - + my $buffer = ''; - + + Log3 $name, 4, "LGTV_WebOS ($name) - process read"; - if ( defined( $hash->{PARTIAL} ) and $hash->{PARTIAL} ) { - + if(defined($hash->{PARTIAL}) and $hash->{PARTIAL}) { + Log3 $name, 5, "LGTV_WebOS ($name) - PARTIAL: " . $hash->{PARTIAL}; $buffer = $hash->{PARTIAL}; - - } - else { - + + } else { + Log3 $name, 4, "LGTV_WebOS ($name) - No PARTIAL buffer"; } Log3 $name, 5, "LGTV_WebOS ($name) - Incoming data: " . $data; - $buffer = $buffer . $data; - Log3 $name, 5, -"LGTV_WebOS ($name) - Current processing buffer (PARTIAL + incoming data): " - . $buffer; + $buffer = $buffer . $data; + Log3 $name, 5, "LGTV_WebOS ($name) - Current processing buffer (PARTIAL + incoming data): " . $buffer; - my ( $json, $tail ) = LGTV_WebOS_ParseMsg( $hash, $buffer ); + my ($json,$tail) = LGTV_WebOS_ParseMsg($hash, $buffer); - while ($json) { + while($json) { + $hash->{LAST_RECV} = time(); - - Log3 $name, 5, - "LGTV_WebOS ($name) - Decoding JSON message. Length: " - . length($json) - . " Content: " - . $json; - Log3 $name, 5, - "LGTV_WebOS ($name) - Vor Sub: Laenge JSON: " - . length($json) - . " Content: " - . $json - . " Tail: " - . $tail; - - LGTV_WebOS_ResponseProcessing( $hash, $json ) - unless ( not defined($tail) and not($tail) ); - - ( $json, $tail ) = LGTV_WebOS_ParseMsg( $hash, $tail ); - - Log3 $name, 5, - "LGTV_WebOS ($name) - Nach Sub: Laenge JSON: " - . length($json) - . " Content: " - . $json - . " Tail: " - . $tail; + + Log3 $name, 5, "LGTV_WebOS ($name) - Decoding JSON message. Length: " . length($json) . " Content: " . $json; + Log3 $name, 5, "LGTV_WebOS ($name) - Vor Sub: Laenge JSON: " . length($json) . " Content: " . $json . " Tail: " . $tail; + + LGTV_WebOS_ResponseProcessing($hash,$json) + unless(not defined($tail) and not ($tail)); + + ($json,$tail) = LGTV_WebOS_ParseMsg($hash, $tail); + + Log3 $name, 5, "LGTV_WebOS ($name) - Nach Sub: Laenge JSON: " . length($json) . " Content: " . $json . " Tail: " . $tail; } + $tail = '' - if ( length($tail) > 30000 ); + if(length($tail) > 30000); $hash->{PARTIAL} = $tail; Log3 $name, 4, "LGTV_WebOS ($name) - PARTIAL lenght: " . length($tail); - + + Log3 $name, 5, "LGTV_WebOS ($name) - Tail: " . $tail; Log3 $name, 5, "LGTV_WebOS ($name) - PARTIAL: " . $hash->{PARTIAL}; } sub LGTV_WebOS_Handshake($) { - my $hash = shift; - my $name = $hash->{NAME}; - my $host = $hash->{HOST}; - my $wsKey = encode_base64( gettimeofday() ); - - my $wsHandshakeCmd = ""; - $wsHandshakeCmd .= "GET / HTTP/1.1\r\n"; - $wsHandshakeCmd .= "Host: $host\r\n"; - $wsHandshakeCmd .= "User-Agent: FHEM\r\n"; - $wsHandshakeCmd .= "Upgrade: websocket\r\n"; - $wsHandshakeCmd .= "Connection: Upgrade\r\n"; - $wsHandshakeCmd .= "Sec-WebSocket-Version: 13\r\n"; - $wsHandshakeCmd .= "Sec-WebSocket-Key: " . $wsKey . "\r\n"; - - LGTV_WebOS_Write( $hash, $wsHandshakeCmd ); - - $hash->{helper}{wsKey} = $wsKey; - + my $hash = shift; + my $name = $hash->{NAME}; + my $host = $hash->{HOST}; + my $wsKey = encode_base64(gettimeofday()); + + my $wsHandshakeCmd = ""; + $wsHandshakeCmd .= "GET / HTTP/1.1\r\n"; + $wsHandshakeCmd .= "Host: $host\r\n"; + $wsHandshakeCmd .= "User-Agent: FHEM\r\n"; + $wsHandshakeCmd .= "Upgrade: websocket\r\n"; + $wsHandshakeCmd .= "Connection: Upgrade\r\n"; + $wsHandshakeCmd .= "Sec-WebSocket-Version: 13\r\n"; + $wsHandshakeCmd .= "Sec-WebSocket-Key: " . $wsKey . "\r\n"; + + LGTV_WebOS_Write($hash,$wsHandshakeCmd); + + $hash->{helper}{wsKey} = $wsKey; + Log3 $name, 4, "LGTV_WebOS ($name) - send Handshake to WriteFn"; - + + LGTV_WebOS_TimerStatusRequest($hash); Log3 $name, 4, "LGTV_WebOS ($name) - start timer status request"; - + LGTV_WebOS_Pairing($hash); Log3 $name, 4, "LGTV_WebOS ($name) - start pairing routine"; } sub LGTV_WebOS_ResponseProcessing($$) { - my ( $hash, $response ) = @_; - my $name = $hash->{NAME}; - + my ($hash,$response) = @_; + my $name = $hash->{NAME}; + + + + ######################## ### Response has HTML Header - if ( $response =~ /HTTP\/1.1 101 Switching Protocols/ ) { - - my $data = $response; - my $header = LGTV_WebOS_Header2Hash($data); - + if( $response =~ /HTTP\/1.1 101 Switching Protocols/ ) { + + my $data = $response; + my $header = LGTV_WebOS_Header2Hash($data); + ################################ ### Handshake for first Connect - if ( defined( $header->{'Sec-WebSocket-Accept'} ) ) { - - my $keyAccept = $header->{'Sec-WebSocket-Accept'}; + if( defined($header->{'Sec-WebSocket-Accept'})) { + + my $keyAccept = $header->{'Sec-WebSocket-Accept'}; Log3 $name, 5, "LGTV_WebOS ($name) - keyAccept: $keyAccept"; - - my $wsKey = $hash->{helper}{wsKey}; - my $expectedResponse = trim( - encode_base64( - pack( - 'H*', - sha1_hex( - trim($wsKey) - . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - ) - ) - ) - ); - - if ( $keyAccept eq $expectedResponse ) { - - Log3 $name, 3, -"LGTV_WebOS ($name) - Sucessfull WS connection to $hash->{HOST}"; - readingsSingleUpdate( $hash, 'state', 'on', 1 ); - - } - else { + + my $wsKey = $hash->{helper}{wsKey}; + my $expectedResponse = trim(encode_base64(pack('H*', sha1_hex(trim($wsKey)."258EAFA5-E914-47DA-95CA-C5AB0DC85B11")))); + + if ($keyAccept eq $expectedResponse) { + + Log3 $name, 3, "LGTV_WebOS ($name) - Sucessfull WS connection to $hash->{HOST}"; + readingsSingleUpdate($hash,'state','on',1); + + } else { LGTV_WebOS_Close($hash); - Log3 $name, 3, -"LGTV_WebOS ($name) - ERROR: Unsucessfull WS connection to $hash->{HOST}"; + Log3 $name, 3, "LGTV_WebOS ($name) - ERROR: Unsucessfull WS connection to $hash->{HOST}"; } } - + return undef; } - - elsif ( $response =~ m/^{"type":".+}}$/ ) { - + + + elsif( $response =~ m/^{"type":".+}}$/ ) { + return Log3 $name, 4, "LGTV_WebOS ($name) - garbage after JSON object" - if ( $response =~ m/^{"type":".+}}.+{"type":".+/ ); - - Log3 $name, 4, - "LGTV_WebOS ($name) - JSON detected, run LGTV_WebOS_WriteReadings"; - - my $json = $response; - - Log3 $name, 4, "LGTV_WebOS ($name) - Corrected JSON String: $json" - if ($json); - - if ( not defined($json) or not($json) ) { + if($response =~ m/^{"type":".+}}.+{"type":".+/); + + Log3 $name, 4, "LGTV_WebOS ($name) - JSON detected, run LGTV_WebOS_WriteReadings"; + my $json = $response; + + Log3 $name, 4, "LGTV_WebOS ($name) - Corrected JSON String: $json" if($json); + + if(not defined($json) or not ($json) ) { + Log3 $name, 4, "LGTV_WebOS ($name) - Corrected JSON String empty"; return; } - - my $decode_json = eval { decode_json( encode_utf8($json) ) }; - if ($@) { + + my $decode_json = eval{decode_json(encode_utf8($json))}; + if($@){ Log3 $name, 3, "LGTV_WebOS ($name) - JSON error while request: $@"; return; } - LGTV_WebOS_WriteReadings( $hash, $decode_json ); - + LGTV_WebOS_WriteReadings($hash,$decode_json); + return undef; } - + + Log3 $name, 4, "LGTV_WebOS ($name) - no Match found"; } sub LGTV_WebOS_WriteReadings($$) { - my ( $hash, $decode_json ) = @_; - - my $name = $hash->{NAME}; + my ($hash,$decode_json) = @_; + + my $name = $hash->{NAME}; my $mute; my $response; my %channelList; + Log3 $name, 4, "LGTV_WebOS ($name) - Beginn Readings writing"; + + + readingsBeginUpdate($hash); - - if ( ref( $decode_json->{payload}{services} ) eq "ARRAY" - and scalar( @{ $decode_json->{payload}{services} } ) > 0 ) - { - foreach my $services ( @{ $decode_json->{payload}{services} } ) { - - readingsBulkUpdateIfChanged( - $hash, - 'service_' . $services->{name}, - 'v.' . $services->{version} - ); + + if( ref($decode_json->{payload}{services}) eq "ARRAY" and scalar(@{$decode_json->{payload}{services}}) > 0 ) { + foreach my $services (@{$decode_json->{payload}{services}}) { + + readingsBulkUpdateIfChanged($hash,'service_'.$services->{name},'v.'.$services->{version}); } } + + elsif( ref($decode_json->{payload}{devices}) eq "ARRAY" and scalar(@{$decode_json->{payload}{devices}}) > 0 ) { + + foreach my $devices ( @{$decode_json->{payload}{devices}} ) { - elsif ( ref( $decode_json->{payload}{devices} ) eq "ARRAY" - and scalar( @{ $decode_json->{payload}{devices} } ) > 0 ) - { - - foreach my $devices ( @{ $decode_json->{payload}{devices} } ) { - - if ( - not - defined( $hash->{helper}{device}{inputs}{ $devices->{label} } ) - or not defined( - $hash->{helper}{device}{inputapps}{ $devices->{appId} } - ) - ) - { - - $hash->{helper}{device}{inputs}{ $devices->{label} } = - $devices->{appId}; - $hash->{helper}{device}{inputapps}{ $devices->{appId} } = - $devices->{label}; + if( not defined($hash->{helper}{device}{inputs}{$devices->{label}}) or not defined($hash->{helper}{device}{inputapps}{$devices->{appId}}) ) { + + $hash->{helper}{device}{inputs}{$devices->{label}} = $devices->{appId}; + $hash->{helper}{device}{inputapps}{$devices->{appId}} = $devices->{label}; } - - readingsBulkUpdateIfChanged( - $hash, - 'extInput_' . $devices->{label}, - 'connect_' . $devices->{connected} - ); + + readingsBulkUpdateIfChanged($hash,'extInput_'.$devices->{label},'connect_'.$devices->{connected}); } } - - elsif ( ref( $decode_json->{payload}{programList} ) eq "ARRAY" - and scalar( @{ $decode_json->{payload}{programList} } ) > 0 ) - { + + elsif( ref($decode_json->{payload}{programList}) eq "ARRAY" and scalar(@{$decode_json->{payload}{programList}}) > 0 ) { use Date::Parse; my $count = 0; - foreach my $programList ( @{ $decode_json->{payload}{programList} } ) { - - if ( - str2time( - LGTV_WebOS_FormartStartEndTime( - $programList->{localEndTime} - ) - ) > time() - ) - { - if ( $count < 1 ) { - - readingsBulkUpdateIfChanged( $hash, 'channelCurrentTitle', - $programList->{programName} ); - readingsBulkUpdateIfChanged( - $hash, - 'channelCurrentStartTime', - LGTV_WebOS_FormartStartEndTime( - $programList->{localStartTime} - ) - ); - readingsBulkUpdateIfChanged( - $hash, - 'channelCurrentEndTime', - LGTV_WebOS_FormartStartEndTime( - $programList->{localEndTime} - ) - ); - + foreach my $programList ( @{$decode_json->{payload}{programList}} ) { + + if( str2time(LGTV_WebOS_FormartStartEndTime($programList->{localEndTime})) > time() ) { + if($count < 1) { + + readingsBulkUpdateIfChanged($hash,'channelCurrentTitle',$programList->{programName}); + readingsBulkUpdateIfChanged($hash,'channelCurrentStartTime',LGTV_WebOS_FormartStartEndTime($programList->{localStartTime})); + readingsBulkUpdateIfChanged($hash,'channelCurrentEndTime',LGTV_WebOS_FormartStartEndTime($programList->{localEndTime})); + + } elsif($count < 2) { + + readingsBulkUpdateIfChanged($hash,'channelNextTitle',$programList->{programName}); + readingsBulkUpdateIfChanged($hash,'channelNextStartTime',LGTV_WebOS_FormartStartEndTime($programList->{localStartTime})); + readingsBulkUpdateIfChanged($hash,'channelNextEndTime',LGTV_WebOS_FormartStartEndTime($programList->{localEndTime})); } - elsif ( $count < 2 ) { - - readingsBulkUpdateIfChanged( $hash, 'channelNextTitle', - $programList->{programName} ); - readingsBulkUpdateIfChanged( - $hash, - 'channelNextStartTime', - LGTV_WebOS_FormartStartEndTime( - $programList->{localStartTime} - ) - ); - readingsBulkUpdateIfChanged( - $hash, - 'channelNextEndTime', - LGTV_WebOS_FormartStartEndTime( - $programList->{localEndTime} - ) - ); - } - + $count++; - return if ( $count > 1 ); + return if($count > 1); } } } - - elsif (defined( $decode_json->{payload}{'mute'} ) - or defined( $decode_json->{payload}{'muted'} ) ) - { - - if ( - defined( $decode_json->{payload}{'mute'} ) - and ( $decode_json->{payload}{'mute'} eq 'true' - or $decode_json->{payload}{'mute'} == 1 ) - ) - { - - readingsBulkUpdateIfChanged( $hash, 'mute', 'on' ); - - } - elsif ( defined( $decode_json->{payload}{'mute'} ) ) { - if ( $decode_json->{payload}{'mute'} eq 'false' - or $decode_json->{payload}{'mute'} == 0 ) - { - - readingsBulkUpdateIfChanged( $hash, 'mute', 'off' ); + + elsif( defined($decode_json->{payload}{'mute'}) or defined($decode_json->{payload}{'muted'})) { + + if( defined($decode_json->{payload}{'mute'}) and ($decode_json->{payload}{'mute'} eq 'true' or $decode_json->{payload}{'mute'} == 1 ) ) { + + readingsBulkUpdateIfChanged($hash,'mute','on'); + + } elsif( defined($decode_json->{payload}{'mute'}) ) { + if( $decode_json->{payload}{'mute'} eq 'false' or $decode_json->{payload}{'mute'} == 0 ) { + + readingsBulkUpdateIfChanged($hash,'mute','off'); } } - - if ( - defined( $decode_json->{payload}{'muted'} ) - and ( $decode_json->{payload}{'muted'} eq 'true' - or $decode_json->{payload}{'muted'} == 1 ) - ) - { - - readingsBulkUpdateIfChanged( $hash, 'mute', 'on' ); - - } - elsif ( - defined( $decode_json->{payload}{'muted'} ) - and ( $decode_json->{payload}{'muted'} eq 'false' - or $decode_json->{payload}{'muted'} == 0 ) - ) - { - - readingsBulkUpdateIfChanged( $hash, 'mute', 'off' ); + + if( defined($decode_json->{payload}{'muted'}) and ($decode_json->{payload}{'muted'} eq 'true' or $decode_json->{payload}{'muted'} == 1) ) { + + readingsBulkUpdateIfChanged($hash,'mute','on'); + + } elsif( defined($decode_json->{payload}{'muted'}) and ($decode_json->{payload}{'muted'} eq 'false' or $decode_json->{payload}{'muted'} == 0) ) { + + readingsBulkUpdateIfChanged($hash,'mute','off'); } } - - elsif ( defined( $decode_json->{payload}{status3D}{status} ) ) { - if ( $decode_json->{payload}{status3D}{status} eq 'false' - or $decode_json->{payload}{status3D}{status} == 0 ) - { - - readingsBulkUpdateIfChanged( $hash, '3D', 'off' ); - + + elsif( defined($decode_json->{payload}{status3D}{status}) ) { + if( $decode_json->{payload}{status3D}{status} eq 'false' or $decode_json->{payload}{status3D}{status} == 0 ) { + + readingsBulkUpdateIfChanged($hash,'3D','off'); + + } elsif( $decode_json->{payload}{status3D}{status} eq 'true' or $decode_json->{payload}{status3D}{status} == 1 ) { + + readingsBulkUpdateIfChanged($hash,'3D','on'); } - elsif ($decode_json->{payload}{status3D}{status} eq 'true' - or $decode_json->{payload}{status3D}{status} == 1 ) - { - - readingsBulkUpdateIfChanged( $hash, '3D', 'on' ); - } - - readingsBulkUpdateIfChanged( $hash, '3DMode', - $decode_json->{payload}{status3D}{pattern} ); + + readingsBulkUpdateIfChanged($hash,'3DMode',$decode_json->{payload}{status3D}{pattern}); } - elsif ( defined( $decode_json->{payload}{appId} ) ) { + elsif( defined($decode_json->{payload}{appId}) ) { + + if( $decode_json->{payload}{appId} =~ /com.webos.app.externalinput/ or $decode_json->{payload}{appId} =~ /com.webos.app.hdmi/ ) { - if ( $decode_json->{payload}{appId} =~ /com.webos.app.externalinput/ - or $decode_json->{payload}{appId} =~ /com.webos.app.hdmi/ ) - { + readingsBulkUpdateIfChanged($hash,'input',$hash->{helper}{device}{inputapps}{$decode_json->{payload}{appId}}); + readingsBulkUpdateIfChanged($hash,'launchApp','-'); + + } else { - readingsBulkUpdateIfChanged( $hash, 'input', - $hash->{helper}{device}{inputapps} - { $decode_json->{payload}{appId} } ); - readingsBulkUpdateIfChanged( $hash, 'launchApp', '-' ); - - } - else { - - readingsBulkUpdateIfChanged( $hash, 'launchApp', - $openAppsPackageName{ $decode_json->{payload}{appId} } ); - readingsBulkUpdateIfChanged( $hash, 'input', '-' ); + readingsBulkUpdateIfChanged($hash,'launchApp',$openAppsPackageName{$decode_json->{payload}{appId}}); + readingsBulkUpdateIfChanged($hash,'input','-'); } } - - if ( defined( $decode_json->{type} ) ) { - - if ( $decode_json->{type} eq 'registered' - and defined( $decode_json->{payload}{'client-key'} ) ) - { - - $hash->{helper}{device}{registered} = 1; - - } - elsif ( - ( - $decode_json->{type} eq 'response' - and ( $decode_json->{payload}{returnValue} eq 'true' - or $decode_json->{payload}{returnValue} == 1 ) - ) - or ( $decode_json->{type} eq 'registered' ) - and defined( $decode_json->{payload}{'client-key'} ) - ) - { - + + if( defined($decode_json->{type}) ) { + + if( $decode_json->{type} eq 'registered' and defined($decode_json->{payload}{'client-key'}) ) { + + $hash->{helper}{device}{registered} = 1; + + } elsif( ($decode_json->{type} eq 'response' and ($decode_json->{payload}{returnValue} eq 'true' or $decode_json->{payload}{returnValue} == 1 )) or ($decode_json->{type} eq 'registered') and defined($decode_json->{payload}{'client-key'}) ) { + $response = 'ok'; - readingsBulkUpdateIfChanged( $hash, 'pairing', 'paired' ); - $hash->{helper}{device}{runsetcmd} = - $hash->{helper}{device}{runsetcmd} - 1 - if ( $hash->{helper}{device}{runsetcmd} > 0 ); - - } - elsif ( $decode_json->{type} eq 'error' ) { - + readingsBulkUpdateIfChanged($hash,'pairing','paired'); + $hash->{helper}{device}{runsetcmd} = $hash->{helper}{device}{runsetcmd} - 1 if($hash->{helper}{device}{runsetcmd} > 0); + + } elsif( $decode_json->{type} eq 'error' ) { + $response = "error - $decode_json->{error}"; - - if ( $decode_json->{error} eq '401 insufficient permissions' - or $decode_json->{error} eq - '401 insufficient permissions (not registered)' ) - { - - readingsBulkUpdateIfChanged( $hash, 'pairing', 'unpaired' ); + + if($decode_json->{error} eq '401 insufficient permissions' or $decode_json->{error} eq '401 insufficient permissions (not registered)') { + + readingsBulkUpdateIfChanged($hash,'pairing','unpaired'); } - - $hash->{helper}{device}{runsetcmd} = - $hash->{helper}{device}{runsetcmd} - 1 - if ( $hash->{helper}{device}{runsetcmd} > 0 ); + + $hash->{helper}{device}{runsetcmd} = $hash->{helper}{device}{runsetcmd} - 1 if($hash->{helper}{device}{runsetcmd} > 0); } } - - readingsBulkUpdateIfChanged( $hash, 'lgKey', - $decode_json->{payload}{'client-key'} ) - if ( defined( $decode_json->{payload}{'client-key'} ) ); - readingsBulkUpdateIfChanged( $hash, 'volume', - $decode_json->{payload}{'volume'} ) - if ( defined( $decode_json->{payload}{'volume'} ) ); - readingsBulkUpdateIfChanged( $hash, 'lastResponse', $response ) - if ( defined($response) ); - - if ( ReadingsVal( $name, 'launchApp', 'none' ) eq 'TV' ) { - - readingsBulkUpdateIfChanged( $hash, 'channel', - $decode_json->{payload}{'channelNumber'} ) - if ( defined( $decode_json->{payload}{'channelNumber'} ) ); - readingsBulkUpdateIfChanged( $hash, 'channelName', - $decode_json->{payload}{'channelName'} ) - if ( defined( $decode_json->{payload}{'channelName'} ) ); - readingsBulkUpdateIfChanged( $hash, 'channelMedia', - $decode_json->{payload}{'channelTypeName'} ) - if ( defined( $decode_json->{payload}{'channelTypeName'} ) ); - + + + readingsBulkUpdateIfChanged($hash,'lgKey',$decode_json->{payload}{'client-key'}) if( defined($decode_json->{payload}{'client-key'}) ); + readingsBulkUpdateIfChanged($hash,'volume',$decode_json->{payload}{'volume'}) if( defined($decode_json->{payload}{'volume'}) ); + readingsBulkUpdateIfChanged($hash,'lastResponse',$response) if( defined($response) ); + + if( ReadingsVal($name,'launchApp','none') eq 'TV' ) { + + readingsBulkUpdateIfChanged($hash,'channel',$decode_json->{payload}{'channelNumber'}) if( defined($decode_json->{payload}{'channelNumber'}) ); + readingsBulkUpdateIfChanged($hash,'channelName',$decode_json->{payload}{'channelName'}) if( defined($decode_json->{payload}{'channelName'}) ); + readingsBulkUpdateIfChanged($hash,'channelMedia',$decode_json->{payload}{'channelTypeName'}) if( defined($decode_json->{payload}{'channelTypeName'}) ); + + } else { + + readingsBulkUpdateIfChanged($hash,'channelName','-'); + readingsBulkUpdateIfChanged($hash,'channel','-'); + readingsBulkUpdateIfChanged($hash,'channelMedia','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentTitle','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentStartTime','-'); + readingsBulkUpdateIfChanged($hash,'channelCurrentEndTime','-'); + readingsBulkUpdateIfChanged($hash,'channelNextTitle','-'); + readingsBulkUpdateIfChanged($hash,'channelNextStartTime','-'); + readingsBulkUpdateIfChanged($hash,'channelNextEndTime','-'); } - else { + + readingsBulkUpdateIfChanged($hash,'state','on'); - readingsBulkUpdateIfChanged( $hash, 'channelName', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channel', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelMedia', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentTitle', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentStartTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelCurrentEndTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextTitle', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextStartTime', '-' ); - readingsBulkUpdateIfChanged( $hash, 'channelNextEndTime', '-' ); - } - - readingsBulkUpdateIfChanged( $hash, 'state', 'on' ); - - readingsEndUpdate( $hash, 1 ); + readingsEndUpdate($hash, 1); } sub LGTV_WebOS_Pairing($) { - my $hash = shift; - my $name = $hash->{NAME}; - + my $hash = shift; + my $name = $hash->{NAME}; + my $lgKey; - + Log3 $name, 4, "LGTV_WebOS ($name) - HASH handshakePayload"; + + my %handshakePayload = ( "pairingType" => "PROMPT", + "manifest" => { + "manifestVersion" => 1, + "appVersion" => "1.1", + "signed" => { + "created" => "20161123", + "appId" => "com.lge.test", + "vendorId" => "com.lge", + "localizedAppNames" => { + "" => "FHEM LG Remote", + "de-DE" => "FHEM LG Fernbedienung" + }, + "localizedVendorNames" => { + "" => "LG Electronics" + }, + "permissions" => [ + "TEST_SECURE", + "CONTROL_INPUT_TEXT", + "CONTROL_MOUSE_AND_KEYBOARD", + "READ_INSTALLED_APPS", + "READ_LGE_SDX", + "READ_NOTIFICATIONS", + "SEARCH", + "WRITE_SETTINGS", + "WRITE_NOTIFICATION_ALERT", + "CONTROL_POWER", + "READ_CURRENT_CHANNEL", + "READ_RUNNING_APPS", + "READ_UPDATE_INFO", + "UPDATE_FROM_REMOTE_APP", + "READ_LGE_TV_INPUT_EVENTS", + "READ_TV_CURRENT_TIME" + ], + "serial" => "2f930e2d2cfe083771f68e4fe7bb07" + }, + "permissions" => [ + "LAUNCH", + "LAUNCH_WEBAPP", + "APP_TO_APP", + "CLOSE", + "TEST_OPEN", + "TEST_PROTECTED", + "CONTROL_AUDIO", + "CONTROL_DISPLAY", + "CONTROL_INPUT_JOYSTICK", + "CONTROL_INPUT_MEDIA_RECORDING", + "CONTROL_INPUT_MEDIA_PLAYBACK", + "CONTROL_INPUT_TV", + "CONTROL_POWER", + "READ_APP_STATUS", + "READ_CURRENT_CHANNEL", + "READ_INPUT_DEVICE_LIST", + "READ_NETWORK_STATE", + "READ_RUNNING_APPS", + "READ_TV_CHANNEL_LIST", + "WRITE_NOTIFICATION_TOAST", + "READ_POWER_STATE", + "READ_COUNTRY_INFO" + ], + "signatures" => [ + { + "signatureVersion" => 1, + "signature" => "eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==" + } + ] + } + ); - my %handshakePayload = ( - "pairingType" => "PROMPT", - "manifest" => { - "manifestVersion" => 1, - "appVersion" => "1.1", - "signed" => { - "created" => "20161123", - "appId" => "com.lge.test", - "vendorId" => "com.lge", - "localizedAppNames" => { - "" => "FHEM LG Remote", - "de-DE" => "FHEM LG Fernbedienung" - }, - "localizedVendorNames" => { - "" => "LG Electronics" - }, - "permissions" => [ - "TEST_SECURE", "CONTROL_INPUT_TEXT", - "CONTROL_MOUSE_AND_KEYBOARD", "READ_INSTALLED_APPS", - "READ_LGE_SDX", "READ_NOTIFICATIONS", - "SEARCH", "WRITE_SETTINGS", - "WRITE_NOTIFICATION_ALERT", "CONTROL_POWER", - "READ_CURRENT_CHANNEL", "READ_RUNNING_APPS", - "READ_UPDATE_INFO", "UPDATE_FROM_REMOTE_APP", - "READ_LGE_TV_INPUT_EVENTS", "READ_TV_CURRENT_TIME" - ], - "serial" => "2f930e2d2cfe083771f68e4fe7bb07" - }, - "permissions" => [ - "LAUNCH", - "LAUNCH_WEBAPP", - "APP_TO_APP", - "CLOSE", - "TEST_OPEN", - "TEST_PROTECTED", - "CONTROL_AUDIO", - "CONTROL_DISPLAY", - "CONTROL_INPUT_JOYSTICK", - "CONTROL_INPUT_MEDIA_RECORDING", - "CONTROL_INPUT_MEDIA_PLAYBACK", - "CONTROL_INPUT_TV", - "CONTROL_POWER", - "READ_APP_STATUS", - "READ_CURRENT_CHANNEL", - "READ_INPUT_DEVICE_LIST", - "READ_NETWORK_STATE", - "READ_RUNNING_APPS", - "READ_TV_CHANNEL_LIST", - "WRITE_NOTIFICATION_TOAST", - "READ_POWER_STATE", - "READ_COUNTRY_INFO" - ], - "signatures" => [ - { - "signatureVersion" => 1, - "signature" => -"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==" - } - ] - } - ); my $usedHandshake = \%handshakePayload; + + my $key = ReadingsVal($name, 'lgKey', ''); - my $key = ReadingsVal( $name, 'lgKey', '' ); - - $usedHandshake->{'client-key'} = $key if ( defined($key) ); - - LGTV_WebOS_CreateSendCommand( $hash, undef, $usedHandshake, 'register' ); + $usedHandshake->{'client-key'} = $key if( defined($key)); + + LGTV_WebOS_CreateSendCommand($hash, undef, $usedHandshake, 'register'); Log3 $name, 4, "LGTV_WebOS ($name) - Send pairing informations"; } sub LGTV_WebOS_CreateSendCommand($$$;$) { - my ( $hash, $uri, $payload, $type ) = @_; - - my $name = $hash->{NAME}; + my ($hash, $uri, $payload, $type) = @_; + + my $name = $hash->{NAME}; my $err; - - $type = 'request' if ( not defined($type) ); - + + + $type = 'request' if( not defined($type) ); + my $command = {}; - $command->{'client-key'} = ReadingsVal( $name, 'lgKey', '' ) - if ( $type ne 'register' ); - $command->{id} = $type . "_" . gettimeofday(); - $command->{type} = $type; - $command->{uri} = $uri if ($uri); - $command->{payload} = $payload if ( defined($payload) ); - -#Log3 $name, 5, "LGTV_WebOS ($name) - Payload Message: $command->{payload}{message}"; - + $command->{'client-key'} = ReadingsVal($name, 'lgKey', '') if( $type ne 'register' ); + $command->{id} = $type."_".gettimeofday(); + $command->{type} = $type; + $command->{uri} = $uri if($uri); + $command->{payload} = $payload if( defined($payload) ); + + #Log3 $name, 5, "LGTV_WebOS ($name) - Payload Message: $command->{payload}{message}"; + my $cmd = encode_json($command); - + Log3 $name, 5, "LGTV_WebOS ($name) - Sending command: $cmd"; - - LGTV_WebOS_Write( $hash, LGTV_WebOS_Hybi10Encode( $cmd, "text", 1 ) ); - + + LGTV_WebOS_Write($hash, LGTV_WebOS_Hybi10Encode($cmd, "text", 1)); + return undef; } sub LGTV_WebOS_Hybi10Encode($;$$) { - my ( $payload, $type, $masked ) = @_; - - $type //= "text"; + my ($payload, $type, $masked) = @_; + + + $type //= "text"; $masked //= 1; my @frameHead; - my $frame = ""; + my $frame = ""; my $payloadLength = length($payload); - if ( $type eq "text" ) { - + + if ($type eq "text") { + # first byte indicates FIN, Text-Frame (10000001): $frameHead[0] = 129; - - } - elsif ( $type eq "close" ) { - + + } elsif ($type eq "close") { + # first byte indicates FIN, Close Frame(10001000): $frameHead[0] = 136; - - } - elsif ( $type eq "ping" ) { - + + } elsif ($type eq "ping") { + # first byte indicates FIN, Ping frame (10001001): $frameHead[0] = 137; - - } - elsif ( $type eq "pong" ) { - + + } elsif ($type eq "pong") { + # first byte indicates FIN, Pong frame (10001010): $frameHead[0] = 138; } # set mask and payload length (using 1, 3 or 9 bytes) - if ( $payloadLength > 65535 ) { - + if ($payloadLength > 65535) { + # TODO - my $payloadLengthBin = sprintf( '%064b', $payloadLength ); + my $payloadLengthBin = sprintf('%064b', $payloadLength); $frameHead[1] = ($masked) ? 255 : 127; - - for ( my $i = 0 ; $i < 8 ; $i++ ) { - - $frameHead[ $i + 2 ] = - oct( "0b" . substr( $payloadLengthBin, $i * 8, $i * 8 + 8 ) ); + + for (my $i = 0; $i < 8; $i++) { + + $frameHead[$i + 2] = oct("0b".substr($payloadLengthBin, $i*8, $i*8+8)); } # most significant bit MUST be 0 (close connection if frame too big) - if ( $frameHead[2] > 127 ) { - + if ($frameHead[2] > 127) { + #$this->close(1004); return undef; } - - } - elsif ( $payloadLength > 125 ) { - - my $payloadLengthBin = sprintf( '%016b', $payloadLength ); + + } elsif ($payloadLength > 125) { + + my $payloadLengthBin = sprintf('%016b', $payloadLength); $frameHead[1] = ($masked) ? 254 : 126; - $frameHead[2] = oct( "0b" . substr( $payloadLengthBin, 0, 8 ) ); - $frameHead[3] = oct( "0b" . substr( $payloadLengthBin, 8, 16 ) ); - - } - else { - + $frameHead[2] = oct("0b".substr($payloadLengthBin, 0, 8)); + $frameHead[3] = oct("0b".substr($payloadLengthBin, 8, 16)); + + } else { + $frameHead[1] = ($masked) ? $payloadLength + 128 : $payloadLength; } # convert frame-head to string: - for ( my $i = 0 ; $i < scalar(@frameHead) ; $i++ ) { - - $frameHead[$i] = chr( $frameHead[$i] ); + for (my $i = 0; $i < scalar(@frameHead); $i++) { + + $frameHead[$i] = chr($frameHead[$i]); } - + my @mask; if ($masked) { - # generate a random mask: - for ( my $i = 0 ; $i < 4 ; $i++ ) { - + for (my $i = 0; $i < 4; $i++) { + #$mask[$i] = chr(int(rand(255))); - $mask[$i] = chr( int( 25 * $i ) ); + $mask[$i] = chr(int(25*$i)); } - - @frameHead = ( @frameHead, @mask ); + + @frameHead = (@frameHead, @mask); } - - $frame = join( "", @frameHead ); + + $frame = join("", @frameHead); # append payload to frame: my $char; - for ( my $i = 0 ; $i < $payloadLength ; $i++ ) { - - $char = substr( $payload, $i, 1 ); - $frame .= ($masked) ? $char ^ $mask[ $i % 4 ] : $char; + for (my $i = 0; $i < $payloadLength; $i++) { + + $char = substr($payload, $i, 1); + $frame .= ($masked) ? $char ^ $mask[$i % 4] : $char; } - + return $frame; } sub LGTV_WebOS_GetAudioStatus($) { - my $hash = shift; - my $name = $hash->{NAME}; + my $hash = shift; + my $name = $hash->{NAME}; + - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetAudioStatus: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{getAudioStatus}, undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetAudioStatus: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{getAudioStatus},undef) if($hash->{helper}{device}{runsetcmd} == 0); } sub LGTV_WebOS_GetCurrentChannel($) { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer( $hash, 'LGTV_WebOS_GetCurrentChannel' ); - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetCurrentChannel: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{getCurrentChannel}, undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + my $hash = shift; + my $name = $hash->{NAME}; + + + RemoveInternalTimer($hash,'LGTV_WebOS_GetCurrentChannel'); + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetCurrentChannel: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{getCurrentChannel},undef) if($hash->{helper}{device}{runsetcmd} == 0); } sub LGTV_WebOS_GetForgroundAppInfo($) { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer( $hash, 'LGTV_WebOS_GetForgroundAppInfo' ); - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetForgroundAppInfo: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{getForegroundAppInfo}, - undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + my $hash = shift; + my $name = $hash->{NAME}; + + + RemoveInternalTimer($hash,'LGTV_WebOS_GetForgroundAppInfo'); + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetForgroundAppInfo: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{getForegroundAppInfo},undef) if($hash->{helper}{device}{runsetcmd} == 0); } sub LGTV_WebOS_GetExternalInputList($) { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer( $hash, 'LGTV_WebOS_GetExternalInputList' ); - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetExternalInputList: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{getExternalInputList}, - undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + my $hash = shift; + my $name = $hash->{NAME}; + + + RemoveInternalTimer($hash,'LGTV_WebOS_GetExternalInputList'); + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetExternalInputList: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{getExternalInputList},undef) if($hash->{helper}{device}{runsetcmd} == 0); } sub LGTV_WebOS_Get3DStatus($) { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer( $hash, 'LGTV_WebOS_Get3DStatus' ); - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_Get3DStatus: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{get3DStatus}, undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + my $hash = shift; + my $name = $hash->{NAME}; + + + RemoveInternalTimer($hash,'LGTV_WebOS_Get3DStatus'); + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_Get3DStatus: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{get3DStatus},undef) if($hash->{helper}{device}{runsetcmd} == 0); } sub LGTV_WebOS_GetChannelProgramInfo($) { - my $hash = shift; - my $name = $hash->{NAME}; - - Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetChannelProgramInfo: " - . $hash->{helper}{device}{runsetcmd}; - LGTV_WebOS_CreateSendCommand( $hash, $lgCommands{getChannelProgramInfo}, - undef ) - if ( $hash->{helper}{device}{runsetcmd} == 0 ); + my $hash = shift; + my $name = $hash->{NAME}; + + + Log3 $name, 4, "LGTV_WebOS ($name) - LGTV_WebOS_GetChannelProgramInfo: " . $hash->{helper}{device}{runsetcmd}; + LGTV_WebOS_CreateSendCommand($hash,$lgCommands{getChannelProgramInfo},undef) if($hash->{helper}{device}{runsetcmd} == 0); } + + + ############################################# ### my little Helper sub LGTV_WebOS_ParseMsg($$) { - my ( $hash, $buffer ) = @_; - - my $name = $hash->{NAME}; - my $open = 0; + my ($hash, $buffer) = @_; + + my $name = $hash->{NAME}; + my $open = 0; my $close = 0; - my $msg = ''; - my $tail = ''; - - if ($buffer) { - foreach my $c ( split //, $buffer ) { - if ( $open == $close && $open > 0 ) { + my $msg = ''; + my $tail = ''; + + + if($buffer) { + foreach my $c (split //, $buffer) { + if($open == $close && $open > 0) { $tail .= $c; - Log3 $name, 5, - "LGTV_WebOS ($name) - $open == $close && $open > 0"; - - } - elsif ( ( $open == $close ) && ( $c ne '{' ) ) { - - Log3 $name, 5, - "LGTV_WebOS ($name) - Garbage character before message: " - . $c; - - } - else { - - if ( $c eq '{' ) { + Log3 $name, 5, "LGTV_WebOS ($name) - $open == $close && $open > 0"; + + } elsif(($open == $close) && ($c ne '{')) { + + Log3 $name, 5, "LGTV_WebOS ($name) - Garbage character before message: " . $c; + + } else { + + if($c eq '{') { $open++; - - } - elsif ( $c eq '}' ) { - + + } elsif($c eq '}') { + $close++; } - + $msg .= $c; } } - - if ( $open != $close ) { - + + if($open != $close) { + $tail = $msg; - $msg = ''; + $msg = ''; } } - + Log3 $name, 5, "LGTV_WebOS ($name) - return msg: $msg and tail: $tail"; - return ( $msg, $tail ); + return ($msg,$tail); } sub LGTV_WebOS_Header2Hash($) { - my $string = shift; - my %hash = (); + my $string = shift; + my %hash = (); - foreach my $line ( split( "\r\n", $string ) ) { - my ( $key, $value ) = split( ": ", $line ); - next if ( !$value ); + foreach my $line (split("\r\n", $string)) { + my ($key,$value) = split( ": ", $line ); + next if( !$value ); $value =~ s/^ //; $hash{$key} = $value; - } - + } + return \%hash; } sub LGTV_WebOS_FormartStartEndTime($) { - my $string = shift; - - my @timeArray = split( ',', $string ); - - return -"$timeArray[0]-$timeArray[1]-$timeArray[2] $timeArray[3]:$timeArray[4]:$timeArray[5]"; + my $string = shift; + + + my @timeArray = split(',', $string); + + return "$timeArray[0]-$timeArray[1]-$timeArray[2] $timeArray[3]:$timeArray[4]:$timeArray[5]"; } ############ Presence Erkennung Begin ################# sub LGTV_WebOS_Presence($) { - my $hash = shift; - my $name = $hash->{NAME}; - - $hash->{helper}{RUNNING_PID} = - BlockingCall( "LGTV_WebOS_PresenceRun", $name . '|' . $hash->{HOST}, - "LGTV_WebOS_PresenceDone", 5, "LGTV_WebOS_PresenceAborted", $hash ) - unless ( exists( $hash->{helper}{RUNNING_PID} ) ); + my $hash = shift; + my $name = $hash->{NAME}; + + + $hash->{helper}{RUNNING_PID} = BlockingCall("LGTV_WebOS_PresenceRun", $name.'|'.$hash->{HOST}, "LGTV_WebOS_PresenceDone", 5, "LGTV_WebOS_PresenceAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}) ); } sub LGTV_WebOS_PresenceRun($) { - my $string = shift; - my ( $name, $host ) = split( "\\|", $string ); - + my $string = shift; + my ($name, $host) = split("\\|", $string); + my $tmp; my $response; + $tmp = qx(ping -c 3 -w 2 $host 2>&1); - if ( defined($tmp) and $tmp ne "" ) { - + if(defined($tmp) and $tmp ne "") { + chomp $tmp; - Log3 $name, 5, - "LGTV_WebOS ($name) - ping command returned with output:\n$tmp"; - $response = "$name|" - . ( - ( - $tmp =~ /\d+ [Bb]ytes (from|von)/ - and not $tmp =~ /[Uu]nreachable/ - ) ? "present" : "absent" - ); - - } - else { - + Log3 $name, 5, "LGTV_WebOS ($name) - ping command returned with output:\n$tmp"; + $response = "$name|".(($tmp =~ /\d+ [Bb]ytes (from|von)/ and not $tmp =~ /[Uu]nreachable/) ? "present" : "absent"); + + } else { + $response = "$name|Could not execute ping command"; } - - Log3 $name, 4, -"Sub LGTV_WebOS_PresenceRun ($name) - Sub finish, Call LGTV_WebOS_PresenceDone"; + + Log3 $name, 4, "Sub LGTV_WebOS_PresenceRun ($name) - Sub finish, Call LGTV_WebOS_PresenceDone"; return $response; } sub LGTV_WebOS_PresenceDone($) { - my ($string) = @_; - - my ( $name, $response ) = split( "\\|", $string ); - my $hash = $defs{$name}; - - delete( $hash->{helper}{RUNNING_PID} ); - - Log3 $name, 4, -"Sub LGTV_WebOS_PresenceDone ($name) - Helper is disabled. Stop processing" - if ( $hash->{helper}{DISABLED} ); - return if ( $hash->{helper}{DISABLED} ); - - readingsSingleUpdate( $hash, 'presence', $response, 1 ); - - LGTV_WebOS_SocketClosePresenceAbsent( $hash, $response ); - + my ($string) = @_; + + my ($name,$response) = split("\\|",$string); + my $hash = $defs{$name}; + + + delete($hash->{helper}{RUNNING_PID}); + + Log3 $name, 4, "Sub LGTV_WebOS_PresenceDone ($name) - Helper is disabled. Stop processing" if($hash->{helper}{DISABLED}); + return if($hash->{helper}{DISABLED}); + + readingsSingleUpdate($hash, 'presence', $response, 1); + + LGTV_WebOS_SocketClosePresenceAbsent($hash,$response); + Log3 $name, 4, "Sub LGTV_WebOS_PresenceDone ($name) - presence done"; } sub LGTV_WebOS_PresenceAborted($) { - my ($hash) = @_; - my $name = $hash->{NAME}; + my ($hash) = @_; + my $name = $hash->{NAME}; - delete( $hash->{helper}{RUNNING_PID} ); - readingsSingleUpdate( $hash, 'presence', 'pingPresence timedout', 1 ); - - Log3 $name, 4, -"Sub LGTV_WebOS_PresenceAborted ($name) - The BlockingCall Process terminated unexpectedly. Timedout!"; + + delete($hash->{helper}{RUNNING_PID}); + readingsSingleUpdate($hash,'presence','pingPresence timedout', 1); + + Log3 $name, 4, "Sub LGTV_WebOS_PresenceAborted ($name) - The BlockingCall Process terminated unexpectedly. Timedout!"; } sub LGTV_WebOS_SocketClosePresenceAbsent($$) { - my ( $hash, $presence ) = @_; - - my $name = $hash->{NAME}; - + my ($hash,$presence) = @_; + + my $name = $hash->{NAME}; + + LGTV_WebOS_Close($hash) - if ( $presence eq 'absent' and not IsDisabled($name) and $hash->{CD} ) - ; # https://forum.fhem.de/index.php/topic,66671.msg694578.html#msg694578 - # Sobald pingPresence absent meldet und der Socket noch steht soll er geschlossen werden, da sonst FHEM nach 4-6 min für 10 min blockiert + if( $presence eq 'absent' and not IsDisabled($name) and $hash->{CD} ); # https://forum.fhem.de/index.php/topic,66671.msg694578.html#msg694578 + # Sobald pingPresence absent meldet und der Socket noch steht soll er geschlossen werden, da sonst FHEM nach 4-6 min für 10 min blockiert } sub LGTV_WebOS_WakeUp_Udp($@) { - my ( $hash, $mac_addr, $host, $port ) = @_; - my $name = $hash->{NAME}; + my ($hash,$mac_addr,$host,$port) = @_; + my $name = $hash->{NAME}; - $port = 9 if ( !defined $port || $port !~ /^\d+$/ ); - my $sock = new IO::Socket::INET( Proto => 'udp' ) or die "socket : $!"; - if ( !$sock ) { - Log3 $name, 3, - "Sub LGTV_WebOS_WakeUp_Udp ($name) - Can't create WOL socket"; + $port = 9 if (!defined $port || $port !~ /^\d+$/ ); + + my $sock = new IO::Socket::INET(Proto=>'udp') or die "socket : $!"; + if(!$sock) { + Log3 $name, 3, "Sub LGTV_WebOS_WakeUp_Udp ($name) - Can't create WOL socket"; return 1; } + + my $ip_addr = inet_aton($host); + my $sock_addr = sockaddr_in($port, $ip_addr); + $mac_addr =~ s/://g; + my $packet = pack('C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16); - my $ip_addr = inet_aton($host); - my $sock_addr = sockaddr_in( $port, $ip_addr ); - $mac_addr =~ s/://g; - my $packet = - pack( 'C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16 ); - - setsockopt( $sock, SOL_SOCKET, SO_BROADCAST, 1 ) or die "setsockopt : $!"; - send( $sock, $packet, 0, $sock_addr ) or die "send : $!"; - close($sock); + setsockopt($sock, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!"; + send($sock, $packet, 0, $sock_addr) or die "send : $!"; + close ($sock); return 1; } ####### Presence Erkennung Ende ############ + + + + + + + + + 1; + =pod =item device =item summary Controls LG SmartTVs run with WebOS Operating System (in beta phase) @@ -1747,6 +1570,12 @@ sub LGTV_WebOS_WakeUp_Udp($@) { Broadcast Address of the Network - wakeOnLanBroadcast <network>.255 +
    +
      +
    • wakeupCmd
    • + Set a command to be executed when turning on an absent device. Can be an FHEM command or Perl command in {}. +
    +
=end html @@ -1854,6 +1683,14 @@ sub LGTV_WebOS_WakeUp_Udp($@) { +
    +
      +
        +
      • wakeupCmd
      • + Befehl zum Einschalten des LG TV. Möglich ist ein FHEM Befehl oder Perl in {}. +
      +
    +



Generierte Readings/Events: