82_LGTV_IP12.pm 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. # $Id: 82_LGTV_IP12.pm 15140 2017-09-26 09:20:09Z markusbloch $
  2. ##############################################################################
  3. #
  4. # 82_LGTV_IP12.pm
  5. # An FHEM Perl module for controlling LG Smart TV's which were
  6. # release between 2012 - 2014.
  7. #
  8. # based on 82_LGTV_IP12.pm from Julian Tatsch (http://www.tatsch-it.de/tag/lgtv/)
  9. #
  10. # Copyright by Markus Bloch
  11. # e-mail: Notausstieg0309@googlemail.com
  12. #
  13. # This file is part of fhem.
  14. #
  15. # Fhem is free software: you can redistribute it and/or modify
  16. # it under the terms of the GNU General Public License as published by
  17. # the Free Software Foundation, either version 2 of the License, or
  18. # (at your option) any later version.
  19. #
  20. # Fhem is distributed in the hope that it will be useful,
  21. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. # GNU General Public License for more details.
  24. #
  25. # You should have received a copy of the GNU General Public License
  26. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  27. #
  28. ##############################################################################
  29. package main;
  30. use warnings;
  31. use strict;
  32. use HttpUtils;
  33. sub LGTV_IP12_displayPairingCode($);
  34. sub LGTV_IP12_Pair($$);
  35. sub LGTV_IP12_getInfo($$);
  36. sub LGTV_IP12_sendCommand($$);
  37. # remote control codes
  38. my %LGTV_IP12_rcCodes = (
  39. "power"=>1,
  40. "0"=>2,
  41. "1"=>3,
  42. "2"=>4,
  43. "3"=>5,
  44. "4"=>6,
  45. "5"=>7,
  46. "6"=>8,
  47. "7"=>9,
  48. "8"=>10,
  49. "9"=>11,
  50. "up"=>12,
  51. "down"=>13,
  52. "left"=>14,
  53. "right"=>15,
  54. "ok"=>20,
  55. "home"=>21,
  56. "menu"=>22,
  57. "back"=>23,
  58. "volumeUp"=>24,
  59. "volumeDown"=>25,
  60. "mute"=>26,
  61. "channelUp"=>27,
  62. "channelDown"=>28,
  63. "blue"=>29,
  64. "green"=>30,
  65. "red"=>31,
  66. "yellow"=>32,
  67. "play"=>33,
  68. "pause"=>34,
  69. "stop"=>35,
  70. "fastForward"=>36,
  71. "rewind"=>37,
  72. "skipForward"=>38,
  73. "skipBackward"=>39,
  74. "record"=>40,
  75. "recordingList"=>41,
  76. "repeat"=>42,
  77. "liveTv"=>43,
  78. "epg"=>44,
  79. "info"=>45,
  80. "ratio"=>46,
  81. "input"=>47,
  82. "PiP"=>48,
  83. "subtitle"=>49,
  84. "proglist"=>50,
  85. "teletext"=>51,
  86. "mark"=>52,
  87. "3Dvideo"=>400,
  88. "3D_L/R"=>401,
  89. "dash"=>402,
  90. "prevchannel"=>403,
  91. "favouriteChannel"=>404,
  92. "quickMenu"=>405,
  93. "textOption"=>406,
  94. "audioDescription"=>407,
  95. "netCast"=>408,
  96. "energySaving"=>409,
  97. "avMode"=>410,
  98. "simplink"=>411,
  99. "exit"=>412,
  100. "reservationProglist"=>413,
  101. "PiP_channelUp"=>414,
  102. "PiP_channelDown"=>415,
  103. "switchPriSecVideo"=>416,
  104. "myApps"=>417,
  105. );
  106. #################################
  107. sub
  108. LGTV_IP12_Initialize($)
  109. {
  110. my ($hash) = @_;
  111. $hash->{DefFn} = "LGTV_IP12_Define";
  112. $hash->{DeleteFn} = "LGTV_IP12_Delete";
  113. $hash->{UndefFn} = "LGTV_IP12_Undef";
  114. $hash->{SetFn} = "LGTV_IP12_Set";
  115. $hash->{GetFn} = "LGTV_IP12_Get";
  116. $hash->{AttrFn} = "LGTV_IP12_Attr";
  117. $hash->{NotifyFn} = "LGTV_IP12_Notify";
  118. $hash->{AttrList} = "do_not_notify:0,1 pairingcode request-timeout:1,2,3,4,5 disable:0,1 disabledForIntervals ".$readingFnAttributes;
  119. }
  120. #################################
  121. sub
  122. LGTV_IP12_Define($$)
  123. {
  124. my ($hash, $def) = @_;
  125. my @args = split("[ \t]+", $def);
  126. my $name = $hash->{NAME};
  127. if (int(@args) < 2)
  128. {
  129. return "LGTV_IP12: not enough arguments. Usage: " .
  130. "define <name> LGTV_IP12 <HOST>";
  131. }
  132. $hash->{HOST} = $args[2];
  133. $hash->{PORT} = "8080";
  134. # if an update interval was given which is greater than zero, use it.
  135. if(defined($args[3]) and $args[3] > 0)
  136. {
  137. $hash->{helper}{OFF_INTERVAL} = $args[3];
  138. }
  139. else
  140. {
  141. $hash->{helper}{OFF_INTERVAL} = 30;
  142. }
  143. if(defined($args[4]) and $args[4] > 0)
  144. {
  145. $hash->{ON_INTERVAL} = $args[4];
  146. $hash->{OFF_INTERVAL} = $hash->{helper}{OFF_INTERVAL};
  147. $hash->{helper}{ON_INTERVAL} = $args[4];
  148. }
  149. else
  150. {
  151. $hash->{INTERVAL} = $hash->{helper}{OFF_INTERVAL};
  152. $hash->{helper}{ON_INTERVAL} = $hash->{helper}{OFF_INTERVAL};
  153. }
  154. $hash->{STATE} = 'defined';
  155. $hash->{NOTIFYDEV} = "global";
  156. return undef;
  157. }
  158. #################################
  159. sub
  160. LGTV_IP12_Get($@)
  161. {
  162. my ($hash, @a) = @_;
  163. my $what;
  164. my $return;
  165. return "argument is missing" if(int(@a) != 2);
  166. $what = $a[1];
  167. return ReadingsVal($hash->{NAME}, $what, "") if(defined(ReadingsVal($hash->{NAME}, $what, undef)));
  168. $return = "unknown argument $what, choose one of";
  169. foreach my $reading (keys %{$hash->{READINGS}})
  170. {
  171. $return .= " $reading:noArg";
  172. }
  173. return $return;
  174. }
  175. #################################
  176. sub
  177. LGTV_IP12_Notify($$)
  178. {
  179. my ($hash,$dev) = @_;
  180. my $name = $hash->{NAME};
  181. return unless(exists($dev->{NAME}) and $dev->{NAME} eq "global");
  182. if(grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}))
  183. {
  184. if(defined(AttrVal($name, "pairingcode", undef)) and AttrVal($name, "pairingcode", undef) =~/^\d{6}$/)
  185. {
  186. Log3 $name, 3, "LGTV_IP12 ($name) - try pairing with pairingcode ".AttrVal($name, "pairingcode", undef);
  187. LGTV_IP12_Pair($hash, AttrVal($name, "pairingcode", undef));
  188. }
  189. LGTV_IP12_ResetTimer($hash, 0);
  190. }
  191. elsif(grep(m/^(?:ATTR $name disable.*|DELETEATTR $name disable.*)$/, @{$dev->{CHANGED}}))
  192. {
  193. LGTV_IP12_ResetTimer($hash, 0);
  194. }
  195. }
  196. #################################
  197. sub
  198. LGTV_IP12_Set($@)
  199. {
  200. my ($hash, @args) = @_;
  201. my $name = $hash->{NAME};
  202. my $what = $args[1];
  203. my $arg = $args[2];
  204. my $usage = "Unknown argument $what, choose one of ". "statusRequest:noArg ".
  205. "showPairCode:noArg ".
  206. "removePairing:noArg ".
  207. "remoteControl:".join(",", sort keys %LGTV_IP12_rcCodes)." ".
  208. (exists($hash->{helper}{CHANNEL_LIST}) ? "channelDown:noArg channelUp:noArg channel:".join(",",sort {$a <=> $b} keys %{$hash->{helper}{CHANNEL_LIST}}) : "")." ".
  209. (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}}) : "")
  210. ;
  211. if($what eq "showPairCode")
  212. {
  213. LGTV_IP12_HttpGet($hash, "/udap/api/pairing", $what, undef, "<api type=\"pairing\"><name>showKey</name></api>");
  214. }
  215. elsif($what eq "removePairing")
  216. {
  217. LGTV_IP12_HttpGet($hash, "/udap/api/pairing", $what, undef, "<api type=\"pairing\"><name>byebye</name><port>8080</port></api>");
  218. }
  219. elsif($what =~ /^(channel|channelUp|channelDown)$/)
  220. {
  221. unless(exists($hash->{helper}{CHANNEL_LIST}))
  222. {
  223. LGTV_IP12_ResetTimer($hash, 0);
  224. }
  225. my $new_channel;
  226. if($what eq "channelUp" or $what eq "channelDown")
  227. {
  228. my $current_channel = ReadingsVal($name, "channel", undef);
  229. if(defined($current_channel) and $current_channel =~ /^\d+$/ and $current_channel > 0)
  230. {
  231. my $found = 0;
  232. $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];
  233. }
  234. }
  235. elsif($what eq "channel" and exists($hash->{helper}{CHANNEL_LIST}) and exists($hash->{helper}{CHANNEL_LIST}{$arg}))
  236. {
  237. $new_channel = $arg;
  238. }
  239. else
  240. {
  241. return $usage;
  242. }
  243. if(defined($new_channel))
  244. {
  245. Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - set new channel: $new_channel";
  246. my $xml = "<api type=\"command\"><name>HandleChannelChange</name>";
  247. $xml .= "<major>".$hash->{helper}{CHANNEL_LIST}{$new_channel}{major}."</major>";
  248. $xml .= "<minor>".$hash->{helper}{CHANNEL_LIST}{$new_channel}{minor}."</minor>";
  249. $xml .= "<sourceIndex>".$hash->{helper}{CHANNEL_LIST}{$new_channel}{sourceIndex}."</sourceIndex>";
  250. $xml .= "<physicalNum>".$hash->{helper}{CHANNEL_LIST}{$new_channel}{physicalNum}."</physicalNum>";
  251. $xml .= "</api>";
  252. LGTV_IP12_HttpGet($hash, "/udap/api/command", "channel", $new_channel, $xml);
  253. }
  254. }
  255. elsif($what eq "startApp" and exists($hash->{helper}{APP_LIST}) and exists($hash->{helper}{APP_LIST}{$arg}))
  256. {
  257. LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "<api type=\"command\"><name>AppExecute</name><auid>".$hash->{helper}{APP_LIST}{$arg}{auid}."</auid><appname>".$hash->{helper}{APP_LIST}{$arg}{name}."</appname><contentId>".$hash->{helper}{APP_LIST}{$arg}{cpid}."</contentId></api>");
  258. }
  259. elsif($what eq "stopApp" and exists($hash->{helper}{APP_LIST}) and exists($hash->{helper}{APP_LIST}{$arg}))
  260. {
  261. LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "<api type=\"command\"><name>AppTerminate</name><auid>".$hash->{helper}{APP_LIST}{$arg}{auid}."</auid><appname>".$hash->{helper}{APP_LIST}{$arg}{name}."</appname><contentId>".$hash->{helper}{APP_LIST}{$arg}{cpid}."</contentId></api>");
  262. }
  263. elsif($what eq "statusRequest")
  264. {
  265. LGTV_IP12_GetStatus($hash)
  266. }
  267. elsif($what eq "remoteControl" and exists($LGTV_IP12_rcCodes{$arg}))
  268. {
  269. LGTV_IP12_HttpGet($hash, "/udap/api/command", $what, $arg, "<api type=\"command\"><name>HandleKeyInput</name><value>".$LGTV_IP12_rcCodes{$arg}."</value></api>");
  270. }
  271. else
  272. {
  273. return $usage;
  274. }
  275. }
  276. ##########################
  277. sub
  278. LGTV_IP12_Attr(@)
  279. {
  280. my @a = @_;
  281. my $hash = $defs{$a[1]};
  282. if($a[0] eq "set" && $a[2] eq "pairingcode")
  283. {
  284. # if a pairing code was set as attribute, try immediatly a pairing
  285. LGTV_IP12_Pair($hash, $a[3]);
  286. }
  287. elsif($a[0] eq "del" && $a[2] eq "pairingcode")
  288. {
  289. # if a pairing code is removed, start unpairing
  290. LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "removePairing", undef, "<api type=\"pairing\"><name>byebye</name><port>8080</port></api>") if(exists($hash->{helper}{PAIRED}) and $hash->{helper}{PAIRED} == 1);
  291. }
  292. if($a[0] eq "set" && $a[2] eq "disable")
  293. {
  294. if($a[3] eq "1")
  295. {
  296. readingsSingleUpdate($hash, "state", "disabled",1);
  297. }
  298. LGTV_IP12_ResetTimer($hash, 0);
  299. }
  300. elsif($a[0] eq "del" && $a[2] eq "disable")
  301. {
  302. LGTV_IP12_ResetTimer($hash, 0);
  303. }
  304. return undef;
  305. }
  306. #################################
  307. sub
  308. LGTV_IP12_Delete($$)
  309. {
  310. my ($hash, $name) = @_;
  311. # unpairing
  312. LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "removePairing", undef, "<api type=\"pairing\"><name>byebye</name><port>8080</port></api>") if(exists($hash->{helper}{PAIRED}) and $hash->{helper}{PAIRED} == 1);
  313. }
  314. #################################
  315. sub
  316. LGTV_IP12_Undef($$)
  317. {
  318. my ($hash, $name) = @_;
  319. RemoveInternalTimer($hash);
  320. }
  321. ############################################################################################################
  322. #
  323. # Begin of helper functions
  324. #
  325. ############################################################################################################
  326. #################################
  327. # start a status request by starting the neccessary requests
  328. sub
  329. LGTV_IP12_GetStatus($)
  330. {
  331. my ($hash) = @_;
  332. unless(exists($hash->{helper}{CHANNEL_LIST}) and ReadingsVal($hash->{NAME}, "state", "off") eq "on")
  333. {
  334. LGTV_IP12_HttpGet($hash, "/udap/api/data?target=channel_list", "statusRequest", "channelList", undef);
  335. }
  336. unless(exists($hash->{helper}{APP_LIST}) and ReadingsVal($hash->{NAME}, "state", "off") eq "on")
  337. {
  338. LGTV_IP12_HttpGet($hash, "/udap/api/data?target=applist_get&type=1&index=0&number=0", "statusRequest", "appList", undef);
  339. }
  340. LGTV_IP12_HttpGet($hash, "/udap/api/data?target=cur_channel", "statusRequest", "currentChannel");
  341. LGTV_IP12_HttpGet($hash, "/udap/api/data?target=volume_info", "statusRequest", "volumeInfo");
  342. LGTV_IP12_HttpGet($hash, "/udap/api/data?target=is_3d", "statusRequest", "is3d");
  343. LGTV_IP12_ResetTimer($hash);
  344. }
  345. #################################
  346. # parses the HTTP response from the TV
  347. sub
  348. LGTV_IP12_ParseHttpResponse($$$)
  349. {
  350. my ( $param, $err, $data ) = @_;
  351. my $hash = $param->{hash};
  352. my $name = $hash->{NAME};
  353. my $cmd = $param->{cmd};
  354. my $arg = $param->{arg};
  355. $err = "" unless(defined($err));
  356. $data = "" unless(defined($data));
  357. # we successfully received a HTTP status code in the response
  358. if($data eq "" and exists($param->{code}))
  359. {
  360. # when a HTTP 401 was received => UNAUTHORIZED => No Pairing
  361. if($param->{code} eq 401)
  362. {
  363. Log3 $name, 3, "LGTV_IP12 ($name) - failed to execute \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": Device is not paired";
  364. if(exists($hash->{helper}{PAIRED}))
  365. {
  366. if($hash->{helper}{PAIRED} == 1)
  367. {
  368. $hash->{helper}{PAIRED} = 0;
  369. }
  370. }
  371. # If a pairing code is set as attribute, try one repair (when $hash->{helper}{PAIRED} == -1)
  372. if(defined(AttrVal($name, "pairingcode", undef)) and AttrVal($name, "pairingcode", undef) =~/^\d{6}$/)
  373. {
  374. Log3 $name, 3, "LGTV_IP12 ($name) - try repairing with pairingcode ".AttrVal($name, "pairingcode", undef);
  375. LGTV_IP12_Pair($hash, AttrVal($name, "pairingcode", undef));
  376. return;
  377. }
  378. }
  379. if($cmd eq "channel" and $param->{code} == 200)
  380. {
  381. readingsSingleUpdate($hash, $cmd, $arg, 1);
  382. LGTV_IP12_ResetTimer($hash, 2);
  383. return;
  384. }
  385. }
  386. readingsBeginUpdate($hash);
  387. # if an error was occured, raise a log entry
  388. if($err ne "")
  389. {
  390. Log3 $name, 5, "LGTV_IP12 ($name) - could not execute command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" - $err";
  391. readingsBulkUpdate($hash, "state", "off");
  392. readingsBulkUpdate($hash, "power", "off");
  393. }
  394. # if the response contains data, examine it.
  395. if($data ne "")
  396. {
  397. Log3 $name, 5, "LGTV_IP12 ($name) - got response for \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\": $data";
  398. readingsBulkUpdate($hash, "state", "on");
  399. readingsBulkUpdate($hash, "power", "on");
  400. if($cmd eq "statusRequest")
  401. {
  402. if($arg eq "volumeInfo")
  403. {
  404. if($data =~ /<level>(.+?)<\/level>/)
  405. {
  406. readingsBulkUpdate($hash, "volume", $1);
  407. }
  408. if($data =~ /<mute>(.+?)<\/mute>/)
  409. {
  410. readingsBulkUpdate($hash, "mute", ($1 eq "true" ? "on" : "off"));
  411. }
  412. }
  413. if($arg eq "currentChannel")
  414. {
  415. if($data =~ /<inputSourceName>(.+?)<\/inputSourceName>/)
  416. {
  417. readingsBulkUpdate($hash, "input", LGTV_IP12_html2txt($1));
  418. }
  419. if($data =~ /<labelName>(.+?)<\/labelName>/)
  420. {
  421. readingsBulkUpdate($hash, "inputLabel", LGTV_IP12_html2txt($1));
  422. }
  423. if($data =~ /<chname>(.+?)<\/chname>/)
  424. {
  425. readingsBulkUpdate($hash, "channelName", LGTV_IP12_html2txt($1));
  426. }
  427. if($data =~ /<major>(.+?)<\/major>/)
  428. {
  429. readingsBulkUpdate($hash, "channel", $1);
  430. }
  431. if($data =~ /<progName>(.+?)<\/progName>/)
  432. {
  433. readingsBulkUpdate($hash, "currentProgram", LGTV_IP12_html2txt($1));
  434. }
  435. }
  436. if($arg eq "is3d")
  437. {
  438. if($data =~ /<is3D>(.+?)<\/is3D>/)
  439. {
  440. readingsBulkUpdate($hash, "3D", $1);
  441. }
  442. }
  443. if($arg eq "appList")
  444. {
  445. while($data =~ /<data><auid>([0-9a-f]+)<\/auid><name>\s*([^<]+?)\s*<\/name><type>(\d+)<\/type><cpid>([\w\d_-]*)<\/cpid>.*?<\/data>/gci)
  446. {
  447. my @fields = ($1,$2,$3,$4);
  448. my $index = $2;
  449. $index =~ s/[^a-z0-9\.-_ ]//gi;
  450. $index =~ s/[\s,]+/_/g;
  451. $hash->{helper}{APP_LIST}{$index}{auid} = $fields[0];
  452. $hash->{helper}{APP_LIST}{$index}{name} = $fields[1];
  453. $hash->{helper}{APP_LIST}{$index}{type} = $fields[2];
  454. $hash->{helper}{APP_LIST}{$index}{cpid} = $fields[3];
  455. }
  456. }
  457. if($arg eq "channelList")
  458. {
  459. delete($hash->{helper}{CHANNEL_LIST}) if(exists($hash->{helper}{CHANNEL_LIST}));
  460. while($data =~ /<data>(.+?)<\/data>/gc)
  461. {
  462. my $channel = $1;
  463. if($channel =~ /<major>(\d+?)<\/major>/)
  464. {
  465. my $channel_major = $1;
  466. $hash->{helper}{CHANNEL_LIST}{$channel_major}{major} = $channel_major;
  467. if($channel =~ /<minor>(\d+?)<\/minor>/)
  468. {
  469. $hash->{helper}{CHANNEL_LIST}{$channel_major}{minor} = $1;
  470. }
  471. if($channel =~ /<sourceIndex>(\d+?)<\/sourceIndex>/)
  472. {
  473. $hash->{helper}{CHANNEL_LIST}{$channel_major}{sourceIndex} = $1;
  474. }
  475. if($channel =~ /<physicalNum>(\d+?)<\/physicalNum>/)
  476. {
  477. $hash->{helper}{CHANNEL_LIST}{$channel_major}{physicalNum} = $1;
  478. }
  479. if($channel =~ /<chname>(.+?)<\/chname>/)
  480. {
  481. Log3 $name, 5 , "LGTV_IP12 ($name) - adding channel ".LGTV_IP12_html2txt($1);
  482. $hash->{helper}{CHANNEL_LIST}{$channel_major}{chname} = LGTV_IP12_html2txt($1);
  483. }
  484. }
  485. }
  486. }
  487. }
  488. }
  489. readingsEndUpdate($hash, 1);
  490. }
  491. #################################
  492. # 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)
  493. sub
  494. LGTV_IP12_HttpGet($$$$;$)
  495. {
  496. my ($hash, $path, $cmd, $arg, $data) = @_;
  497. if(defined($data))
  498. {
  499. Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - sending POST request for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" to url $path: $data";
  500. # start a HTTP POST on the given url with content data
  501. HttpUtils_NonblockingGet({
  502. url => "http://".$hash->{HOST}.":8080".$path,
  503. timeout => AttrVal($hash->{NAME}, "request-timeout", 4),
  504. noshutdown => 1,
  505. header => "User-Agent: Linux/2.6.18 UDAP/2.0 CentOS/5.8\r\nContent-Type: text/xml; charset=utf-8\r\nConnection: Close",
  506. data => "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope>".$data."</envelope>",
  507. loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
  508. hash => $hash,
  509. cmd => $cmd,
  510. arg => $arg,
  511. httpversion => "1.1",
  512. callback => \&LGTV_IP12_ParseHttpResponse
  513. });
  514. }
  515. else
  516. {
  517. Log3 $hash->{NAME}, 5 , "LGTV_IP12 (".$hash->{NAME}.") - sending GET request for command \"$cmd".(defined($arg) ? " ".(split("\\|", $arg))[0] : "")."\" to url $path";
  518. # start a HTTP GET on the given url
  519. HttpUtils_NonblockingGet({
  520. url => "http://".$hash->{HOST}.":8080".$path,
  521. timeout => AttrVal($hash->{NAME}, "request-timeout", 4),
  522. noshutdown => 1,
  523. header => "User-Agent: Linux/2.6.18 UDAP/2.0 CentOS/5.8",
  524. loglevel => ($hash->{helper}{AVAILABLE} ? undef : 5),
  525. hash => $hash,
  526. cmd => $cmd,
  527. arg => $arg,
  528. httpversion => "1.1",
  529. callback => \&LGTV_IP12_ParseHttpResponse
  530. });
  531. }
  532. }
  533. #################################
  534. # sends the pairing request.
  535. sub
  536. LGTV_IP12_Pair($$)
  537. {
  538. my ($hash, $code) = @_;
  539. LGTV_IP12_HttpGet($hash, "/udap/api/pairing", "pairing", $code, "<api type=\"pairing\"><name>hello</name><value>$code</value><port>8080</port></api>");
  540. }
  541. #################################
  542. # resets the status update timer according to the current state
  543. sub LGTV_IP12_ResetTimer($;$)
  544. {
  545. my ($hash, $interval) = @_;
  546. RemoveInternalTimer($hash);
  547. unless(IsDisabled($hash->{NAME}))
  548. {
  549. if(defined($interval))
  550. {
  551. InternalTimer(gettimeofday()+$interval, "LGTV_IP12_GetStatus", $hash, 0);
  552. }
  553. elsif(ReadingsVal($hash->{NAME}, "state", "off") eq "on")
  554. {
  555. InternalTimer(gettimeofday()+$hash->{helper}{ON_INTERVAL}, "LGTV_IP12_GetStatus", $hash, 0);
  556. }
  557. else
  558. {
  559. InternalTimer(gettimeofday()+$hash->{helper}{OFF_INTERVAL}, "LGTV_IP12_GetStatus", $hash, 0);
  560. }
  561. }
  562. return undef;
  563. }
  564. #############################
  565. # convert all HTML entities into UTF-8 aquivalents
  566. sub LGTV_IP12_html2txt($)
  567. {
  568. my ($string) = @_;
  569. $string =~ s/&amp;/&/g;
  570. $string =~ s/&amp;/&/g;
  571. $string =~ s/&nbsp;/ /g;
  572. $string =~ s/&apos;/'/g;
  573. $string =~ s/(\xe4|&auml;)/ä/g;
  574. $string =~ s/(\xc4|&Auml;)/Ä/g;
  575. $string =~ s/(\xf6|&ouml;)/ö/g;
  576. $string =~ s/(\xd6|&Ouml;)/Ö/g;
  577. $string =~ s/(\xfc|&uuml;)/ü/g;
  578. $string =~ s/(\xdc|&Uuml;)/Ü/g;
  579. $string =~ s/(\xdf|&szlig;)/ß/g;
  580. $string =~ s/<.+?>//g;
  581. $string =~ s/(^\s+|\s+$)//g;
  582. return $string;
  583. }
  584. 1;
  585. =pod
  586. =item device
  587. =item summary controls LG SmartTV's build between 2012-2014 via LAN connection
  588. =item summary_DE steuert LG SmartTV's via LAN, welche zwischen 2012-2014 hergestellt wurden
  589. =begin html
  590. <a name="LGTV_IP12"></a>
  591. <h3>LGTV_IP12</h3>
  592. <ul>
  593. This module controls LG SmartTV's which were released between 2012 - 2014 via network connection. You are able
  594. to switch query it's power state, control the TV channels, open and close apps and send all remote control commands.
  595. <br><br>
  596. For a list of supported models see the compatibility list for <a href="https://itunes.apple.com/de/app/lg-tv-remote/id509979485?mt=8" target="_new">LG TV Remote</a> smartphone app.
  597. <br><br>
  598. <a name="LGTV_IP12_define"></a>
  599. <b>Define</b>
  600. <ul>
  601. <code>
  602. define &lt;name&gt; LGTV_IP12 &lt;ip-address&gt; [&lt;status_interval&gt;]
  603. <br><br>
  604. define &lt;name&gt; LGTV_IP12 &lt;ip-address&gt; [&lt;off_status_interval&gt;] [&lt;on_status_interval&gt;]
  605. </code>
  606. <br><br>
  607. Defining a LGTV_IP12 device will schedule an internal task (interval can be set
  608. with optional parameter &lt;status_interval&gt; in seconds, if not set, the value is 30
  609. seconds), which periodically reads the status of the TV (power state, current channel, input, ...)
  610. and triggers notify/FileLog commands.
  611. <br><br>
  612. Different status update intervals depending on the power state can be given also.
  613. If two intervals are given to the define statement, the first interval statement represents the status update
  614. interval in seconds in case the device is off. The second
  615. interval statement is used when the device is on.
  616. Example:<br><br>
  617. <ul><code>
  618. define TV LGTV_IP12 192.168.0.10
  619. <br><br>
  620. # With custom status interval of 60 seconds<br>
  621. define TV LGTV_IP12 192.168.0.10 60
  622. <br><br>
  623. # With custom "off"-interval of 60 seconds and "on"-interval of 10 seconds<br>
  624. define TV LGTV_IP12 192.168.0.10 60 10
  625. </code></ul>
  626. </ul>
  627. <br><br>
  628. <a name="LGTV_IP12_set"></a>
  629. <b>Set </b>
  630. <ul>
  631. <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code>
  632. <br><br>
  633. Currently, the following commands are defined.
  634. <br><br>
  635. <ul>
  636. <li><b>channel</b> &nbsp;&nbsp;-&nbsp;&nbsp; set the current channel</li>
  637. <li><b>channelUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches to next channel</li>
  638. <li><b>channelDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; switches to previous channel</li>
  639. <li><b>removePairing</b> &nbsp;&nbsp;-&nbsp;&nbsp; deletes the pairing with the device</li>
  640. <li><b>showPairCode</b> &nbsp;&nbsp;-&nbsp;&nbsp; requests the TV to display the pair code on the TV screen. This pair code must be set in the attribute <a href="#LGTV_IP12_pairingcode">pairingcode</a></li>
  641. <li><b>startApp</b> &nbsp;&nbsp;-&nbsp;&nbsp; start a installed app on the TV</li>
  642. <li><b>stopApp</b> &nbsp;&nbsp;-&nbsp;&nbsp; stops a running app on the TV</li>
  643. <li><b>statusRequest</b> &nbsp;&nbsp;-&nbsp;&nbsp; requests the current status of the device</li>
  644. <li><b>remoteControl</b> up,down,... &nbsp;&nbsp;-&nbsp;&nbsp; sends remote control commands</li>
  645. </ul>
  646. </ul>
  647. <br><br>
  648. <a name="LGTV_IP12get"></a>
  649. <b>Get</b>
  650. <ul>
  651. <code>get &lt;name&gt; &lt;reading&gt;</code>
  652. <br><br>
  653. Currently, the get command only returns the reading values. For a specific list of possible values, see section "Generated Readings/Events".
  654. <br><br>
  655. </ul>
  656. <br><br>
  657. <a name="LGTV_IP12_attr"></a>
  658. <b>Attributes</b>
  659. <ul>
  660. <li><a href="#do_not_notify">do_not_notify</a></li>
  661. <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
  662. <li><a name="LGTV_IP12_disable">disable</a></li>
  663. Optional attribute to disable the internal cyclic status update of the TV. Manual status updates via statusRequest command is still possible.
  664. <br><br>
  665. Possible values: 0 => perform cyclic status update, 1 => don't perform cyclic status updates.<br><br>
  666. <li><a name="LGTV_IP12_disabledForIntervals">disabledForIntervals</a> HH:MM-HH:MM HH:MM-HH-MM...</li>
  667. Optional attribute to disable the internal cyclic status update of the TV during a specific time interval. The attribute contains a space separated list of HH:MM tupels.
  668. If the current time is between any of these time specifications, the cyclic update will be disabled.
  669. Instead of HH:MM you can also specify HH or HH:MM:SS.
  670. <br><br>To specify an interval spawning midnight, you have to specify two intervals, e.g.:
  671. <pre>23:00-24:00 00:00-01:00</pre>
  672. Default Value is <i>empty</i> (no intervals defined, cyclic update is always active)<br><br>
  673. <li><a name="LGTV_IP12_request-timeout">request-timeout</a></li>
  674. Optional attribute change the response timeout in seconds for all queries to the TV.
  675. <br><br>
  676. Possible values: 1-5 seconds. Default value is 4 seconds.<br><br>
  677. <li><a name="LGTV_IP12_pairingcode">pairingcode</a></li>
  678. This attribute contains the pairing code to authenticate FHEM as trusted controller. The pairing code can be displayed via <a href="#LGTV_IP12_set">set command</a> <code>showPairCode</code>
  679. </ul>
  680. <br><br>
  681. <b>Generated Readings/Events:</b><br>
  682. <ul>
  683. <li><b>3D</b> - The status of 3D playback (can be "true" or "false")</li>
  684. <li><b>channel</b> - The number of the current channel</li>
  685. <li><b>channelName</b> - The name of the current channel</li>
  686. <li><b>currentProgram</b> - The name of the running program of the current channel</li>
  687. <li><b>input</b> - The current input source (e.g. Antenna, Sattelite, HDMI1, ...)</li>
  688. <li><b>inputLabel</b> - The user defined name of the current input source</li>
  689. <li><b>mute</b> - Reports the current mute state (can be "on" or "off")</li>
  690. <li><b>power</b> - The power status (can be "on" or "off")</li>
  691. <li><b>volume</b> - Reports the volume state.</li>
  692. </ul>
  693. </ul>
  694. =end html
  695. =begin html_DE
  696. <a name="LGTV_IP12"></a>
  697. <h3>LGTV_IP12</h3>
  698. <ul>
  699. Dieses Modul steuert SmartTV's des Herstellers LG welche zwischen 2012 und 2014 produziert wurden &uuml;ber die Netzwerkschnittstelle.
  700. Es bietet die M&ouml;glichkeit den aktuellen TV Kanal zu steuern, sowie Apps zu starten, Fernbedienungsbefehle zu senden, sowie den aktuellen Status abzufragen.
  701. <br><br>
  702. Es werden alle TV Modelle unterst&uuml;tzt, welche mit der <a href="https://itunes.apple.com/de/app/lg-tv-remote/id509979485?mt=8" target="_new">LG TV Remote</a> Smartphone App steuerbar sind.
  703. <br><br>
  704. <a name="LGTV_IP12_define"></a>
  705. <b>Definition</b>
  706. <ul>
  707. <code>define &lt;name&gt; LGTV_IP12 &lt;IP-Addresse&gt; [&lt;Status_Interval&gt;]
  708. <br><br>
  709. define &lt;name&gt; LGTV_IP12 &lt;IP-Addresse&gt; [&lt;Off_Interval&gt;] [&lt;On_Interval&gt;]
  710. </code>
  711. <br><br>
  712. Bei der Definition eines LGTV_IP12-Moduls wird eine interne Routine in Gang gesetzt, welche regelm&auml;&szlig;ig
  713. (einstellbar durch den optionalen Parameter <code>&lt;Status_Interval&gt;</code>; falls nicht gesetzt ist der Standardwert 30 Sekunden)
  714. den Status des TV abfragt und entsprechende Notify-/FileLog-Definitionen triggert.
  715. <br><br>
  716. Sofern 2 Interval-Argumente &uuml;bergeben werden, wird der erste Parameter <code>&lt;Off_Interval&gt;</code> genutzt
  717. sofern der TV ausgeschaltet ist. Der zweiter Parameter <code>&lt;On_Interval&gt;</code>
  718. wird verwendet, sofern der TV eingeschaltet ist.
  719. <br><br>
  720. Beispiel:<br><br>
  721. <ul><code>
  722. define TV LGTV_IP12 192.168.0.10
  723. <br><br>
  724. # Mit modifiziertem Status Interval (60 Sekunden)<br>
  725. define TV LGTV_IP12 192.168.0.10 60
  726. <br><br>
  727. # Mit gesetztem "Off"-Interval (60 Sekunden) und "On"-Interval (10 Sekunden)<br>
  728. define TV LGTV_IP12 192.168.0.10 60 10
  729. </code></ul>
  730. </ul>
  731. <br><br>
  732. <a name="LGTV_IP12_set"></a>
  733. <b>Set-Kommandos </b>
  734. <ul>
  735. <code>set &lt;Name&gt; &lt;Kommando&gt; [&lt;Parameter&gt;]</code>
  736. <br><br>
  737. Aktuell werden folgende Kommandos unterst&uuml;tzt.
  738. <br><br>
  739. <ul>
  740. <li><b>channel</b> &lt;Nummer&gt;&nbsp;&nbsp;-&nbsp;&nbsp; w&auml;hlt den aktuellen TV-Kanal aus</li>
  741. <li><b>channelUp</b> &nbsp;&nbsp;-&nbsp;&nbsp; schaltet auf den n&auml;chsten Kanal um </li>
  742. <li><b>channelDown</b> &nbsp;&nbsp;-&nbsp;&nbsp; schaltet auf den vorherigen Kanal um </li>
  743. <li><b>removePairing</b> &nbsp;&nbsp;-&nbsp;&nbsp; l&ouml;scht das Pairing zwischen FHEM und dem TV</li>
  744. <li><b>showPairCode</b> &nbsp;&nbsp;-&nbsp;&nbsp; zeigt den Pair-Code auf dem TV-Bildschirm an. Dieser Code muss im Attribut <a href="#LGTV_IP12_pairingcode">pairingcode</a> gesetzt werden, damit FHEM mit dem TV kommunizieren kann.</li>
  745. <li><b>startApp</b> &lt;Name&gt;&nbsp;&nbsp;-&nbsp;&nbsp; startet eine installierte App</li>
  746. <li><b>stopApp</b> &lt;Name&gt;&nbsp;&nbsp;-&nbsp;&nbsp; stoppt eine laufende App</li>
  747. <li><b>statusRequest</b> &nbsp;&nbsp;-&nbsp;&nbsp; fragt den aktuellen Status ab</li>
  748. <li><b>remoteControl</b> up,down,... &nbsp;&nbsp;-&nbsp;&nbsp; sendet Fernbedienungsbefehle</li>
  749. </ul>
  750. </ul>
  751. <br><br>
  752. <a name="LGTV_IP12_get"></a>
  753. <b>Get-Kommandos</b>
  754. <ul>
  755. <code>get &lt;Name&gt; &lt;Readingname&gt;</code>
  756. <br><br>
  757. Aktuell stehen via GET lediglich die Werte der Readings zur Verf&uuml;gung. Eine genaue Auflistung aller m&ouml;glichen Readings folgen unter "Generierte Readings/Events".
  758. </ul>
  759. <br><br>
  760. <a name="LGTV_IP12_attr"></a>
  761. <b>Attribute</b>
  762. <ul>
  763. <li><a href="#do_not_notify">do_not_notify</a></li>
  764. <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
  765. <li><a name="LGTV_IP12_disable">disable</a></li>
  766. Optionales Attribut zur Deaktivierung des zyklischen Status-Updates. Ein manuelles Update via statusRequest-Befehl ist dennoch m&ouml;glich.
  767. <br><br>
  768. M&ouml;gliche Werte: 0 => zyklische Status-Updates, 1 => keine zyklischen Status-Updates.<br><br>
  769. <li><a name="LGTV_IP12_disabledForIntervals">disabledForIntervals</a> HH:MM-HH:MM HH:MM-HH-MM...</li>
  770. Optionales Attribut zur Deaktivierung der zyklischen Status-Updates innerhalb von bestimmten Zeitintervallen.
  771. Das Argument ist eine Leerzeichen-getrennte Liste von Minuszeichen-getrennten HH:MM Paaren (Stunde : Minute).
  772. Falls die aktuelle Uhrzeit zwischen diese Werte f&auml;llt, dann werden zyklische Status-Updates, wie bei <a href="#LGTV_IP12_disable">disable</a>, ausgesetzt.
  773. Statt HH:MM kann man auch HH oder HH:MM:SS angeben.<br><br>
  774. Um einen Intervall um Mitternacht zu spezifizieren, muss man zwei einzelne Intervalle angeben, z.Bsp.:
  775. <pre>23:00-24:00 00:00-01:00</pre>
  776. Standardwert ist <i>nicht gesetzt</i> (dauerhaft aktiv)<br><br>
  777. <li><a name="LGTV_IP12_request-timeout">request-timeout</a></li>
  778. Optionales Attribut. Maximale Dauer einer Anfrage in Sekunden zum TV.
  779. <br><br>
  780. M&ouml;gliche Werte: 1-5 Sekunden. Standartwert ist 4 Sekunden<br><br>
  781. <li><a name="LGTV_IP12_pairingcode">pairingcode</a></li>
  782. Dieses Attribut speichert den Pairing Code um sich gegen&uuml;ber dem TV als vertrauensw&uuml;rdigen Controller zu authentifizieren. Der Pairing-Code kann via Set-Kommando <a href="#LGTV_IP12_set">showPairCode</a> angezeigt werden.
  783. </ul>
  784. <br><br>
  785. <b>Generierte Readings/Events:</b><br>
  786. <ul>
  787. <li><b>3D</b> - Status des 3D-Wiedergabemodus ("true" =&gt; 3D Wiedergabemodus aktiv, "false" =&gt; 3D Wiedergabemodus nicht aktiv)</li>
  788. <li><b>channel</b> - Die Nummer des aktuellen TV-Kanals</li>
  789. <li><b>channelName</b> - Der Name des aktuellen TV-Kanals</li>
  790. <li><b>currentProgram</b> - Der Name der laufenden Sendung</li>
  791. <li><b>input</b> - Die aktuelle Eingangsquelle (z.B. Antenna, Sattelite, HDMI1, ...)</li>
  792. <li><b>inputLabel</b> - Die benutzerdefinierte Bezeichnung der aktuellen Eingangsquelle</li>
  793. <li><b>mute</b> on,off - Der aktuelle Stumm-Status ("on" =&gt; Stumm, "off" =&gt; Laut)</li>
  794. <li><b>power</b> on,off - Der aktuelle Power-Status ("on" =&gt; eingeschaltet, "off" =&gt; ausgeschaltet)</li>
  795. <li><b>volume</b> - Der aktuelle Lautstärkepegel.</li>
  796. </ul>
  797. </ul>
  798. =end html_DE
  799. =cut