diff --git a/fhem/CHANGED b/fhem/CHANGED
index 88a31b027..70f60d484 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,7 @@
# 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: new module LGTV_IP12 for controlling LG SmartTV's manufactured
+ between 2012-2014 via network connection
- feature: 30_MilightBridge: Support tcp bridge.
- bugfix: 30_MilightBridge/98_ping: Use Blocking.pm for ping checks so
it does not block main thread.
diff --git a/fhem/FHEM/82_LGTV_IP12.pm b/fhem/FHEM/82_LGTV_IP12.pm
new file mode 100755
index 000000000..57aaceadd
--- /dev/null
+++ b/fhem/FHEM/82_LGTV_IP12.pm
@@ -0,0 +1,868 @@
+# $Id$
+##############################################################################
+#
+# 82_LGTV_IP12.pm
+# An FHEM Perl module for controlling LG Smart TV's which were
+# release between 2012 - 2014.
+#
+# based on 82_LGTV_IP12.pm from Julian Tatsch (http://www.tatsch-it.de/tag/lgtv/)
+#
+# Copyright by Markus Bloch
+# e-mail: Notausstieg0309@googlemail.com
+#
+# This file is part of fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+##############################################################################
+
+package main;
+
+use warnings;
+use strict;
+use HttpUtils;
+
+
+sub LGTV_IP12_displayPairingCode($);
+sub LGTV_IP12_Pair($$);
+sub LGTV_IP12_getInfo($$);
+sub LGTV_IP12_sendCommand($$);
+
+# remote control codes
+my %LGTV_IP12_rcCodes = (
+ "power"=>1,
+ "0"=>2,
+ "1"=>3,
+ "2"=>4,
+ "3"=>5,
+ "4"=>6,
+ "5"=>7,
+ "6"=>8,
+ "7"=>9,
+ "8"=>10,
+ "9"=>11,
+ "up"=>12,
+ "down"=>13,
+ "left"=>14,
+ "right"=>15,
+ "ok"=>20,
+ "home"=>21,
+ "menu"=>22,
+ "back"=>23,
+ "volumeUp"=>24,
+ "volumeDown"=>25,
+ "mute"=>26,
+ "channelUp"=>27,
+ "channelDown"=>28,
+ "blue"=>29,
+ "green"=>30,
+ "red"=>31,
+ "yellow"=>32,
+ "play"=>33,
+ "pause"=>34,
+ "stop"=>35,
+ "fastForward"=>36,
+ "rewind"=>37,
+ "skipForward"=>38,
+ "skipBackward"=>39,
+ "record"=>40,
+ "recordingList"=>41,
+ "repeat"=>42,
+ "liveTv"=>43,
+ "epg"=>44,
+ "info"=>45,
+ "ratio"=>46,
+ "input"=>47,
+ "PiP"=>48,
+ "subtitle"=>49,
+ "proglist"=>50,
+ "teletext"=>51,
+ "mark"=>52,
+ "3Dvideo"=>400,
+ "3D_L/R"=>401,
+ "dash"=>402,
+ "prevchannel"=>403,
+ "favouriteChannel"=>404,
+ "quickMenu"=>405,
+ "textOption"=>406,
+ "audioDescription"=>407,
+ "netCast"=>408,
+ "energySaving"=>409,
+ "avMode"=>410,
+ "simplink"=>411,
+ "exit"=>412,
+ "reservationProglist"=>413,
+ "PiP_channelUp"=>414,
+ "PiP_channelDown"=>415,
+ "switchPriSecVideo"=>416,
+ "myApps"=>417,
+);
+
+sub
+LGTV_IP12_Initialize($)
+{
+ my ($hash) = @_;
+
+ $hash->{DefFn} = "LGTV_IP12_Define";
+ $hash->{DeleteFn} = "LGTV_IP12_Delete";
+ $hash->{SetFn} = "LGTV_IP12_Set";
+ $hash->{GetFn} = "LGTV_IP12_Get";
+ $hash->{AttrFn} = "LGTV_IP12_Attr";
+ $hash->{NotifyFn} = "LGTV_IP12_Notify";
+ $hash->{NOTIFYDEV} = "global";
+ $hash->{AttrList} = "do_not_notify:0,1 pairingcode request-timeout:1,2,3,4,5 disable:0,1 ".$readingFnAttributes;
+}
+
+sub
+LGTV_IP12_Define($$)
+{
+ my ($hash, $def) = @_;
+ my @args = split("[ \t]+", $def);
+ my $name = $hash->{NAME};
+ if (int(@args) < 2)
+ {
+ return "LGTV_IP12: not enough arguments. Usage: " .
+ "define LGTV_IP12 ";
+ }
+
+ $hash->{HOST} = $args[2];
+ $hash->{PORT} = "8080";
+
+ # if an update interval was given which is greater than zero, use it.
+ if(defined($args[3]) and $args[3] > 0)
+ {
+ $hash->{helper}{OFF_INTERVAL} = $args[3];
+ }
+ else
+ {
+ $hash->{helper}{OFF_INTERVAL} = 30;
+ }
+
+ if(defined($args[4]) and $args[4] > 0)
+ {
+ $hash->{ON_INTERVAL} = $args[4];
+ $hash->{OFF_INTERVAL} = $hash->{helper}{OFF_INTERVAL};
+ $hash->{helper}{ON_INTERVAL} = $args[4];
+ }
+ else
+ {
+ $hash->{INTERVAL} = $hash->{helper}{OFF_INTERVAL};
+ $hash->{helper}{ON_INTERVAL} = $hash->{helper}{OFF_INTERVAL};
+ }
+
+ $hash->{helper}{DISABLED} = 0 unless(exists($hash->{helper}{DISABLED}));
+
+ $hash->{STATE} = 'defined';
+
+ return undef;
+}
+
+sub
+LGTV_IP12_Get($@)
+{
+ # not implemented yet
+
+}
+sub
+LGTV_IP12_Notify($$)
+{
+ my ($hash,$dev) = @_;
+ my $name = $hash->{NAME};
+
+ return if($dev->{NAME} ne "global");
+ return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
+
+ if(defined(AttrVal($name, "pairingcode", undef)) and AttrVal($name, "pairingcode", undef) =~/^\d{6}$/)
+ {
+ Log3 $name, 3, "LGTV_IP12 ($name) - try pairing with pairingcode ".AttrVal($name, "pairingcode", undef);
+ LGTV_IP12_Pair($hash, AttrVal($name, "pairingcode", undef));
+ }
+
+ LGTV_IP12_ResetTimer($hash, 0);
+}
+
+sub
+LGTV_IP12_Set($@)
+{
+ my ($hash, @args) = @_;
+ my $name = $hash->{NAME};
+
+ my $what = $args[1];
+ my $arg = $args[2];
+
+ my $usage = "Unknown argument $what, choose one of ". "statusRequest:noArg ".
+ "showPairCode:noArg ".
+ "removePairing:noArg ".
+ "remoteControl:".join(",", sort keys %LGTV_IP12_rcCodes)." ".
+ (exists($hash->{helper}{CHANNEL_LIST}) ? "channelDown:noArg channelUp:noArg channel:".join(",",sort {$a <=> $b} keys %{$hash->{helper}{CHANNEL_LIST}}) : "")." ".
+ (exists($hash->{helper}{APP_LIST}) ? "startApp:".join(",",sort {$a cmp $b} keys %{$hash->{helper}{APP_LIST}})." stopApp:".join(",",sort {$a cmp $b} keys %{$hash->{helper}{APP_LIST}}) : "")
+ ;
+
+ if($what eq "showPairCode")
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/pairing", $what, undef, "showKey");
+ }
+ elsif($what eq "removePairing")
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/pairing", $what, undef, "byebye8080");
+ }
+ elsif($what =~ /^(channel|channelUp|channelDown)$/)
+ {
+ unless(exists($hash->{helper}{CHANNEL_LIST}))
+ {
+ LGTV_IP12_RetrieveChannelList($hash);
+ }
+
+ my $new_channel;
+
+ if($what eq "channelUp" or $what eq "channelDown")
+ {
+ my $current_channel = ReadingsVal($name, "channel", undef);
+
+ if(defined($current_channel) and $current_channel =~ /^\d+$/ and $current_channel > 0)
+ {
+ my $found = 0;
+
+ $new_channel = (grep { $found++ < 1; } grep { ($what eq "channelUp" ? $_ > $current_channel : $_ < $current_channel ) } sort { ($what eq "channelUp" ? $a <=> $b : $b <=> $a) } grep { defined($_) and /^\d+$/ } keys %{$hash->{helper}{CHANNEL_LIST}})[0];
+
+ }
+ }
+ elsif($what eq "channel" and exists($hash->{helper}{CHANNEL_LIST}) and exists($hash->{helper}{CHANNEL_LIST}{$arg}))
+ {
+ $new_channel = $arg;
+ }
+ else
+ {
+ return $usage;
+ }
+
+ if(defined($new_channel))
+ {
+ Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - set new channel: $new_channel";
+
+ my $xml = "HandleChannelChange";
+ $xml .= "".$hash->{helper}{CHANNEL_LIST}{$new_channel}{major}."";
+ $xml .= "".$hash->{helper}{CHANNEL_LIST}{$new_channel}{minor}."";
+ $xml .= "".$hash->{helper}{CHANNEL_LIST}{$new_channel}{sourceIndex}."";
+ $xml .= "".$hash->{helper}{CHANNEL_LIST}{$new_channel}{physicalNum}."";
+ $xml .= "";
+
+ LGTV_IP12_HttpGet($hash, "/udap/api/command", "channel", $new_channel, $xml);
+ }
+ }
+ elsif($what eq "startApp" and exists($hash->{helper}{APP_LIST}) and exists($hash->{helper}{APP_LIST}{$arg}))
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "AppExecute".$hash->{helper}{APP_LIST}{$arg}{auid}."".$hash->{helper}{APP_LIST}{$arg}{name}."".$hash->{helper}{APP_LIST}{$arg}{cpid}."");
+ }
+ elsif($what eq "stopApp" and exists($hash->{helper}{APP_LIST}) and exists($hash->{helper}{APP_LIST}{$arg}))
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "AppTerminate".$hash->{helper}{APP_LIST}{$arg}{auid}."".$hash->{helper}{APP_LIST}{$arg}{name}."".$hash->{helper}{APP_LIST}{$arg}{cpid}."");
+ }
+ elsif($what eq "statusRequest")
+ {
+ LGTV_IP12_GetStatus($hash)
+ }
+ elsif($what eq "remoteControl" and exists($LGTV_IP12_rcCodes{$arg}))
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "HandleKeyInput".$LGTV_IP12_rcCodes{$arg}."");
+ }
+ else
+ {
+ return $usage;
+ }
+}
+
+##########################
+sub
+LGTV_IP12_Attr(@)
+{
+ my @a = @_;
+ my $hash = $defs{$a[1]};
+
+ if($a[0] eq "set" && $a[2] eq "pairingcode")
+ {
+ # if a pairing code was set as attribute, try immediatly a pairing
+ LGTV_IP12_Pair($hash, $a[3]);
+ }
+ elsif($a[0] eq "del" && $a[2] eq "pairingcode")
+ {
+ # if a pairing code is removed, start unpairing
+ LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "removePairing", undef, "byebye8080") if(exists($hash->{helper}{PAIRED}) and $hash->{helper}{PAIRED} == 1);
+ }
+
+ if($a[0] eq "set" && $a[2] eq "disable")
+ {
+ if($a[3] eq "0")
+ {
+ $hash->{helper}{DISABLED} = 0;
+ }
+ elsif($a[3] eq "1")
+ {
+ $hash->{helper}{DISABLED} = 1;
+ readingsSingleUpdate($hash, "state", "disabled",1);
+ }
+ LGTV_IP12_ResetTimer($hash, 0);
+ }
+ elsif($a[0] eq "del" && $a[2] eq "disable")
+ {
+ $hash->{helper}{DISABLED} = 0;
+ LGTV_IP12_ResetTimer($hash, 0);
+ }
+
+ return undef;
+}
+
+
+sub
+LGTV_IP12_Delete($$)
+{
+ my ($hash, $name) = @_;
+ # unpairing
+ LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "removePairing", undef, "byebye8080") if(exists($hash->{helper}{PAIRED}) and $hash->{helper}{PAIRED} == 1);
+}
+
+
+
+#################################
+
+# start a status request by starting the neccessary requests
+sub
+LGTV_IP12_GetStatus($)
+{
+ my ($hash) = @_;
+
+ unless(exists($hash->{helper}{CHANNEL_LIST}) and ReadingsVal($hash->{NAME}, "state", "off") eq "on")
+ {
+ LGTV_IP12_RetrieveChannelList($hash);
+ }
+
+ unless(exists($hash->{helper}{APP_LIST}) and ReadingsVal($hash->{NAME}, "state", "off") eq "on")
+ {
+ LGTV_IP12_HttpGet($hash, "/udap/api/data?target=applist_get&type=1&index=0&number=0", "statusRequest", "appList", undef);
+ }
+
+ LGTV_IP12_HttpGet($hash, "/udap/api/data?target=cur_channel", "statusRequest", "currentChannel");
+
+ LGTV_IP12_HttpGet($hash, "/udap/api/data?target=volume_info", "statusRequest", "volumeInfo");
+
+ LGTV_IP12_HttpGet($hash, "/udap/api/data?target=is_3d", "statusRequest", "is3d");
+
+ LGTV_IP12_ResetTimer($hash);
+}
+
+sub
+LGTV_IP12_ParseHttpResponse($$$)
+{
+
+ my ( $param, $err, $data ) = @_;
+
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+ my $cmd = $param->{cmd};
+ my $arg = $param->{arg};
+
+ $err = "" unless(defined($err));
+ $data = "" unless(defined($data));
+
+ # we successfully received a HTTP status code in the response
+ if($data eq "" and exists($param->{code}))
+ {
+ # when a HTTP 401 was received => UNAUTHORIZED => No Pairing
+ if($param->{code} eq 401)
+ {
+ Log3 $name, 3, "LGTV_IP12 ($name) - failed to execute \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": Device is not paired";
+
+ if(exists($hash->{helper}{PAIRED}))
+ {
+ if($hash->{helper}{PAIRED} == 1)
+ {
+ $hash->{helper}{PAIRED} = 0;
+ }
+ }
+
+ # If a pairing code is set as attribute, try one repair (when $hash->{helper}{PAIRED} == -1)
+ if(defined(AttrVal($name, "pairingcode", undef)) and AttrVal($name, "pairingcode", undef) =~/^\d{6}$/)
+ {
+ Log3 $name, 3, "LGTV_IP12 ($name) - try repairing with pairingcode ".AttrVal($name, "pairingcode", undef);
+ LGTV_IP12_Pair($hash, AttrVal($name, "pairingcode", undef));
+ return;
+ }
+ }
+
+ if($cmd eq "channel" and $param->{code} == 200)
+ {
+ readingsSingleUpdate($hash, $cmd, $arg, 1);
+ LGTV_IP12_ResetTimer($hash, 2);
+ }
+ }
+
+
+ # if an error was occured, raise a log entry
+ if($err ne "")
+ {
+ Log3 $name, 5, "LGTV_IP12 ($name) - could not execute command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" - $err";
+
+ readingsSingleUpdate($hash, "state", "off", 1);
+ }
+
+ # if the response contains data, examine it.
+ if($data ne "")
+ {
+ Log3 $name, 5, "LGTV_IP12 ($name) - got response for \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $data";
+
+ readingsSingleUpdate($hash, "state", "on", 1);
+
+ if($cmd eq "statusRequest")
+ {
+ readingsBeginUpdate($hash);
+
+ if($arg eq "volumeInfo")
+ {
+ if($data =~ /(.+?)<\/level>/)
+ {
+ readingsBulkUpdate($hash, "volume", $1);
+ }
+
+ if($data =~ /(.+?)<\/mute>/)
+ {
+ readingsBulkUpdate($hash, "mute", $1);
+ }
+ }
+
+ if($arg eq "currentChannel")
+ {
+ if($data =~ /(.+?)<\/inputSourceName>/)
+ {
+ readingsBulkUpdate($hash, "input", LGTV_IP12_html2txt($1));
+ }
+
+ if($data =~ /(.+?)<\/labelName>/)
+ {
+ readingsBulkUpdate($hash, "inputLabel", LGTV_IP12_html2txt($1));
+ }
+
+ if($data =~ /(.+?)<\/chname>/)
+ {
+ readingsBulkUpdate($hash, "channelName", LGTV_IP12_html2txt($1));
+ }
+
+ if($data =~ /(.+?)<\/major>/)
+ {
+ readingsBulkUpdate($hash, "channel", $1);
+ }
+
+ if($data =~ /(.+?)<\/progName>/)
+ {
+ readingsBulkUpdate($hash, "currentProgram", LGTV_IP12_html2txt($1));
+ }
+ }
+
+ if($arg eq "is3d")
+ {
+ if($data =~ /(.+?)<\/is3D>/)
+ {
+ readingsBulkUpdate($hash, "3D", $1);
+ }
+ }
+
+ if($arg eq "appList")
+ {
+ while($data =~ /([0-9a-f]+)<\/auid>\s*([^<]+?)\s*<\/name>(\d+)<\/type>([\w\d_-]*)<\/cpid>.*?<\/data>/gci)
+ {
+ my @fields = ($1,$2,$3,$4);
+ my $index = $2;
+ $index =~ s/[^a-z0-9\.-_ ]//gi;
+ $index =~ s/[\s,]+/_/g;
+ $hash->{helper}{APP_LIST}{$index}{auid} = $fields[0];
+ $hash->{helper}{APP_LIST}{$index}{name} = $fields[1];
+ $hash->{helper}{APP_LIST}{$index}{type} = $fields[2];
+ $hash->{helper}{APP_LIST}{$index}{cpid} = $fields[3];
+ }
+ }
+
+ readingsEndUpdate($hash, 1);
+
+ LGTV_IP12_RetrieveChannelList($hash) if(not exists($hash->{helper}{CHANNEL_LIST}));
+ }
+ }
+}
+
+# executes a http request with or without data and starts the HTTP request non-blocking to avoid timing problems for other modules (e.g. HomeMatic)
+sub
+LGTV_IP12_HttpGet($$$$;$)
+{
+my ($hash, $path, $cmd, $arg, $data) = @_;
+
+
+
+ if(defined($data))
+ {
+ Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - sending POST request for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" to url $path: $data";
+ # start a HTTP POST on the given url with content data
+ HttpUtils_NonblockingGet({
+ url => "http://".$hash->{HOST}.":8080".$path,
+ timeout => AttrVal($hash->{NAME}, "request-timeout", 4),
+ noshutdown => 1,
+ header => "User-Agent: Linux/2.6.18 UDAP/2.0 CentOS/5.8\r\nContent-Type: text/xml; charset=utf-8\r\nConnection: Close",
+ data => "".$data."",
+ loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
+ hash => $hash,
+ cmd => $cmd,
+ arg => $arg,
+ httpversion => "1.1",
+ callback => \&LGTV_IP12_ParseHttpResponse
+ });
+ }
+ else
+ {
+ Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - sending GET request for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" to url $path";
+
+ # start a HTTP GET on the given url
+ HttpUtils_NonblockingGet({
+ url => "http://".$hash->{HOST}.":8080".$path,
+ timeout => AttrVal($hash->{NAME}, "request-timeout", 4),
+ noshutdown => 1,
+ header => "User-Agent: Linux/2.6.18 UDAP/2.0 CentOS/5.8",
+ loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
+ hash => $hash,
+ cmd => $cmd,
+ arg => $arg,
+ httpversion => "1.1",
+ callback => \&LGTV_IP12_ParseHttpResponse
+ });
+ }
+}
+
+# sends the pairing request.
+sub
+LGTV_IP12_Pair($$)
+{
+ my ($hash, $code) = @_;
+
+ LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "pairing", $code, "hello$code8080");
+
+}
+
+sub
+LGTV_IP12_RetrieveChannelList($)
+{
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ Log3 $name, 4 , "LGTV_IP12 ($name) - requesting channel list";
+
+ my ($err, $channel_list) = HttpUtils_BlockingGet({
+ url => "http://".$hash->{HOST}.":8080/udap/api/data?target=channel_list",
+ timeout => AttrVal($hash->{NAME}, "request-timeout", 4),
+ noshutdown => 1,
+ header => "User-Agent: Linux/2.6.18 UDAP/2.0 CentOS/5.8",
+ loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
+ httpversion => "1.1",
+ });
+
+ Log3 $name, 3 , "LGTV_IP12 ($name) - error while retrieving channel list: $err" if($err ne "" and $hash->{helper}{AVAILABLE});
+
+ return if(not defined($channel_list) or $channel_list eq "");
+
+ delete($hash->{helper}{CHANNEL_LIST}) if(exists($hash->{helper}{CHANNEL_LIST}));
+
+ while($channel_list =~ /(.+?)<\/data>/gc)
+ {
+ my $channel = $1;
+ if($channel =~ /(\d+?)<\/major>/)
+ {
+ my $channel_major = $1;
+ $hash->{helper}{CHANNEL_LIST}{$channel_major}{major} = $channel_major;
+
+ if($channel =~ /(\d+?)<\/minor>/)
+ {
+ $hash->{helper}{CHANNEL_LIST}{$channel_major}{minor} = $1;
+ }
+
+ if($channel =~ /(\d+?)<\/sourceIndex>/)
+ {
+ $hash->{helper}{CHANNEL_LIST}{$channel_major}{sourceIndex} = $1;
+ }
+
+ if($channel =~ /(\d+?)<\/physicalNum>/)
+ {
+ $hash->{helper}{CHANNEL_LIST}{$channel_major}{physicalNum} = $1;
+ }
+
+ if($channel =~ /(.+?)<\/chname>/)
+ {
+ Log3 $name, 5 , "LGTV_IP12 ($name) - adding channel ".LGTV_IP12_html2txt($1);
+ $hash->{helper}{CHANNEL_LIST}{$channel_major}{chname} = LGTV_IP12_html2txt($1);
+ }
+ }
+ }
+}
+
+sub LGTV_IP12_ResetTimer($;$)
+{
+ my ($hash, $interval) = @_;
+
+ RemoveInternalTimer($hash);
+
+ if($hash->{helper}{DISABLED} == 0)
+ {
+ if(defined($interval))
+ {
+ InternalTimer(gettimeofday()+$interval, "LGTV_IP12_GetStatus", $hash, 0);
+ }
+ elsif(ReadingsVal($hash->{NAME}, "state", "off") eq "on")
+ {
+ InternalTimer(gettimeofday()+$hash->{helper}{ON_INTERVAL}, "LGTV_IP12_GetStatus", $hash, 0);
+ }
+ else
+ {
+ InternalTimer(gettimeofday()+$hash->{helper}{OFF_INTERVAL}, "LGTV_IP12_GetStatus", $hash, 0);
+ }
+ }
+}
+#############################
+# convert all HTML entities into UTF-8 aquivalents
+sub LGTV_IP12_html2txt($)
+{
+ my ($string) = @_;
+
+ $string =~ s/&/&/g;
+ $string =~ s/&/&/g;
+ $string =~ s/ / /g;
+ $string =~ s/'/'/g;
+ $string =~ s/(\xe4|ä)/ä/g;
+ $string =~ s/(\xc4|Ä)/Ä/g;
+ $string =~ s/(\xf6|ö)/ö/g;
+ $string =~ s/(\xd6|Ö)/Ö/g;
+ $string =~ s/(\xfc|ü)/ü/g;
+ $string =~ s/(\xdc|Ü)/Ü/g;
+ $string =~ s/(\xdf|ß)/ß/g;
+
+ $string =~ s/<.+?>//g;
+ $string =~ s/(^\s+|\s+$)//g;
+
+ return $string;
+}
+
+1;
+
+=pod
+=item device
+=begin html
+
+
+LGTV_IP12
+
+ This module controls LG SmartTV's which were released between 2012 - 2014 via network connection. You are able
+ to switch query it's power state, control the TV channels, open and close apps and send all remote control commands.
+
+ For a list of supported models see the compatibility list for LG TV Remote smartphone app.
+
+
+ Define
+
+
+ define <name> LGTV_IP12 <ip-address> [<status_interval>]
+
+ define <name> LGTV_IP12 <ip-address> [<off_status_interval>] [<on_status_interval>]
+
+
+
+ Defining a LGTV_IP12 device will schedule an internal task (interval can be set
+ with optional parameter <status_interval> in seconds, if not set, the value is 30
+ seconds), which periodically reads the status of the TV (power state, current channel, input, ...)
+ and triggers notify/FileLog commands.
+
+ Different status update intervals depending on the power state can be given also.
+ If two intervals are given to the define statement, the first interval statement represents the status update
+ interval in seconds in case the device is off, absent or any other non-normal state. The second
+ interval statement is used when the device is on.
+
+ Example:
+
+ define TV LGTV_IP12 192.168.0.10
+
+ # With custom status interval of 60 seconds
+ define TV LGTV_IP12 192.168.0.10 60
+
+ # With custom "off"-interval of 60 seconds and "on"-interval of 10 seconds
+ define TV LGTV_IP12 192.168.0.10 60 10
+
+
+
+
+
+
+ Set
+
+ set <name> <command> [<parameter>]
+
+ Currently, the following commands are defined.
+
+
+ - channel - set the current channel
+ - channelUp - switches to next channel
+ - channelDown - switches to previous channel
+ - removePairing - deletes the pairing with the device
+ - showPairCode - requests the TV to display the pair code on the TV screen. This pair code must be set in the attribute pairingcode
+ - startApp - start a installed app on the TV
+ - stopApp - stops a running app on the TV
+ - statusRequest - requests the current status of the device
+ - remoteControl up,down,... - sends remote control commands
+
+
+
+
+ Get
+
+ get <name> <reading>
+
+ Currently, the get command only returns the reading values. For a specific list of possible values, see section "Generated Readings/Events".
+
+
+
+ Attributes
+
+ - do_not_notify
+ - readingFnAttributes
+ - disable
+ Optional attribute to disable the internal cyclic status update of the player. Manual status updates via statusRequest command is still possible.
+
+ Possible values: 0 => perform cyclic status update, 1 => don't perform cyclic status updates.
+ - request-timeout
+ Optional attribute change the response timeout in seconds for all queries to the player.
+
+ Possible values: 1-5 seconds. Default value is 4 seconds.
+ - pairingcode
+ This attribute contains the pairing code to authenticate FHEM as trusted controller. The pairing code can be displayed via set command showPairCode
+
+
+ Generated Readings/Events:
+
+ - 3D - The status of 3D playback (can be "true" or "false")
+ - channel - The number of the current channel
+ - channelName - The name of the current channel
+ - currentProgram - The name of the running program of the current channel
+ - input - The current input source (e.g. Antenna, Sattelite, HDMI1, ...)
+ - inputLabel - The user defined name of the current input source
+ - mute - Reports the current mute state (can be "on" or "off")
+ - presence - Reports the presence state (can be "present" or "absent").
+ - volume - Reports the volume state.
+
+
+
+
+=end html
+=begin html_DE
+
+
+LGTV_IP12
+
+ Dieses Modul steuert SmartTV's des Herstellers LG welche zwischen 2012 und 2014 produziert wurden über die Netzwerkschnittstelle.
+ Es bietet die Möglichkeit den aktuellen TV Kanal zu steuern, sowie Apps zu starten, Fernbedienungsbefehle zu senden, sowie den aktuellen Status abzufragen.
+
+ Es werden alle TV Modelle unterstützt, welche mit der LG TV Remote Smartphone App steuerbar sind.
+
+
+ Definition
+
+ define <name> LGTV_IP12 <IP-Addresse> [<Status_Interval>]
+
+ define <name> LGTV_IP12 <IP-Addresse> [<Off_Interval>] [<On_Interval>]
+
+
+ Bei der Definition eines LGTV_IP12-Moduls wird eine interne Routine in Gang gesetzt, welche regelmäßig
+ (einstellbar durch den optionalen Parameter <Status_Interval>; falls nicht gesetzt ist der Standardwert 30 Sekunden)
+ den Status des TV abfragt und entsprechende Notify-/FileLog-Definitionen triggert.
+
+ Sofern 2 Interval-Argumente übergeben werden, wird der erste Parameter <Off_Interval> genutzt
+ sofern der TV ausgeschaltet oder nicht erreichbar ist. Der zweiter Parameter <On_Interval>
+ wird verwendet, sofern der TV eingeschaltet ist.
+
+ Beispiel:
+
+ define TV LGTV_IP12 192.168.0.10
+
+ # Mit modifiziertem Status Interval (60 Sekunden)
+ define TV LGTV_IP12 192.168.0.10 60
+
+ # Mit gesetztem "Off"-Interval (60 Sekunden) und "On"-Interval (10 Sekunden)
+ define TV LGTV_IP12 192.168.0.10 60 10
+
+
+
+
+ Set-Kommandos
+
+ set <Name> <Kommando> [<Parameter>]
+
+ Aktuell werden folgende Kommandos unterstützt.
+
+
+ - channel <Nummer> - wählt den aktuellen TV-Kanal aus
+ - channelUp - schaltet auf den nächsten Kanal um
+ - channelDown - schaltet auf den vorherigen Kanal um
+ - removePairing - löscht das Pairing zwischen FHEM und dem TV
+ - showPairCode - zeigt den Pair-Code auf dem TV-Bildschirm an. Dieser Code muss im Attribut pairingcode gesetzt werden, damit FHEM mit dem TV kommunizieren kann.
+ - startApp <Name> - startet eine installierte App
+ - stopApp <Name> - stoppt eine laufende App
+ - statusRequest - fragt den aktuellen Status ab
+ - remoteControl up,down,... - sendet Fernbedienungsbefehle
+
+
+
+
+ Get-Kommandos
+
+ get <Name> <Readingname>
+
+ Aktuell stehen via GET lediglich die Werte der Readings zur Verfügung. Eine genaue Auflistung aller möglichen Readings folgen unter "Generierte Readings/Events".
+
+
+
+ Attribute
+
+
+ - do_not_notify
+ - readingFnAttributes
+ - disable
+ Optionales Attribut zur Deaktivierung des zyklischen Status-Updates. Ein manuelles Update via statusRequest-Befehl ist dennoch möglich.
+
+ Mögliche Werte: 0 => zyklische Status-Updates, 1 => keine zyklischen Status-Updates.
+ - request-timeout
+ Optionales Attribut. Maximale Dauer einer Anfrage in Sekunden zum TV.
+
+ Mögliche Werte: 1-5 Sekunden. Standartwert ist 4 Sekunden
+ - pairingcode
+ Dieses Attribut speichert den Pairing Code um sich gegenüber dem TV als vertrauenswürdigen Controller zu authentifizieren. Der Pairing-Code kann via Set-Kommando showPairCode angezeigt werden.
+
+
+ Generierte Readings/Events:
+
+ - 3D - Status des 3D-Wiedergabemodus ("true" => 3D Wiedergabemodus aktiv, "false" => 3D Wiedergabemodus nicht aktiv)
+ - channel - Die Nummer des aktuellen TV-Kanals
+ - channelName - Der Name des aktuellen TV-Kanals
+ - currentProgram - Der Name der laufenden Sendung
+ - input - Die aktuelle Eingangsquelle (z.B. Antenna, Sattelite, HDMI1, ...)
+ - inputLabel - Die benutzerdefinierte Bezeichnung der aktuellen Eingangsquelle
+ - mute on,off - Der aktuelle Stumm-Status ("on" => Stumm, "off" => Laut)
+ - presence - Zeigt an, ob das Gerät aktuell empfangsbereit ist ("present" => Gerät ist empfangsbereit, "absent" => Gerät ist nicht empfangsbereit).
+ - volume - Der aktuelle Lautstärkepegel.
+
+
+=end html_DE
+
+=cut
+
+
diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index 56ba79207..72f73fdf5 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -263,6 +263,7 @@ FHEM/80_M232.pm borisneubert http://forum.fhem.de Sonstige
FHEM/80_xxLG7000.pm markusbloch http://forum.fhem.de Multimedia
FHEM/81_M232Counter.pm borisneubert http://forum.fhem.de Sonstige Systeme
FHEM/82_LGTV.pm markusbloch http://forum.fhem.de Multimedia
+FHEM/82_LGTV_IP12.pm markusbloch http://forum.fhem.de Multimedia
FHEM/82_M232Voltage.pm borisneubert http://forum.fhem.de Sonstige Systeme
FHEM/87_WS2000.pm tdressler http://forum.fhem.de Sonstiges
FHEM/88_ALL4000T.pm sachag http://forum.fhem.de Sonstiges