70_BRAVIA.pm 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109
  1. # $Id: 70_BRAVIA.pm 15479 2017-11-22 21:18:34Z vuffiraa $
  2. ##############################################################################
  3. #
  4. # 70_BRAVIA.pm
  5. # An FHEM Perl module for controlling Sony Televisons
  6. # via network connection. Supported are models with release date starting from 2011.
  7. #
  8. # Copyright by Ulf von Mersewsky
  9. # e-mail: umersewsky at gmail.com
  10. #
  11. # This file is part of fhem.
  12. #
  13. # Fhem is free software: you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation, either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # Fhem is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. # You should have received a copy of the GNU General Public License
  24. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  25. #
  26. ##############################################################################
  27. package main;
  28. use 5.012;
  29. use strict;
  30. use warnings;
  31. use Data::Dumper;
  32. use Time::HiRes qw(gettimeofday);
  33. use Time::Local;
  34. use HttpUtils;
  35. use SetExtensions;
  36. use Encode;
  37. use JSON qw(decode_json);
  38. use MIME::Base64;
  39. use XML::Simple qw(:strict);
  40. use IO::Socket;
  41. sub BRAVIA_Set($@);
  42. sub BRAVIA_Get($@);
  43. sub BRAVIA_GetStatus($;$);
  44. sub BRAVIA_Define($$);
  45. sub BRAVIA_Undefine($$);
  46. #########################
  47. # Forward declaration for remotecontrol module
  48. #sub BRAVIA_RClayout_TV();
  49. #sub BRAVIA_RCmakenotify($$);
  50. ###################################
  51. sub BRAVIA_Initialize($) {
  52. my ($hash) = @_;
  53. Log3 $hash, 5, "BRAVIA_Initialize: Entering";
  54. $hash->{GetFn} = "BRAVIA_Get";
  55. $hash->{SetFn} = "BRAVIA_Set";
  56. $hash->{DefFn} = "BRAVIA_Define";
  57. $hash->{UndefFn} = "BRAVIA_Undefine";
  58. $hash->{AttrList} = "disable:0,1 macaddr:textField channelsMax:textField " . $readingFnAttributes;
  59. $data{RC_layout}{BRAVIA_SVG} = "BRAVIA_RClayout_SVG";
  60. $data{RC_layout}{BRAVIA} = "BRAVIA_RClayout";
  61. $data{RC_makenotify}{BRAVIA} = "BRAVIA_RCmakenotify";
  62. return;
  63. }
  64. #####################################
  65. sub BRAVIA_GetStatus($;$) {
  66. my ( $hash, $update ) = @_;
  67. my $name = $hash->{NAME};
  68. my $interval = $hash->{INTERVAL};
  69. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_GetStatus()";
  70. RemoveInternalTimer($hash);
  71. InternalTimer( gettimeofday() + $interval, "BRAVIA_GetStatus", $hash, 0 );
  72. return if ( AttrVal($name, "disable", 0) == 1 );
  73. # check device availability
  74. if (!$update) {
  75. BRAVIA_SendCommand( $hash, "getStatus", "xml" )
  76. if (ReadingsVal($name, "requestFormat", "xml") eq "xml");
  77. BRAVIA_SendCommand( $hash, "getStatus", "json" )
  78. if (ReadingsVal($name, "requestFormat", "json") eq "json");
  79. }
  80. return;
  81. }
  82. ###################################
  83. sub BRAVIA_Get($@) {
  84. my ( $hash, @a ) = @_;
  85. my $name = $hash->{NAME};
  86. my $what;
  87. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_Get()";
  88. return "argument is missing" if ( int(@a) < 2 );
  89. $what = $a[1];
  90. if ( $what =~ /^(power|presence|input|channel|volume|mute)$/ ) {
  91. my $value = ReadingsVal($name, $what, "");
  92. if ($value ne "") {
  93. return $value;
  94. }
  95. else {
  96. return "no such reading: $what";
  97. }
  98. }
  99. else {
  100. return
  101. "Unknown argument $what, choose one of power:noArg presence:noArg input:noArg channel:noArg volume:noArg mute:noArg";
  102. }
  103. }
  104. ###################################
  105. sub BRAVIA_Set($@) {
  106. my ( $hash, @a ) = @_;
  107. my $name = $hash->{NAME};
  108. my $power = ReadingsVal($name, "power", "");
  109. my $presence = ReadingsVal($name, "presence", "");
  110. my $channel = ReadingsVal($name, "channel", "");
  111. my $channelId = ReadingsVal($name, "channelId", "");
  112. my $channels = "";
  113. my $inputs = "";
  114. my $mutes = "toggle";
  115. if ( ReadingsVal($name, "input", "") ne "-" ) {
  116. $hash->{helper}{lastInput} = ReadingsVal($name, "input", "");
  117. } elsif ( !defined( $hash->{helper}{lastInput} ) ) {
  118. $hash->{helper}{lastInput} = "";
  119. }
  120. my $input = $hash->{helper}{lastInput};
  121. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_Set()";
  122. return "No Argument given" if ( !defined( $a[1] ) );
  123. # Input handling
  124. my @inputs;
  125. if ( defined( $hash->{helper}{device}{inputPreset} )
  126. && ref( $hash->{helper}{device}{inputPreset} ) eq "HASH" ) {
  127. @inputs = keys %{ $hash->{helper}{device}{inputPreset} };
  128. }
  129. @inputs = sort(@inputs);
  130. $inputs = join(",", @inputs);
  131. # load channel list
  132. my @channels;
  133. if ( defined( $hash->{helper}{device}{channelPreset} )
  134. && ref( $hash->{helper}{device}{channelPreset} ) eq "HASH" )
  135. {
  136. my $count = 0;
  137. my @keys = keys %{ $hash->{helper}{device}{channelPreset} };
  138. @keys = sort(@keys);
  139. my $maxChannels = (@keys < AttrVal($name, "channelsMax", 50) ? @keys : AttrVal($name, "channelsMax", 50));
  140. for (my $i = 0; $i < $maxChannels; $i++) {
  141. my $preset = $keys[$i];
  142. if ( $hash->{helper}{device}{channelPreset}{$preset}{name}
  143. && $hash->{helper}{device}{channelPreset}{$preset}{name} ne ""
  144. && $hash->{helper}{device}{channelPreset}{$preset}{name} ne "-"
  145. && $hash->{helper}{device}{channelPreset}{$preset}{id} ne "-" ) {
  146. push(
  147. @channels,
  148. $hash->{helper}{device}{channelPreset}{$preset}{id}.":".$hash->{helper}{device}{channelPreset}{$preset}{name});
  149. }
  150. }
  151. }
  152. if ( $channel ne "" && $channel ne "-" && $channelId ne "-" ) {
  153. my $currentChannel = $channelId . ":" . $channel;
  154. my @matches = grep("/".$currentChannel."/", @channels);
  155. push( @channels, $currentChannel ) if ( ( scalar @matches ) eq "0" );
  156. }
  157. @channels = sort(@channels);
  158. $channels = join(",", @channels);
  159. $mutes .= ",on,off";
  160. #$mutes .= ",off" if ( defined( $hash->{READINGS}{generation}{VAL} ) and $hash->{READINGS}{generation}{VAL} ne "1.0" );
  161. my $usage = "Unknown argument " . $a[1] . ", choose one of";
  162. $usage .= " requestFormat:json,xml register";
  163. $usage .= ":noArg"
  164. if (ReadingsVal($name, "requestFormat", "") eq "xml");
  165. $usage .= " statusRequest:noArg toggle:noArg on:noArg off:noArg tvpause:noarg play:noArg pause:noArg stop:noArg record:noArg upnp:on,off volume:slider,1,1,100 volumeUp:noArg volumeDown:noArg channelUp:noArg channelDown:noArg remoteControl";
  166. $usage .= " mute:" . $mutes;
  167. $usage .= " input:" . $inputs if ( $inputs ne "" );
  168. $usage .= " channel:$channels" if ( $channels ne "" );
  169. my $cmd = '';
  170. my $result;
  171. # statusRequest
  172. if ( lc( $a[1] ) eq "statusrequest" ) {
  173. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  174. delete $hash->{helper}{device}
  175. if ( defined( $hash->{helper}{device} ) );
  176. BRAVIA_GetStatus($hash);
  177. }
  178. # toggle
  179. elsif ( $a[1] eq "toggle" ) {
  180. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  181. if ( $power eq "off" ) {
  182. return BRAVIA_Set( $hash, $name, "on" );
  183. }
  184. else {
  185. return BRAVIA_Set( $hash, $name, "off" );
  186. }
  187. }
  188. # on
  189. elsif ( $a[1] eq "on" ) {
  190. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  191. if ( $power eq "off" ) {
  192. readingsSingleUpdate($hash, "state", "set_on", 1);
  193. my $macAddr = AttrVal( $name, "macaddr", "" );
  194. if ($macAddr eq "") {
  195. $macAddr = ReadingsVal( $name, "macAddr", "");
  196. }
  197. if ( $macAddr ne "" && $macAddr ne "-" &&
  198. ($presence eq "absent" || ReadingsVal($name, "generation", "") eq "1.0.5") ) {
  199. $result = BRAVIA_wake( $name, $macAddr );
  200. return "wake-up command sent";
  201. } else {
  202. $cmd = "POWER";
  203. BRAVIA_SendCommand( $hash, "ircc", $cmd );
  204. }
  205. }
  206. }
  207. # off
  208. elsif ( $a[1] eq "off" ) {
  209. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  210. if ( $presence eq "present" ) {
  211. readingsSingleUpdate($hash, "state", "set_off", 1);
  212. if ( ReadingsVal($name, "generation", "") ne "1.0" ) {
  213. $cmd = "STANDBY";
  214. } else {
  215. $cmd = "POWER";
  216. }
  217. BRAVIA_SendCommand( $hash, "ircc", $cmd );
  218. } else {
  219. return "Device needs to be reachable to toggle standby mode.";
  220. }
  221. }
  222. # volume
  223. elsif ( $a[1] eq "volume" ) {
  224. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  225. return "No argument given" if ( !defined( $a[2] ) );
  226. my $vol = $a[2];
  227. if ( $presence eq "present" ) {
  228. if ( $vol =~ m/^\d+$/ && $vol >= 1 && $vol <= 100 ) {
  229. $cmd = 'setVolume:' . $vol;
  230. }
  231. else {
  232. return
  233. "Argument does not seem to be a valid integer between 1 and 100";
  234. }
  235. BRAVIA_SendCommand( $hash, "upnp", $cmd );
  236. readingsSingleUpdate( $hash, "volume", $a[2], 1 )
  237. if ( ReadingsVal($name, "volume", "") ne $a[2] );
  238. }
  239. else {
  240. return "Device needs to be ON to adjust volume.";
  241. }
  242. }
  243. # volumeUp/volumeDown
  244. elsif ( lc( $a[1] ) =~ /^(volumeup|volumedown)$/ ) {
  245. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  246. if ( $presence eq "present" ) {
  247. if ( lc( $a[1] ) eq "volumeup" ) {
  248. $cmd = "VOLUP";
  249. }
  250. else {
  251. $cmd = "VOLDOWN";
  252. }
  253. BRAVIA_SendCommand( $hash, "ircc", $cmd );
  254. }
  255. else {
  256. return "Device needs to be ON to adjust volume.";
  257. }
  258. }
  259. # mute
  260. elsif ( $a[1] eq "mute" ) {
  261. if ( defined( $a[2] ) ) {
  262. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  263. }
  264. else {
  265. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  266. }
  267. if ( $presence eq "present" ) {
  268. if ( !defined( $a[2] ) || $a[2] eq "toggle" ) {
  269. $result = BRAVIA_SendCommand( $hash, "ircc", "MUTE" );
  270. readingsSingleUpdate( $hash, "mute", (ReadingsVal($name, "mute", "") eq "on" ? "off" : "on"), 1 );
  271. }
  272. elsif ( $a[2] eq "off" ) {
  273. #$result = BRAVIA_SendCommand( $hash, "MuteOff" )
  274. $result = BRAVIA_SendCommand( $hash, "upnp", "setMute:0" );
  275. readingsSingleUpdate( $hash, "mute", $a[2], 1 )
  276. if ( ReadingsVal($name, "mute", "") ne $a[2] );
  277. }
  278. elsif ( $a[2] eq "on" ) {
  279. #$result = BRAVIA_SendCommand( $hash, "MuteOn" )
  280. $result = BRAVIA_SendCommand( $hash, "upnp", "setMute:1" );
  281. readingsSingleUpdate( $hash, "mute", $a[2], 1 )
  282. if ( ReadingsVal($name, "mute", "") ne $a[2] );
  283. }
  284. else {
  285. return "Unknown argument " . $a[2];
  286. }
  287. }
  288. else {
  289. return "Device needs to be ON to mute/unmute audio.";
  290. }
  291. }
  292. # remoteControl
  293. elsif ( lc( $a[1] ) eq "remotecontrol" ) {
  294. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  295. if ( $presence eq "present" ) {
  296. if ( !defined( $a[2] ) ) {
  297. my $commandKeys = "";
  298. for (
  299. sort keys %{
  300. BRAVIA_GetRemotecontrolCommand(
  301. "GetRemotecontrolCommands")
  302. }
  303. )
  304. {
  305. $commandKeys = $commandKeys . " " . $_;
  306. }
  307. return "No argument given, choose one of" . $commandKeys;
  308. }
  309. $cmd = uc( $a[2] );
  310. if ( $cmd eq "MUTE" ) {
  311. BRAVIA_Set( $hash, $name, "mute" );
  312. }
  313. elsif ( $cmd eq "CHANUP" ) {
  314. BRAVIA_Set( $hash, $name, "channelUp" );
  315. }
  316. elsif ( $cmd eq "CHANDOWN" ) {
  317. BRAVIA_Set( $hash, $name, "channelDown" );
  318. }
  319. elsif ( $cmd ne "" ) {
  320. BRAVIA_SendCommand( $hash, "ircc", $cmd );
  321. }
  322. else {
  323. my $commandKeys = "";
  324. for (
  325. sort keys %{
  326. BRAVIA_GetRemotecontrolCommand(
  327. "GetRemotecontrolCommands")
  328. }
  329. )
  330. {
  331. $commandKeys = $commandKeys . " " . $_;
  332. }
  333. return
  334. "Unknown argument "
  335. . $a[2]
  336. . ", choose one of"
  337. . $commandKeys;
  338. }
  339. }
  340. else {
  341. return "Device needs to be reachable to be controlled remotely.";
  342. }
  343. }
  344. # channel
  345. elsif ( $a[1] eq "channel" ) {
  346. if (defined($a[2]) && $presence eq "present" && $power ne "on" ) {
  347. Log3 $name, 4, "BRAVIA $name: indirect switching request to ON";
  348. BRAVIA_Set( $hash, $name, "on" );
  349. }
  350. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  351. return
  352. "No argument given, choose one of channel presetNumber channelName "
  353. if ( !defined( $a[2] ) );
  354. if ( $presence eq "present" ) {
  355. my $channelName = $a[2];
  356. if ( $channelName =~ /^(\d)(\d?)(\d?)(\d?):.*$/ ) {
  357. BRAVIA_SendCommand( $hash, "ircc", $1, "blocking" );
  358. BRAVIA_SendCommand( $hash, "ircc", $2, "blocking" ) if (defined($2));
  359. BRAVIA_SendCommand( $hash, "ircc", $3, "blocking" ) if (defined($3));
  360. BRAVIA_SendCommand( $hash, "ircc", $4, "blocking" ) if (defined($4));
  361. } else {
  362. return "Argument " . $channelName . " is not a valid channel name";
  363. }
  364. }
  365. else {
  366. return
  367. "Device needs to be reachable to switch to a specific channel.";
  368. }
  369. }
  370. # channelUp/channelDown
  371. elsif ( lc( $a[1] ) =~ /^(channelup|channeldown)$/ ) {
  372. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  373. if ( $presence eq "present" ) {
  374. if ( lc( $a[1] ) eq "channelup" ) {
  375. $cmd = "CHANUP";
  376. }
  377. else {
  378. $cmd = "CHANDOWN";
  379. }
  380. BRAVIA_SendCommand( $hash, "ircc", $cmd );
  381. }
  382. else {
  383. return "Device needs to be ON to switch channel.";
  384. }
  385. }
  386. # input
  387. elsif ( $a[1] eq "input" ) {
  388. if (defined($a[2]) && $presence eq "present" && $power ne "on" ) {
  389. Log3 $name, 4, "BRAVIA $name: indirect switching request to ON";
  390. BRAVIA_Set( $hash, $name, "on" );
  391. }
  392. return "No 2nd argument given" if ( !defined( $a[2] ) );
  393. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  394. # Resolve input uri
  395. my $input_uri;
  396. if ( defined( $hash->{helper}{device}{inputPreset}{ $a[2] } ) ) {
  397. $input_uri = $hash->{helper}{device}{inputPreset}{ $a[2] }{uri};
  398. } else {
  399. return "Unknown source input '" . $a[2] . "' on that device.";
  400. }
  401. if ( $presence eq "present" ) {
  402. BRAVIA_SendCommand( $hash, "setPlayContent", $input_uri );
  403. if ( ReadingsVal($name, "input", "") ne $a[2] ) {
  404. readingsSingleUpdate( $hash, "input", $a[2], 1 );
  405. }
  406. }
  407. else {
  408. return "Device needs to be reachable to switch input.";
  409. }
  410. }
  411. # tvpause
  412. elsif ( $a[1] eq "tvpause" ) {
  413. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  414. if ( $power eq "on" ) {
  415. BRAVIA_SendCommand( $hash, "ircc", "TVPAUSE" );
  416. }
  417. else {
  418. return "Device needs to be ON to pause tv.";
  419. }
  420. }
  421. # pause
  422. elsif ( $a[1] eq "pause" ) {
  423. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  424. if ( $power eq "on" ) {
  425. BRAVIA_SendCommand( $hash, "ircc", "PAUSE" );
  426. }
  427. else {
  428. return "Device needs to be ON to pause video.";
  429. }
  430. }
  431. # play
  432. elsif ( $a[1] eq "play" ) {
  433. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  434. if ( $power eq "on" ) {
  435. BRAVIA_SendCommand( $hash, "ircc", "PLAY" );
  436. }
  437. else {
  438. return "Device needs to be ON to play video.";
  439. }
  440. }
  441. # stop
  442. elsif ( $a[1] eq "stop" ) {
  443. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  444. if ( $power eq "on" ) {
  445. BRAVIA_SendCommand( $hash, "ircc", "STOP" );
  446. }
  447. else {
  448. return "Device needs to be ON to stop video.";
  449. }
  450. }
  451. # record
  452. elsif ( $a[1] eq "record" ) {
  453. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  454. if ( $power eq "on" ) {
  455. BRAVIA_SendCommand( $hash, "ircc", "RECORD" );
  456. }
  457. else {
  458. return "Device needs to be ON to start instant recording.";
  459. }
  460. }
  461. # register
  462. elsif ( $a[1] eq "register" ) {
  463. if (defined($a[2])) {
  464. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  465. BRAVIA_SendCommand( $hash, "register", $a[2] );
  466. } else {
  467. Log3 $name, 2, "BRAVIA set $name " . $a[1];
  468. BRAVIA_SendCommand( $hash, "register" );
  469. }
  470. }
  471. # requestFormat
  472. elsif ( $a[1] eq "requestFormat" ) {
  473. return "No 2nd argument given" if ( !defined( $a[2] ) );
  474. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  475. readingsSingleUpdate( $hash, "requestFormat", $a[2], 1 )
  476. if ( ReadingsVal($name, "requestFormat", "") ne $a[2] );
  477. }
  478. # upnp
  479. elsif ( $a[1] eq "upnp" ) {
  480. return "No 2nd argument given" if ( !defined( $a[2] ) );
  481. Log3 $name, 2, "BRAVIA set $name " . $a[1] . " " . $a[2];
  482. readingsSingleUpdate( $hash, "upnp", $a[2], 1 )
  483. if ( ReadingsVal($name, "upnp", "") ne $a[2] );
  484. }
  485. # return usage hint
  486. else {
  487. return $usage;
  488. }
  489. return;
  490. }
  491. ###################################
  492. sub BRAVIA_Define($$) {
  493. my ( $hash, $def ) = @_;
  494. my @a = split( "[ \t][ \t]*", $def );
  495. my $name = $hash->{NAME};
  496. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_Define()";
  497. if ( int(@a) < 3 ) {
  498. my $msg =
  499. "Wrong syntax: define <name> BRAVIA <ip-or-hostname> [<poll-interval>]";
  500. Log3 $name, 4, $msg;
  501. return $msg;
  502. }
  503. $hash->{TYPE} = "BRAVIA";
  504. my $address = $a[2];
  505. $hash->{helper}{ADDRESS} = $address;
  506. # use interval of 45 sec if not defined
  507. my $interval = $a[3] || 45;
  508. $hash->{INTERVAL} = $interval;
  509. # number of channels read from channellist, maximum 50
  510. my $channelCount = 50;
  511. $hash->{CHANNELCOUNT} = $channelCount;
  512. $hash->{helper}{PORT} = {
  513. 'IRCC' => "80",
  514. 'SERVICE' => "80",
  515. 'UPNP' => "52323",
  516. };
  517. $hash->{helper}{HEADER} = 'X-CERS-DEVICE-ID: fhem_remote';
  518. $hash->{name} = ReadingsVal($name, "name", "");
  519. $hash->{model} = ReadingsVal($name, "model", "");
  520. $hash->{generation} = ReadingsVal($name, "generation", "");
  521. unless ( defined( AttrVal( $name, "webCmd", undef ) ) ) {
  522. $attr{$name}{webCmd} = 'volume:channelUp:channelDown';
  523. }
  524. unless ( defined( AttrVal( $name, "devStateIcon", undef ) ) ) {
  525. $attr{$name}{devStateIcon} =
  526. 'on:rc_GREEN:off off:rc_YELLOW:on absent:rc_STOP:on';
  527. }
  528. unless ( defined( AttrVal( $name, "icon", undef ) ) ) {
  529. $attr{$name}{icon} = 'it_television';
  530. }
  531. # start the status update timer
  532. RemoveInternalTimer($hash);
  533. InternalTimer( gettimeofday() + 2, "BRAVIA_GetStatus", $hash, 1 );
  534. return;
  535. }
  536. ############################################################################################################
  537. #
  538. # Begin of helper functions
  539. #
  540. ############################################################################################################
  541. ###################################
  542. sub BRAVIA_SendCommand($$;$$) {
  543. my ( $hash, $service, $cmd, $type ) = @_;
  544. my $name = $hash->{NAME};
  545. my $address = $hash->{helper}{ADDRESS};
  546. my $port = $hash->{helper}{PORT};
  547. my $header = $hash->{helper}{HEADER};
  548. my $timestamp = gettimeofday();
  549. my $data;
  550. my $timeout;
  551. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_SendCommand()";
  552. my $URL;
  553. my $response;
  554. my $return;
  555. my $requestFormat = ReadingsVal($name, "requestFormat", "");
  556. BRAVIA_CheckRegistration($hash) if ($service ne "register" && $service ne "getStatus");
  557. if ( !defined($cmd) ) {
  558. Log3 $name, 4, "BRAVIA $name: REQ $service";
  559. }
  560. else {
  561. Log3 $name, 4, "BRAVIA $name: REQ $service/" . urlDecode($cmd);
  562. }
  563. $URL = "http://" . $address . ":";
  564. $header .= "\r\nCookie: auth=".ReadingsVal($name, "authCookie", "")
  565. if (ReadingsVal($name, "authCookie", "") ne "");
  566. if ($service eq "ircc") {
  567. $URL .= $port->{IRCC};
  568. $URL .= "/sony"
  569. if ($requestFormat eq "json");
  570. $URL .= "/IRCC";
  571. $header .= "\r\nSoapaction: \"urn:schemas-sony-com:service:IRCC:1#X_SendIRCC\"";
  572. $header .= "\r\nContent-Type: text/xml; charset=UTF-8";
  573. $cmd = BRAVIA_GetRemotecontrolCommand($cmd);
  574. $data = BRAVIA_GetIrccRequest($cmd);
  575. } elsif ($service eq "upnp") {
  576. my $value;
  577. if ($cmd =~ m/^(.+):(\d+)$/) {
  578. $cmd = $1;
  579. $value = $2;
  580. }
  581. $URL .= $port->{UPNP};
  582. $URL .= "/upnp/control/RenderingControl";
  583. $header .= "\r\nSoapaction: \"urn:schemas-upnp-org:service:RenderingControl:1#";
  584. $header .= ucfirst($cmd);
  585. $header .= "\"";
  586. $header .= "\r\nContent-Type: text/xml";
  587. $data = BRAVIA_GetUpnpRequest($cmd, $value);
  588. } elsif ($service eq "register") {
  589. my $id = "Fhem Remote";
  590. my $device = "fhem_remote";
  591. $URL .= $port->{SERVICE};
  592. if ($requestFormat eq "json") {
  593. my $uuid = ReadingsVal($name, "registrationUUID", "");
  594. if (defined($cmd) && $uuid ne "") {
  595. if ($cmd ne "renew") {
  596. $header = "Authorization: Basic ";
  597. $header .= encode_base64(":".$cmd,"");
  598. }
  599. } else {
  600. undef $header;
  601. $uuid = createUniqueId();
  602. readingsSingleUpdate($hash, "registrationUUID", $uuid, 1);
  603. }
  604. $URL .= "/sony/accessControl";
  605. $data = "{\"method\":\"actRegister\",\"params\":[{";
  606. $data .= "\"clientid\":\"".$id.":".$uuid."\",";
  607. $data .= "\"nickname\":\"".$id." (".$device.")\",";
  608. $data .= "\"level\":\"private\"},";
  609. $data .= "[{\"value\":\"yes\",\"function\":\"WOL\"}]],\"id\":8,\"version\":\"1.0\"}";
  610. } else {
  611. $URL .= "/cers/api/register?name=".urlEncode($id)."&registrAtionType=initial&deviceId=".$device;
  612. }
  613. } elsif ($service eq "getStatus") {
  614. $URL .= $port->{SERVICE};
  615. if ($cmd eq "xml") {
  616. $URL .= "/cers/api/" . $service;
  617. } else {
  618. $URL .= "/sony/system";
  619. $data = "{\"method\":\"getPowerStatus\",\"params\":[],\"id\":1,\"version\":\"1.0\"}";
  620. }
  621. } elsif ($service eq "getContentInformation") {
  622. $URL .= $port->{SERVICE};
  623. if ($requestFormat eq "json") {
  624. $URL .= "/sony/avContent";
  625. $data = "{\"method\":\"getPlayingContentInfo\",\"params\":[],\"id\":1,\"version\":\"1.0\"}";
  626. } else {
  627. $URL .= "/cersEx/api/" . $service;
  628. }
  629. } elsif ($service eq "getContentCount") {
  630. $URL .= $port->{SERVICE};
  631. if ($requestFormat eq "json") {
  632. $URL .= "/sony/avContent";
  633. $data = "{\"method\":\"getContentCount\",\"params\":[{\"source\":\"" . $cmd . "\",\"type\":\"\"}],\"id\":1,\"version\":\"1.0\"}";
  634. }
  635. } elsif ($service eq "getContentList") {
  636. $URL .= $port->{SERVICE};
  637. if ($requestFormat eq "json") {
  638. my $source = $cmd;
  639. my $index = 0;
  640. if ($cmd =~ /^(.*)\|(\d+)$/){
  641. $source = $1;
  642. $index = $2;
  643. }
  644. $URL .= "/sony/avContent";
  645. $data = "{\"method\":\"getContentList\",\"params\":[{\"source\":\"".$source."\",\"type\":\"\",\"cnt\":".InternalVal($name, "CHANNELCOUNT", 50).",\"stIdx\":".$index."}],\"id\":1,\"version\":\"1.0\"}";
  646. }
  647. } elsif ($service eq "getCurrentExternalInputsStatus") {
  648. $URL .= $port->{SERVICE};
  649. if ($requestFormat eq "json") {
  650. $URL .= "/sony/avContent";
  651. $data = "{\"id\":2,\"method\":\"getCurrentExternalInputsStatus\",\"version\":\"1.0\",\"params\":[]}";
  652. }
  653. } elsif ($service eq "setPlayContent") {
  654. $URL .= $port->{SERVICE};
  655. if ($requestFormat eq "json") {
  656. $URL .= "/sony/avContent";
  657. $data = "{\"id\":2,\"method\":\"setPlayContent\",\"version\":\"1.0\",\"params\":[{\"uri\":\"".$cmd."\"}]}";
  658. }
  659. } elsif ($service eq "setPlayTvContent") {
  660. $URL .= $port->{SERVICE};
  661. if ($requestFormat eq "json") {
  662. $URL .= "/sony/avContent";
  663. $data = "{\"id\":2,\"method\":\"setPlayTvContent\",\"version\":\"1.0\",\"params\":[{\"channel\":\"".$cmd."\"}]}";
  664. }
  665. } elsif ($service eq "getScheduleList") {
  666. $URL .= $port->{SERVICE};
  667. if ($requestFormat eq "json") {
  668. $URL .= "/sony/recording";
  669. $data = "{\"method\":\"getScheduleList\",\"params\":[{\"cnt\":100,\"stIdx\":0}],\"id\":1,\"version\":\"1.0\"}";
  670. } else {
  671. $URL .= "/cersEx/api/" . $service;
  672. }
  673. } else {
  674. $URL .= $port->{SERVICE};
  675. if ($requestFormat eq "json") {
  676. $URL .= "/sony/system";
  677. $data = "{\"method\":\"".$service."\",\"params\":[],\"id\":1,\"version\":\"1.0\"}";
  678. } else {
  679. $URL .= "/cers";
  680. if ($service =~ /^Mute.*$/) {
  681. $URL .= "/command/".$service;
  682. } else {
  683. $URL .= "/api/" . $service;
  684. }
  685. }
  686. }
  687. if ( defined( $attr{$name}{timeout} ) && $attr{$name}{timeout} =~ /^\d+$/ ) {
  688. $timeout = $attr{$name}{timeout};
  689. } elsif ( $service eq "getStatus" ) {
  690. $timeout = 10;
  691. } else {
  692. $timeout = 30;
  693. }
  694. # send request via HTTP-POST method
  695. Log3 $name, 5, "BRAVIA $name: POST " . $URL . " (" . urlDecode($data) . ")"
  696. if ( defined($data) );
  697. Log3 $name, 5, "BRAVIA $name: GET " . $URL
  698. if ( !defined($data) );
  699. Log3 $name, 5, "BRAVIA $name: header " . $header
  700. if ( defined($header) );
  701. if ( defined($type) && $type eq "blocking" ) {
  702. my ($err, $data) = HttpUtils_BlockingGet(
  703. {
  704. url => $URL,
  705. timeout => 4,
  706. noshutdown => 1,
  707. header => $header,
  708. data => $data,
  709. hash => $hash,
  710. service => $service,
  711. cmd => $cmd,
  712. type => $type,
  713. timestamp => $timestamp,
  714. }
  715. );
  716. Log3 $name, 5, "BRAVIA $name: REQ $service received err: $err data: $data ";
  717. sleep 1;
  718. } else {
  719. HttpUtils_NonblockingGet(
  720. {
  721. url => $URL,
  722. timeout => $timeout,
  723. noshutdown => 1,
  724. header => $header,
  725. data => $data,
  726. hash => $hash,
  727. service => $service,
  728. cmd => $cmd,
  729. type => $type,
  730. timestamp => $timestamp,
  731. callback => \&BRAVIA_ReceiveCommand,
  732. }
  733. );
  734. }
  735. return;
  736. }
  737. ###################################
  738. sub BRAVIA_ReceiveCommand($$$) {
  739. my ( $param, $err, $data ) = @_;
  740. my $hash = $param->{hash};
  741. my $name = $hash->{NAME};
  742. my $service = $param->{service};
  743. my $cmd = $param->{cmd};
  744. my $newstate;
  745. my $rc = ( $param->{buf} ) ? $param->{buf} : $param;
  746. my $return;
  747. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_ReceiveCommand() rc: $rc err: $err data: $data ";
  748. # device not reachable
  749. if ($err) {
  750. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  751. Log3 $name, 4, "BRAVIA $name: RCV TIMEOUT $service";
  752. }
  753. else {
  754. Log3 $name, 4,
  755. "BRAVIA $name: RCV TIMEOUT $service/" . urlDecode($cmd);
  756. }
  757. # device is not reachable or
  758. # does not even support master command for status
  759. if ( $service eq "getStatus" ) {
  760. BRAVIA_ClearContentInformation($hash);
  761. $newstate = "absent";
  762. if (
  763. ( !defined( $hash->{helper}{AVAILABLE} ) )
  764. or ( defined( $hash->{helper}{AVAILABLE} )
  765. and $hash->{helper}{AVAILABLE} eq 1 )
  766. )
  767. {
  768. $hash->{helper}{AVAILABLE} = 0;
  769. readingsSingleUpdate( $hash, "presence", "absent", 1 );
  770. }
  771. }
  772. }
  773. # data received
  774. elsif ($data) {
  775. if (
  776. ( !defined( $hash->{helper}{AVAILABLE} ) )
  777. or ( defined( $hash->{helper}{AVAILABLE} )
  778. and $hash->{helper}{AVAILABLE} eq 0 )
  779. )
  780. {
  781. $hash->{helper}{AVAILABLE} = 1;
  782. readingsSingleUpdate( $hash, "presence", "present", 1 );
  783. }
  784. if ( !defined($cmd) ) {
  785. Log3 $name, 4, "BRAVIA $name: RCV $service";
  786. }
  787. else {
  788. Log3 $name, 4, "BRAVIA $name: RCV $service/" . urlDecode($cmd);
  789. }
  790. if ( $data ne "" ) {
  791. if ( $data =~ /^<\?xml/ ) {
  792. my $parser = XML::Simple->new(
  793. NormaliseSpace => 2,
  794. KeepRoot => 0,
  795. ForceArray => 0,
  796. SuppressEmpty => 1
  797. );
  798. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  799. Log3 $name, 4, "BRAVIA $name: RES $service - $data";
  800. }
  801. else {
  802. Log3 $name, 4,
  803. "BRAVIA $name: RES $service/" . urlDecode($cmd) . " - $data";
  804. }
  805. readingsSingleUpdate( $hash, "requestFormat", "xml", 1 )
  806. if ( $service eq "getStatus" && ReadingsVal($name , "requestFormat", "") eq "" );
  807. $return = $parser->XMLin( encode_utf8($data), KeyAttr => [ ] );
  808. }
  809. elsif ( $data =~ /^{/ || $data =~ /^\[/ ) {
  810. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  811. Log3 $name, 4, "BRAVIA $name: RES $service - $data";
  812. }
  813. else {
  814. Log3 $name, 4,
  815. "BRAVIA $name: RES $service/" . urlDecode($cmd) . " - $data";
  816. }
  817. readingsSingleUpdate( $hash, "requestFormat", "json", 1 )
  818. if ( $service eq "getStatus" && ReadingsVal($name , "requestFormat", "") eq "" );
  819. $return = decode_json( encode_utf8($data) );
  820. }
  821. elsif ( $data eq "<html><head><title>not found</title></head><body>not found</body></html>" ) {
  822. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  823. Log3 $name, 4, "BRAVIA $name: RES $service - not found";
  824. }
  825. else {
  826. Log3 $name, 4,
  827. "BRAVIA $name: RES $service/" . urlDecode($cmd) . " - not found";
  828. }
  829. $return = "not found";
  830. }
  831. elsif ( $data =~ /^<s:Envelope/ ) {
  832. if ( !defined($cmd) ) {
  833. Log3 $name, 4, "BRAVIA $name: RES $service - response";
  834. }
  835. else {
  836. Log3 $name, 4,
  837. "BRAVIA $name: RES $service/" . urlDecode($cmd) . " - response";
  838. }
  839. $return = "ok";
  840. }
  841. else {
  842. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  843. Log3 $name, 5, "BRAVIA $name: RES ERROR $service\n" . $data;
  844. }
  845. else {
  846. Log3 $name, 5,
  847. "BRAVIA $name: RES ERROR $service/"
  848. . urlDecode($cmd) . "\n"
  849. . $data;
  850. }
  851. return undef;
  852. }
  853. }
  854. $newstate = BRAVIA_ProcessCommandData( $param, $return );
  855. }
  856. if ( defined( $newstate ) ) {
  857. readingsBeginUpdate($hash);
  858. # Set reading for power
  859. #
  860. my $readingPower = "off";
  861. if ( $newstate eq "on" ) {
  862. $readingPower = "on";
  863. }
  864. if ( ReadingsVal($name, "power", "") ne $readingPower )
  865. {
  866. readingsBulkUpdate( $hash, "power", $readingPower );
  867. }
  868. # Set reading for state
  869. #
  870. my $currentState = ReadingsVal($name, "state", "");
  871. if ( ( $currentState !~ /set_.*/ and $currentState ne $newstate )
  872. or $currentState eq "set_".$newstate
  873. or ($currentState =~ /set_.*/ and ReadingsAge($name, "state", 0) > 60) )
  874. {
  875. readingsBulkUpdate( $hash, "state", $newstate );
  876. }
  877. # Set BRAVIA online-only readings to "-"
  878. # in case box is not reachable
  879. if ( $newstate eq "absent"
  880. || $newstate eq "undefined" )
  881. {
  882. foreach ( 'input', ) {
  883. if ( ReadingsVal($name, $_, "-") ne "-" ) {
  884. readingsBulkUpdate( $hash, $_, "-" );
  885. }
  886. }
  887. }
  888. readingsEndUpdate( $hash, 1 );
  889. }
  890. return;
  891. }
  892. ###################################
  893. sub BRAVIA_Undefine($$) {
  894. my ( $hash, $arg ) = @_;
  895. my $name = $hash->{NAME};
  896. Log3 $name, 5, "BRAVIA $name: called function BRAVIA_Undefine()";
  897. # Stop the internal GetStatus-Loop and exit
  898. RemoveInternalTimer($hash);
  899. return;
  900. }
  901. ###################################
  902. sub BRAVIA_wake ($$) {
  903. my ( $name, $mac_addr ) = @_;
  904. my $address = '255.255.255.255';
  905. my $port = 9;
  906. my $sock = new IO::Socket::INET( Proto => 'udp' )
  907. or die "socket : $!";
  908. die "Can't create WOL socket" if ( !$sock );
  909. my $ip_addr = inet_aton($address);
  910. my $sock_addr = sockaddr_in( $port, $ip_addr );
  911. $mac_addr =~ s/://g;
  912. my $packet =
  913. pack( 'C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16 );
  914. setsockopt( $sock, SOL_SOCKET, SO_BROADCAST, 1 )
  915. or die "setsockopt : $!";
  916. Log3 $name, 4,
  917. "BRAVIA $name: Waking up by sending Wake-On-Lan magic package to "
  918. . $mac_addr;
  919. send( $sock, $packet, 0, $sock_addr ) or die "send : $!";
  920. close($sock);
  921. return;
  922. }
  923. ###################################
  924. # process return data
  925. sub BRAVIA_ProcessCommandData ($$) {
  926. my ($param, $return) = @_;
  927. my $hash = $param->{hash};
  928. my $name = $hash->{NAME};
  929. my $service = $param->{service};
  930. my $cmd = $param->{cmd};
  931. my $type = ( $param->{type} ) ? $param->{type} : "";
  932. my $header = $param->{httpheader};
  933. my $newstate;
  934. # ircc
  935. if ( $service eq "ircc" ) {
  936. if ( ref($return) ne "HASH" && $return eq "ok" ) {
  937. # toggle standby
  938. if ( defined($type) && $type eq "off" ) {
  939. $newstate = "off";
  940. }
  941. # toggle standby
  942. elsif ( defined($type) && $type eq "on" ) {
  943. $newstate = "on";
  944. }
  945. }
  946. }
  947. # upnp
  948. elsif ( $service eq "upnp" ) {
  949. if ( ref($return) eq "HASH" ) {
  950. if ( $cmd eq "getVolume" ) {
  951. my $volume = $return->{"s:Body"}{"u:GetVolumeResponse"}{CurrentVolume};
  952. if ( defined( $volume ) ) {
  953. readingsSingleUpdate( $hash, "volume", $volume, 1 )
  954. if (ReadingsVal($name, "volume", "-1") ne $volume);
  955. }
  956. } elsif ( $cmd eq "getMute" ) {
  957. my $mute = $return->{"s:Body"}{"u:GetMuteResponse"}{CurrentMute} eq "0" ? "off" : "on";
  958. if ( defined( $mute ) ) {
  959. readingsSingleUpdate( $hash, "mute", $mute, 1 )
  960. if (ReadingsVal($name, "mute", "-1") ne $mute);
  961. }
  962. }
  963. }
  964. }
  965. # getStatus
  966. elsif ( $service eq "getStatus" ) {
  967. my $input = "-";
  968. my $setInput;
  969. my %statusKeys;
  970. foreach ( keys %{ $hash->{READINGS} } ) {
  971. $statusKeys{$_} = 1 if ( $_ =~ /^s_.*/ && ReadingsVal($name, $_, "") ne "-" );
  972. }
  973. readingsBeginUpdate($hash);
  974. if ( ref($return) eq "HASH" ) {
  975. if ( ref($return->{status}{statusItem}) eq "ARRAY" ) {
  976. foreach ( @{ $return->{status}{statusItem} } ) {
  977. if ( $_->{field} eq "source" ) {
  978. $input = $_->{value};
  979. $setInput = "true";
  980. } else {
  981. readingsBulkUpdate( $hash, "s_".$_->{field}, $_->{value} )
  982. if (ReadingsVal($name, "s_".$_->{field}, "") ne $_->{value} );
  983. }
  984. delete $statusKeys{"s_".$_->{field}};
  985. }
  986. } elsif (defined($return->{status}{statusItem}{field})) {
  987. my $field = "s_".$return->{status}{statusItem}{field};
  988. if ( defined($field) && $field ne "" ) {
  989. if ( $field eq "s_source" ) {
  990. $input = $return->{status}{statusItem}{value};
  991. $setInput = "true";
  992. } else {
  993. readingsBulkUpdate( $hash, $field, $return->{status}{statusItem}{value} )
  994. if (ReadingsVal($name, $field, "") ne $return->{status}{statusItem}{value} );
  995. }
  996. delete $statusKeys{$field};
  997. }
  998. }
  999. }
  1000. readingsBulkUpdate( $hash, "input", $input )
  1001. if ( defined($setInput) and
  1002. (ReadingsVal($name, "input", "") ne $input) );
  1003. #remove outdated content information - replaces by "-"
  1004. foreach ( keys %statusKeys ) {
  1005. readingsBulkUpdate( $hash, $_, "-" );
  1006. }
  1007. readingsEndUpdate( $hash, 1 );
  1008. # check for valid status
  1009. if (ref $return eq ref {} && ref($return->{error}) eq "ARRAY" && $return->{error}[0] eq "404") {
  1010. BRAVIA_ClearContentInformation($hash);
  1011. return "off";
  1012. }
  1013. # fetch other info
  1014. # read system information if not existing
  1015. BRAVIA_SendCommand( $hash, "getSystemInformation" )
  1016. if ( ReadingsVal($name, "name", "0") eq "0" || ReadingsVal($name, "model", "0") eq "0" );
  1017. # read content information
  1018. if ( ReadingsVal($name, "generation", "1.0") ne "1.0" ) {
  1019. if (ref $return eq ref {} && ref($return->{result}) eq "ARRAY" && $return->{result}[0]{status} ne "active") {
  1020. # current status is not active, don't need to fetch content information
  1021. BRAVIA_ClearContentInformation($hash);
  1022. $newstate = "off";
  1023. } else {
  1024. BRAVIA_SendCommand( $hash, "getContentInformation" );
  1025. }
  1026. } elsif (ref $return eq ref {}) {
  1027. if (ref($return->{result}) eq "ARRAY") {
  1028. $newstate = ( $return->{result}[0]{status} eq "active" ? "on" : $return->{result}[0]{status} );
  1029. } else {
  1030. $newstate = ( $return->{status}{name} eq "viewing" ? "on" : $return->{status}{name} );
  1031. }
  1032. # get current system settings
  1033. if ($newstate eq "on" && ReadingsVal($name, "upnp", "on") eq "on") {
  1034. BRAVIA_SendCommand( $hash, "upnp", "getVolume" );
  1035. BRAVIA_SendCommand( $hash, "upnp", "getMute" );
  1036. }
  1037. }
  1038. }
  1039. # getSystemInformation
  1040. elsif ( $service eq "getSystemInformation" ) {
  1041. if ( ref($return) eq "HASH" ) {
  1042. readingsBeginUpdate($hash);
  1043. if (ref($return->{result}) eq "ARRAY") {
  1044. my $sysInfo = $return->{result}[0];
  1045. readingsBulkUpdate( $hash, "name", $sysInfo->{name} );
  1046. readingsBulkUpdate( $hash, "generation", $sysInfo->{generation} );
  1047. readingsBulkUpdate( $hash, "area", $sysInfo->{area} );
  1048. readingsBulkUpdate( $hash, "language", $sysInfo->{language} );
  1049. readingsBulkUpdate( $hash, "country", $sysInfo->{region} );
  1050. readingsBulkUpdate( $hash, "model", $sysInfo->{model} );
  1051. readingsBulkUpdate( $hash, "macAddr", $sysInfo->{macAddr} );
  1052. $hash->{name} = $sysInfo->{name};
  1053. $hash->{model} = $sysInfo->{model};
  1054. $hash->{generation} = $sysInfo->{generation};
  1055. } else {
  1056. readingsBulkUpdate( $hash, "name", $return->{name} );
  1057. readingsBulkUpdate( $hash, "generation", $return->{generation} );
  1058. readingsBulkUpdate( $hash, "area", $return->{area} );
  1059. readingsBulkUpdate( $hash, "language", $return->{language} );
  1060. readingsBulkUpdate( $hash, "country", $return->{country} );
  1061. readingsBulkUpdate( $hash, "model", $return->{modelName} );
  1062. $hash->{name} = $return->{name};
  1063. $hash->{model} = $return->{modelName};
  1064. $hash->{generation} = $return->{generation};
  1065. }
  1066. readingsEndUpdate( $hash, 1 );
  1067. }
  1068. }
  1069. # getContentInformation
  1070. elsif ( $service eq "getContentInformation" ) {
  1071. my %contentKeys;
  1072. my $channelName = "-";
  1073. my $channelNo = "-";
  1074. my $currentTitle = "-";
  1075. my $currentMedia = "-";
  1076. foreach ( keys %{ $hash->{READINGS} } ) {
  1077. $contentKeys{$_} = 1
  1078. if ( $_ =~ /^ci_.*/ and ReadingsVal($name, $_, "") ne "-" );
  1079. }
  1080. if ( ref($return) eq "HASH" ) {
  1081. $newstate = "on";
  1082. if ( defined($return->{infoItem}) ) {
  1083. # xml
  1084. if ( ref($return->{infoItem}) eq "ARRAY" ) {
  1085. readingsBeginUpdate($hash);
  1086. foreach ( @{ $return->{infoItem} } ) {
  1087. if ( $_->{field} eq "displayNumber" ) {
  1088. $channelNo = $_->{value};
  1089. } elsif ( $_->{field} eq "inputType" ) {
  1090. $currentMedia = $_->{value};
  1091. } elsif ( $_->{field} eq "serviceName" ) {
  1092. $channelName = BRAVIA_GetNormalizedName($_->{value});
  1093. } elsif ( $_->{field} eq "title" ) {
  1094. $currentTitle = $_->{value};
  1095. } else {
  1096. readingsBulkUpdate( $hash, "ci_".$_->{field}, $_->{value} )
  1097. if ( ReadingsVal($name, "ci_".$_->{field}, "") ne $_->{value} );
  1098. delete $contentKeys{"ci_".$_->{field}};
  1099. }
  1100. }
  1101. readingsEndUpdate( $hash, 1 );
  1102. } else {
  1103. my $field = "ci_".$return->{infoItem}->{field};
  1104. my $value = $return->{infoItem}->{value};
  1105. readingsSingleUpdate( $hash, $field, $value, 1 )
  1106. if ( ReadingsVal($name, $field, "") ne $value );
  1107. delete $contentKeys{$field};
  1108. }
  1109. } else {
  1110. # json
  1111. if ( ref($return->{result}[0]) eq "HASH" ) {
  1112. readingsBeginUpdate($hash);
  1113. foreach ( keys %{$return->{result}[0]} ) {
  1114. if ( $_ eq "dispNum" ) {
  1115. $channelNo = $return->{result}[0]{$_};
  1116. } elsif ( $_ eq "programMediaType" ) {
  1117. $currentMedia = $return->{result}[0]{$_};
  1118. } elsif ( $_ eq "title" ) {
  1119. $channelName = BRAVIA_GetNormalizedName($return->{result}[0]{$_});
  1120. } elsif ( $_ eq "programTitle" ) {
  1121. $currentTitle = $return->{result}[0]{$_};
  1122. } elsif ( $_ eq "source" ) {
  1123. readingsBulkUpdate( $hash, "input", $return->{result}[0]{$_} )
  1124. if ( ReadingsVal($name, "input", "") ne $return->{result}[0]{$_} );
  1125. } elsif ( $_ eq "uri" ) {
  1126. readingsBulkUpdate( $hash, "uri", $return->{result}[0]{$_} )
  1127. if ( ReadingsVal($name, "uri", "") ne $return->{result}[0]{$_} );
  1128. # set TV input uri to last tv-norm (tv:dvbt, tv:dvbs)
  1129. $hash->{helper}{device}{inputPreset}{TV}{uri} = $return->{result}[0]{$_}
  1130. if (defined($hash->{helper}{device}{inputPreset}) && $return->{result}[0]{$_} =~ /tv:.*/);
  1131. } else {
  1132. readingsBulkUpdate( $hash, "ci_".$_, $return->{result}[0]{$_} )
  1133. if ( ReadingsVal($name, "ci_".$_, "") ne $return->{result}[0]{$_} );
  1134. delete $contentKeys{"ci_".$_};
  1135. }
  1136. }
  1137. readingsEndUpdate( $hash, 1 );
  1138. } elsif ( ref($return->{error}) eq "ARRAY" && $return->{error}[0] eq "7" && $return->{error}[1] eq "Illegal State" ) {
  1139. #could be timeshift mode
  1140. BRAVIA_SendCommand( $hash, "getScheduleList" );
  1141. return;
  1142. }
  1143. }
  1144. } else {
  1145. if ( ReadingsVal($name, "input", "") eq "Others" || ReadingsVal($name, "input", "") eq "Broadcast" ) {
  1146. $newstate = "off";
  1147. } else {
  1148. $newstate = "on";
  1149. }
  1150. }
  1151. readingsBeginUpdate($hash);
  1152. readingsBulkUpdate( $hash, "channel", $channelName )
  1153. if ( ReadingsVal($name, "channel", "") ne $channelName );
  1154. readingsBulkUpdate( $hash, "channelId", $channelNo )
  1155. if ( ReadingsVal($name, "channelId", "") ne $channelNo );
  1156. readingsBulkUpdate( $hash, "currentTitle", $currentTitle )
  1157. if ( ReadingsVal($name, "currentTitle", "") ne $currentTitle );
  1158. readingsBulkUpdate( $hash, "currentMedia", $currentMedia )
  1159. if ( ReadingsVal($name, "currentMedia", "") ne $currentMedia );
  1160. #remove outdated content information - replaces by "-"
  1161. foreach ( keys %contentKeys ) {
  1162. readingsBulkUpdate( $hash, $_, "-" );
  1163. }
  1164. readingsEndUpdate( $hash, 1 );
  1165. if ($channelName ne "-" && $channelNo ne "-") {
  1166. BRAVIA_SendCommand( $hash, "getContentList", ReadingsVal($name, "input", "") )
  1167. if (ReadingsVal($name, "requestFormat", "") eq "json"
  1168. && (!defined($hash->{helper}{device}{channelPreset}) || ReadingsVal($name, "state", "") ne "on"));
  1169. $hash->{helper}{device}{channelPreset}{ $channelNo }{id} = $channelNo;
  1170. $hash->{helper}{device}{channelPreset}{ $channelNo }{name} = $channelName;
  1171. }
  1172. # get current system settings
  1173. if ($newstate eq "on" && ReadingsVal($name, "upnp", "on") eq "on") {
  1174. BRAVIA_SendCommand( $hash, "upnp", "getVolume" );
  1175. BRAVIA_SendCommand( $hash, "upnp", "getMute" );
  1176. }
  1177. # load input list if just switched on
  1178. if ($newstate eq "on"
  1179. && (ReadingsVal($name, "state", "") ne "on" || !defined($hash->{helper}{device}{inputPreset}))
  1180. && ReadingsVal($name, "requestFormat", "") eq "json") {
  1181. BRAVIA_SendCommand( $hash, "getCurrentExternalInputsStatus" );
  1182. }
  1183. }
  1184. # getScheduleList
  1185. elsif ( $service eq "getScheduleList" ) {
  1186. my %contentKeys;
  1187. my $channelName = "-";
  1188. my $currentTitle = "-";
  1189. my $currentMedia = "-";
  1190. foreach ( keys %{ $hash->{READINGS} } ) {
  1191. $contentKeys{$_} = 1
  1192. if ( $_ =~ /^ci_.*/ and ReadingsVal($name, $_, "") ne "-" );
  1193. }
  1194. readingsBeginUpdate($hash);
  1195. if ( ref($return) eq "HASH" ) {
  1196. if (ref($return->{result}) eq "ARRAY") {
  1197. $newstate = "on";
  1198. foreach ( @{ $return->{result} } ) {
  1199. foreach ( @{ $_ } ) {
  1200. if ($_->{recordingStatus} eq "recording") {
  1201. my $key;
  1202. foreach $key ( keys %{ $_ }) {
  1203. if ( $key eq "type" ) {
  1204. $currentMedia = $_->{$key};
  1205. readingsBulkUpdate( $hash, "input", $_->{$key} )
  1206. if ( ReadingsVal($name, "input", "") ne $_->{$key} );
  1207. } elsif ( $key eq "channelName" ) {
  1208. $channelName = BRAVIA_GetNormalizedName($_->{$key});
  1209. } elsif ( $key eq "title" ) {
  1210. $currentTitle = $_->{$key};
  1211. } else {
  1212. readingsBulkUpdate( $hash, "ci_".$key, $_->{$key} )
  1213. if ( ReadingsVal($name, "ci_".$key, "") ne $_->{$key} );
  1214. delete $contentKeys{"ci_".$key};
  1215. }
  1216. }
  1217. }
  1218. }
  1219. }
  1220. }
  1221. }
  1222. readingsBulkUpdate( $hash, "channel", $channelName )
  1223. if ( ReadingsVal($name, "channel", "") ne $channelName );
  1224. readingsBulkUpdate( $hash, "currentTitle", $currentTitle )
  1225. if ( ReadingsVal($name, "currentTitle", "") ne $currentTitle );
  1226. readingsBulkUpdate( $hash, "currentMedia", $currentMedia )
  1227. if ( ReadingsVal($name, "currentMedia", "") ne $currentMedia );
  1228. #remove outdated content information - replaces by "-"
  1229. foreach ( keys %contentKeys ) {
  1230. readingsBulkUpdate( $hash, $_, "-" );
  1231. }
  1232. readingsEndUpdate( $hash, 1 );
  1233. # get current system settings
  1234. if (ReadingsVal($name, "upnp", "on") eq "on") {
  1235. BRAVIA_SendCommand( $hash, "upnp", "getVolume" );
  1236. BRAVIA_SendCommand( $hash, "upnp", "getMute" );
  1237. }
  1238. }
  1239. # getContentList
  1240. elsif ( $service eq "getContentList" ) {
  1241. my $channelIndex = 0;
  1242. if ( ref($return) eq "HASH" ) {
  1243. if (ref($return->{result}) eq "ARRAY") {
  1244. foreach ( @{ $return->{result} } ) {
  1245. foreach ( @{ $_ } ) {
  1246. my $channelNo;
  1247. my $channelName;
  1248. my $key;
  1249. foreach $key ( keys %{ $_ }) {
  1250. if ( $key eq "dispNum" ) {
  1251. $channelNo = $_->{$key};
  1252. } elsif ( $key eq "title" ) {
  1253. $channelName = BRAVIA_GetNormalizedName($_->{$key});
  1254. } elsif ( $key eq "index" ) {
  1255. $channelIndex = $_->{$key};
  1256. }
  1257. }
  1258. $hash->{helper}{device}{channelPreset}{ $channelNo }{id} = $channelNo;
  1259. $hash->{helper}{device}{channelPreset}{ $channelNo }{name} = $channelName;
  1260. }
  1261. }
  1262. }
  1263. }
  1264. # increment index, because it starts with 0
  1265. if (++$channelIndex % InternalVal($name, "CHANNELCOUNT", 50) == 0) {
  1266. # try next junk of channels
  1267. BRAVIA_SendCommand( $hash, "getContentList", ReadingsVal($name, "input", "")."|".$channelIndex );
  1268. }
  1269. }
  1270. # getCurrentExternalInputsStatus
  1271. elsif ( $service eq "getCurrentExternalInputsStatus" ) {
  1272. my $channelIndex = 0;
  1273. if ( ref($return) eq "HASH" ) {
  1274. if (ref($return->{result}) eq "ARRAY") {
  1275. foreach ( @{ $return->{result} } ) {
  1276. foreach ( @{ $_ } ) {
  1277. my $inputName;
  1278. my $inputUri;
  1279. my $key;
  1280. foreach $key ( keys %{ $_ }) {
  1281. if ( $key eq "uri" ) {
  1282. $inputUri = $_->{$key};
  1283. } elsif ( $key eq "title" ) {
  1284. $inputName = BRAVIA_GetNormalizedName($_->{$key});
  1285. }
  1286. }
  1287. $hash->{helper}{device}{inputPreset}{$inputName}{uri} = $inputUri;
  1288. }
  1289. }
  1290. my $tvUri = ReadingsVal($name, "uri", "tv");
  1291. $tvUri = "tv" if ($tvUri !~ /tv.*/);
  1292. $hash->{helper}{device}{inputPreset}{TV}{uri} = $tvUri;
  1293. }
  1294. }
  1295. }
  1296. # setPlayContent
  1297. elsif ( $service eq "setPlayContent" ) {
  1298. # nothing to do
  1299. }
  1300. # register
  1301. elsif ( $service eq "register" ) {
  1302. readingsBeginUpdate($hash);
  1303. if ( $header =~ /auth=([A-Za-z0-9]+)/ ) {
  1304. readingsBulkUpdate( $hash, "authCookie", $1 );
  1305. }
  1306. if ( $header =~ /[Ee]xpires=([^;]+)/ ) {
  1307. readingsBulkUpdate( $hash, "authExpires", $1 );
  1308. }
  1309. if ( $header =~ /[Mm]ax-[Aa]ge=(\d+)/ ) {
  1310. readingsBulkUpdate( $hash, "authMaxAge", $1 ) if (ReadingsVal($name, "authMaxAge", 0) != $1);
  1311. }
  1312. readingsEndUpdate( $hash, 1 );
  1313. }
  1314. # all other command results
  1315. else {
  1316. Log3 $name, 2, "BRAVIA $name: ERROR: method to handle response of $service not implemented";
  1317. }
  1318. return $newstate;
  1319. }
  1320. #####################################
  1321. sub BRAVIA_ClearContentInformation ($) {
  1322. my ($hash) = @_;
  1323. my $name = $hash->{NAME};
  1324. readingsBeginUpdate($hash);
  1325. #remove outdated content information - replaces by "-"
  1326. foreach ( keys %{ $hash->{READINGS} } ) {
  1327. readingsBulkUpdate($hash, $_, "-")
  1328. if ( $_ =~ /^ci_.*/ and ReadingsVal($name, $_, "") ne "-" );
  1329. }
  1330. readingsBulkUpdate( $hash, "channel", "-" )
  1331. if ( ReadingsVal($name, "channel", "") ne "-" );
  1332. readingsBulkUpdate( $hash, "channelId", "-" )
  1333. if ( ReadingsVal($name, "channelId", "") ne "-" );
  1334. readingsBulkUpdate( $hash, "currentTitle", "-" )
  1335. if ( ReadingsVal($name, "currentTitle", "") ne "-" );
  1336. readingsBulkUpdate( $hash, "currentMedia", "-" )
  1337. if ( ReadingsVal($name, "currentMedia", "") ne "-" );
  1338. readingsBulkUpdate( $hash, "input", "-" )
  1339. if ( ReadingsVal($name, "input", "") ne "-" );
  1340. readingsEndUpdate( $hash, 1 );
  1341. }
  1342. #####################################
  1343. # Callback from 95_remotecontrol for command makenotify.
  1344. sub BRAVIA_RCmakenotify($$) {
  1345. my ( $nam, $ndev ) = @_;
  1346. my $nname = "notify_$nam";
  1347. fhem( "define $nname notify $nam set $ndev remoteControl " . '$EVENT', 1 );
  1348. Log3 undef, 2, "[remotecontrol:BRAVIA] Notify created: $nname";
  1349. return "Notify created by BRAVIA: $nname";
  1350. }
  1351. #####################################
  1352. # RC layouts
  1353. # Sony TV with SVG
  1354. sub BRAVIA_RClayout_SVG() {
  1355. my @row;
  1356. $row[0] = "SOURCE:rc_AV.svg,:rc_BLANK.svg,:rc_BLANK.svg,POWER:rc_POWER.svg";
  1357. $row[1] = "TVPAUSE:rc_TVstop.svg,ASPECT,MODE3D,TRACKID";
  1358. $row[2] = "PREVIOUS:rc_PREVIOUS.svg,REWIND:rc_REW.svg,FORWARD:rc_FF.svg,NEXT:rc_NEXT.svg";
  1359. $row[3] = "REC:rc_REC.svg,PLAY:rc_PLAY.svg,PAUSE:rc_PAUSE.svg,STOP:rc_STOP.svg";
  1360. $row[4] = "RED:rc_RED.svg,GREEN:rc_GREEN.svg,YELLOW:rc_YELLOW.svg,BLUE:rc_BLUE.svg";
  1361. $row[5] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  1362. $row[6] = "HELP:rc_HELP.svg,SEN,SYNCMENU";
  1363. $row[7] = "GUIDE:rc_MENU.svg,UP:rc_UP.svg,INFO:rc_INFO.svg";
  1364. $row[8] = "LEFT:rc_LEFT.svg,OK:rc_OK.svg,RIGHT:rc_RIGHT.svg";
  1365. $row[9] = "RETURN:rc_BACK.svg,DOWN:rc_DOWN.svg,OPTIONS:rc_OPTIONS.svg,HOMEtxt";
  1366. $row[10] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  1367. $row[11] = "DIGITAL,EXIT:rc_EXIT.svg,TV:rc_TV.svg";
  1368. $row[12] = "1:rc_1.svg,2:rc_2.svg,3:rc_3.svg";
  1369. $row[13] = "4:rc_4.svg,5:rc_5.svg,6:rc_6.svg";
  1370. $row[14] = "7:rc_7.svg,8:rc_8.svg,9:rc_9.svg";
  1371. $row[15] = "TEXT:rc_TEXT.svg,0:rc_0.svg,SUBTITLE";
  1372. $row[16] = ":rc_BLANK.svg,:rc_BLANK.svg,:rc_BLANK.svg";
  1373. $row[17] = "MUTE:rc_MUTE.svg,VOLUP:rc_VOLPLUS.svg,CHANNELUP:rc_UP.svg,AUDIO:rc_AUDIO.svg";
  1374. $row[18] = ":rc_BLANK.svg,VOLDOWN:rc_VOLMINUS.svg,CHANNELDOWN:rc_DOWN.svg";
  1375. $row[19] = "attr rc_iconpath icons";
  1376. $row[20] = "attr rc_iconprefix rc_";
  1377. return @row;
  1378. }
  1379. # Sony TV with PNG
  1380. sub BRAVIA_RClayout() {
  1381. my @row;
  1382. $row[0] = "SOURCE,:blank,:blank,POWER:POWEROFF";
  1383. $row[1] = "TVPAUSE:TVstop,ASPECT,MODE3D,TRACKID";
  1384. $row[2] = "PREVIOUS,REWIND,FORWARD:FF,NEXT";
  1385. $row[3] = "REC,PLAY,PAUSE,STOP";
  1386. $row[4] = "RED,GREEN,YELLOW,BLUE";
  1387. $row[5] = ":blank,:blank,:blank";
  1388. $row[6] = "HELP,SEN,SYNCMENU";
  1389. $row[7] = "GUIDE,UP,INFO";
  1390. $row[8] = "LEFT,OK,RIGHT";
  1391. $row[9] = "RETURN,DOWN,OPTIONS:SUBMENU,HOMEtxt";
  1392. $row[10] = ":blank,:blank,:blank";
  1393. $row[11] = "DIGITAL,EXIT,TV";
  1394. $row[12] = "1,2,3";
  1395. $row[13] = "4,5,6";
  1396. $row[14] = "7,8,9";
  1397. $row[15] = "TEXT,0,SUBTITLE";
  1398. $row[16] = ":blank,:blank,:blank";
  1399. $row[17] = "MUTE,VOLUP:VOLUP2,CHANNELUP:CHUP2,AUDIO";
  1400. $row[18] = ":blank,VOLDOWN:VOLDOWN2,CHANNELDOWN:CHDOWN2";
  1401. return @row;
  1402. }
  1403. ###################################
  1404. # <command name="Confirm" type="ircc" value="AAAAAQAAAAEAAABlAw==" />
  1405. # <command name="Up" type="ircc" value="AAAAAQAAAAEAAAB0Aw==" />
  1406. # <command name="Down" type="ircc" value="AAAAAQAAAAEAAAB1Aw==" />
  1407. # <command name="Right" type="ircc" value="AAAAAQAAAAEAAAAzAw==" />
  1408. # <command name="Left" type="ircc" value="AAAAAQAAAAEAAAA0Aw==" />
  1409. # <command name="Home" type="ircc" value="AAAAAQAAAAEAAABgAw==" />
  1410. # <command name="Options" type="ircc" value="AAAAAgAAAJcAAAA2Aw==" />
  1411. # <command name="Return" type="ircc" value="AAAAAgAAAJcAAAAjAw==" />
  1412. # <command name="Num1" type="ircc" value="AAAAAQAAAAEAAAAAAw==" />
  1413. # <command name="Num2" type="ircc" value="AAAAAQAAAAEAAAABAw==" />
  1414. # <command name="Num3" type="ircc" value="AAAAAQAAAAEAAAACAw==" />
  1415. # <command name="Num4" type="ircc" value="AAAAAQAAAAEAAAADAw==" />
  1416. # <command name="Num5" type="ircc" value="AAAAAQAAAAEAAAAEAw==" />
  1417. # <command name="Num6" type="ircc" value="AAAAAQAAAAEAAAAFAw==" />
  1418. # <command name="Num7" type="ircc" value="AAAAAQAAAAEAAAAGAw==" />
  1419. # <command name="Num8" type="ircc" value="AAAAAQAAAAEAAAAHAw==" />
  1420. # <command name="Num9" type="ircc" value="AAAAAQAAAAEAAAAIAw==" />
  1421. # <command name="Num0" type="ircc" value="AAAAAQAAAAEAAAAJAw==" />
  1422. # <command name="Num11" type="ircc" value="AAAAAQAAAAEAAAAKAw==" />
  1423. # <command name="Num12" type="ircc" value="AAAAAQAAAAEAAAALAw==" />
  1424. # <command name="Power" type="ircc" value="AAAAAQAAAAEAAAAVAw==" />
  1425. # <command name="Display" type="ircc" value="AAAAAQAAAAEAAAA6Aw==" />
  1426. # <command name="VolumeUp" type="ircc" value="AAAAAQAAAAEAAAASAw==" />
  1427. # <command name="VolumeDown" type="ircc" value="AAAAAQAAAAEAAAATAw==" />
  1428. # <command name="Mute" type="ircc" value="AAAAAQAAAAEAAAAUAw==" />
  1429. # <command name="Audio" type="ircc" value="AAAAAQAAAAEAAAAXAw==" />
  1430. # <command name="SubTitle" type="ircc" value="AAAAAgAAAJcAAAAoAw==" />
  1431. # <command name="Yellow" type="ircc" value="AAAAAgAAAJcAAAAnAw==" />
  1432. # <command name="Blue" type="ircc" value="AAAAAgAAAJcAAAAkAw==" />
  1433. # <command name="Red" type="ircc" value="AAAAAgAAAJcAAAAlAw==" />
  1434. # <command name="Green" type="ircc" value="AAAAAgAAAJcAAAAmAw==" />
  1435. # <command name="Play" type="ircc" value="AAAAAgAAAJcAAAAaAw==" />
  1436. # <command name="Stop" type="ircc" value="AAAAAgAAAJcAAAAYAw==" />
  1437. # <command name="Pause" type="ircc" value="AAAAAgAAAJcAAAAZAw==" />
  1438. # <command name="Rewind" type="ircc" value="AAAAAgAAAJcAAAAbAw==" />
  1439. # <command name="Forward" type="ircc" value="AAAAAgAAAJcAAAAcAw==" />
  1440. # <command name="Prev" type="ircc" value="AAAAAgAAAJcAAAA8Aw==" />
  1441. # <command name="Next" type="ircc" value="AAAAAgAAAJcAAAA9Aw==" />
  1442. # <command name="Replay" type="ircc" value="AAAAAgAAAJcAAAB5Aw==" />
  1443. # <command name="Advance" type="ircc" value="AAAAAgAAAJcAAAB4Aw==" />
  1444. # <command name="TopMenu" type="ircc" value="AAAAAgAAABoAAABgAw==" />
  1445. # <command name="PopUpMenu" type="ircc" value="AAAAAgAAABoAAABhAw==" />
  1446. # <command name="Eject" type="ircc" value="AAAAAgAAAJcAAABIAw==" />
  1447. # <command name="Rec" type="ircc" value="AAAAAgAAAJcAAAAgAw==" />
  1448. # <command name="SyncMenu" type="ircc" value="AAAAAgAAABoAAABYAw==" />
  1449. # <command name="ClosedCaption" type="ircc" value="AAAAAgAAAKQAAAAQAw==" />
  1450. # <command name="Teletext" type="ircc" value="AAAAAQAAAAEAAAA/Aw==" />
  1451. # <command name="ChannelUp" type="ircc" value="AAAAAQAAAAEAAAAQAw==" />
  1452. # <command name="ChannelDown" type="ircc" value="AAAAAQAAAAEAAAARAw==" />
  1453. # <command name="Input" type="ircc" value="AAAAAQAAAAEAAAAlAw==" />
  1454. # <command name="GGuide" type="ircc" value="AAAAAQAAAAEAAAAOAw==" />
  1455. # <command name="EPG" type="ircc" value="AAAAAgAAAKQAAABbAw==" />
  1456. # 755 <command name="Enter" type="ircc" value="AAAAAQAAAAEAAAALAw==" />
  1457. # <command name="DOT" type="ircc" value="AAAAAgAAAJcAAAAdAw==" />
  1458. # <command name="Analog" type="ircc" value="AAAAAgAAAHcAAAANAw==" />
  1459. # <command name="Exit" type="ircc" value="AAAAAQAAAAEAAABjAw==" />
  1460. # 755 <command name="*AD" type="ircc" value="AAAAAgAAABoAAAA7Aw==" />
  1461. # <command name="Digital" type="ircc" value="AAAAAgAAAJcAAAAyAw==" />
  1462. # 755 <command name="Analog?" type="ircc" value="AAAAAgAAAJcAAAAuAw==" />
  1463. # <command name="BS" type="ircc" value="AAAAAgAAAJcAAAAsAw==" />
  1464. # <command name="CS" type="ircc" value="AAAAAgAAAJcAAAArAw==" />
  1465. # <command name="BSCS" type="ircc" value="AAAAAgAAAJcAAAAQAw==" />
  1466. # <command name="Ddata" type="ircc" value="AAAAAgAAAJcAAAAVAw==" />
  1467. # 755 <command name="SEN" type="ircc" value="AAAAAgAAABoAAAB9Aw==" />
  1468. # 755 <command name="Netflix" type="ircc" value="AAAAAgAAABoAAAB8Aw==" />
  1469. # <command name="InternetWidgets" type="ircc" value="AAAAAgAAABoAAAB6Aw==" />
  1470. # <command name="InternetVideo" type="ircc" value="AAAAAgAAABoAAAB5Aw==" />
  1471. # <command name="SceneSelect" type="ircc" value="AAAAAgAAABoAAAB4Aw==" />
  1472. # <command name="Mode3D" type="ircc" value="AAAAAgAAAHcAAABNAw==" />
  1473. # <command name="iManual" type="ircc" value="AAAAAgAAABoAAAB7Aw==" />
  1474. # <command name="Wide" type="ircc" value="AAAAAgAAAKQAAAA9Aw==" />
  1475. # <command name="Jump" type="ircc" value="AAAAAQAAAAEAAAA7Aw==" />
  1476. # <command name="PAP" type="ircc" value="AAAAAgAAAKQAAAB3Aw==" />
  1477. # <command name="MyEPG" type="ircc" value="AAAAAgAAAHcAAABrAw==" />
  1478. # <command name="ProgramDescription" type="ircc" value="AAAAAgAAAJcAAAAWAw==" />
  1479. # <command name="WriteChapter" type="ircc" value="AAAAAgAAAHcAAABsAw==" />
  1480. # <command name="TrackID" type="ircc" value="AAAAAgAAABoAAAB+Aw==" />
  1481. # <command name="TenKey" type="ircc" value="AAAAAgAAAJcAAAAMAw==" />
  1482. # <command name="AppliCast" type="ircc" value="AAAAAgAAABoAAABvAw==" />
  1483. # <command name="acTVila" type="ircc" value="AAAAAgAAABoAAAByAw==" />
  1484. # <command name="DeleteVideo" type="ircc" value="AAAAAgAAAHcAAAAfAw==" />
  1485. # <command name="EasyStartUp" type="ircc" value="AAAAAgAAAHcAAABqAw==" />
  1486. # <command name="OneTouchTimeRec" type="ircc" value="AAAAAgAAABoAAABkAw==" />
  1487. # <command name="OneTouchView" type="ircc" value="AAAAAgAAABoAAABlAw==" />
  1488. # <command name="OneTouchRec" type="ircc" value="AAAAAgAAABoAAABiAw==" />
  1489. # <command name="OneTouchRecStop" type="ircc" value="AAAAAgAAABoAAABjAw==" />
  1490. # <command name="Analog2" type="ircc" value="AAAAAQAAAAEAAAA4Aw==" />
  1491. # 755 <command name="Tv_Radio" type="ircc" value="AAAAAgAAABoAAABXAw==" />
  1492. # 755 <command name="PhotoFrame" type="ircc" value="AAAAAgAAABoAAABVAw==" />
  1493. # 755 <command name="TvPause" type="ircc" value="AAAAAgAAABoAAABnAw==" />
  1494. # <command name="MuteOn" type="url" value="http://192.168.2.43:80/cers/command/MuteOn" />
  1495. # <command name="MuteOff" type="url" value="http://192.168.2.43:80/cers/command/MuteOff" />
  1496. # 755 <command name="PowerOff" type="ircc" value="AAAAAQAAAAEAAAAvAw==" />
  1497. # 755 <command name="ZoomIn" type="url" value="http://192.168.2.43:80/cers/command/ZoomIn" />
  1498. # 755 <command name="ZoomOut" type="url" value="http://192.168.2.43:80/cers/command/ZoomOut" />
  1499. # 755 <command name="BrowserBack" type="url" value="http://192.168.2.43:80/cers/command/BrowserBack" />
  1500. # 755 <command name="BrowserForward" type="url" value="http://192.168.2.43:80/cers/command/BrowserForward" />
  1501. # 755 <command name="BrowserReload" type="url" value="http://192.168.2.43:80/cers/command/BrowserReload" />
  1502. # 755 <command name="BrowserStop" type="url" value="http://192.168.2.43:80/cers/command/BrowserStop" />
  1503. # 755 <command name="BrowserBookmarkList" type="url" value="http://192.168.2.43:80/cers/command/BrowserBookmarkList" />
  1504. sub BRAVIA_GetRemotecontrolCommand($) {
  1505. my ($command) = @_;
  1506. my $commands = {
  1507. 'POWER' => "AAAAAQAAAAEAAAAVAw==",
  1508. 'STANDBY' => "AAAAAQAAAAEAAAAvAw==",
  1509. 'EXIT' => "AAAAAQAAAAEAAABjAw==",
  1510. 'RED' => "AAAAAgAAAJcAAAAlAw==",
  1511. 'GREEN' => "AAAAAgAAAJcAAAAmAw==",
  1512. 'YELLOW' => "AAAAAgAAAJcAAAAnAw==",
  1513. 'BLUE' => "AAAAAgAAAJcAAAAkAw==",
  1514. 'HOME' => "AAAAAQAAAAEAAABgAw==",
  1515. 'VOLUP' => "AAAAAQAAAAEAAAASAw==",
  1516. 'VOLUMEUP' => "AAAAAQAAAAEAAAASAw==",
  1517. 'VOLDOWN' => "AAAAAQAAAAEAAAATAw==",
  1518. 'VOLUMEDOWN' => "AAAAAQAAAAEAAAATAw==",
  1519. 'MUTE' => "AAAAAQAAAAEAAAAUAw==",
  1520. 'OPTIONS' => "AAAAAgAAAJcAAAA2Aw==",
  1521. 'DOT' => "AAAAAgAAAJcAAAAdAw==",
  1522. '0' => "AAAAAQAAAAEAAAAJAw==",
  1523. '1' => "AAAAAQAAAAEAAAAAAw==",
  1524. '2' => "AAAAAQAAAAEAAAABAw==",
  1525. '3' => "AAAAAQAAAAEAAAACAw==",
  1526. '4' => "AAAAAQAAAAEAAAADAw==",
  1527. '5' => "AAAAAQAAAAEAAAAEAw==",
  1528. '6' => "AAAAAQAAAAEAAAAFAw==",
  1529. '7' => "AAAAAQAAAAEAAAAGAw==",
  1530. '8' => "AAAAAQAAAAEAAAAHAw==",
  1531. '9' => "AAAAAQAAAAEAAAAIAw==",
  1532. 'GUIDE' => "AAAAAQAAAAEAAAAOAw==",
  1533. 'INFO' => "AAAAAQAAAAEAAAA6Aw==",
  1534. 'UP' => "AAAAAQAAAAEAAAB0Aw==",
  1535. 'DOWN' => "AAAAAQAAAAEAAAB1Aw==",
  1536. 'LEFT' => "AAAAAQAAAAEAAAA0Aw==",
  1537. 'RIGHT' => "AAAAAQAAAAEAAAAzAw==",
  1538. 'OK' => "AAAAAQAAAAEAAABlAw==",
  1539. 'RETURN' => "AAAAAgAAAJcAAAAjAw==",
  1540. 'NEXT' => "AAAAAgAAAJcAAAA9Aw==",
  1541. 'PREVIOUS' => "AAAAAgAAAJcAAAA8Aw==",
  1542. 'TV' => "AAAAAgAAABoAAABXAw==",
  1543. 'TVPAUSE' => "AAAAAgAAABoAAABnAw==",
  1544. 'MODE3D' => "AAAAAgAAAHcAAABNAw==",
  1545. 'TEXT' => "AAAAAQAAAAEAAAA/Aw==",
  1546. 'SUBTITLE' => "AAAAAgAAAJcAAAAoAw==",
  1547. 'CHANUP' => "AAAAAQAAAAEAAAAQAw==",
  1548. 'CHANNELUP' => "AAAAAQAAAAEAAAAQAw==",
  1549. 'CHANDOWN' => "AAAAAQAAAAEAAAARAw==",
  1550. 'CHANNELDOWN' => "AAAAAQAAAAEAAAARAw==",
  1551. 'SOURCE' => "AAAAAQAAAAEAAAAlAw==",
  1552. 'PLAY' => "AAAAAgAAAJcAAAAaAw==",
  1553. 'PAUSE' => "AAAAAgAAAJcAAAAZAw==",
  1554. 'FORWARD' => "AAAAAgAAAJcAAAAcAw==",
  1555. 'STOP' => "AAAAAgAAAJcAAAAYAw==",
  1556. 'REWIND' => "AAAAAgAAAJcAAAAbAw==",
  1557. 'RECORD' => "AAAAAgAAAJcAAAAgAw==",
  1558. 'ASPECT' => "AAAAAQAAAAEAAAA6Aw==",
  1559. 'HELP' => "AAAAAgAAABoAAAB7Aw==",
  1560. 'DIGITAL' => "AAAAAgAAABoAAAA7Aw==",
  1561. 'TRACKID' => "AAAAAgAAABoAAAB+Aw==",
  1562. 'AUDIO' => "AAAAAQAAAAEAAAAXAw==",
  1563. 'SEN' => "AAAAAgAAABoAAAB9Aw==",
  1564. 'SYNCMENU' => "AAAAAgAAABoAAABYAw==",
  1565. 'SCENESELECT' => "AAAAAgAAABoAAAB4Aw==",
  1566. 'NETFLIX' => "AAAAAgAAABoAAAB8Aw==",
  1567. 'PICTUREMODE' => "AAAAAQAAAAEAAABkAw==",
  1568. };
  1569. if ( defined( $commands->{$command} ) ) {
  1570. return $commands->{$command};
  1571. }
  1572. elsif ( $command eq "GetRemotecontrolCommands" ) {
  1573. return $commands;
  1574. }
  1575. else {
  1576. # return command itself if not mapped
  1577. return $command;
  1578. }
  1579. }
  1580. sub BRAVIA_GetModelYear($) {
  1581. my ($command) = @_;
  1582. my $commands = {
  1583. '1.0' => "2011",
  1584. '1.1' => "2012",
  1585. '1.0.4' => "2013",
  1586. '1.0.5' => "2013", #KDL42-W655A
  1587. '2.4.0' => "2014",
  1588. '3.8.0' => "2016", #KD-55XD8505
  1589. };
  1590. if (defined( $commands->{$command})) {
  1591. return $commands->{$command};
  1592. } else {
  1593. return "";
  1594. }
  1595. }
  1596. sub BRAVIA_GetIrccRequest($) {
  1597. my ($cmd) = @_;
  1598. my $data = "<?xml version=\"1.0\"?>";
  1599. $data .= "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
  1600. $data .= "<s:Body>";
  1601. $data .= "<u:X_SendIRCC xmlns:u=\"urn:schemas-sony-com:service:IRCC:1\">";
  1602. $data .= "<IRCCCode>" . $cmd . "</IRCCCode>";
  1603. $data .= "</u:X_SendIRCC>";
  1604. $data .= "</s:Body>";
  1605. $data .= "</s:Envelope>";
  1606. return $data;
  1607. }
  1608. sub BRAVIA_GetUpnpRequest($$) {
  1609. my ($cmd,$value) = @_;
  1610. my $data = "<?xml version=\"1.0\"?>";
  1611. $data .= "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">";
  1612. $data .= "<s:Body>";
  1613. if ($cmd eq "getVolume") {
  1614. $data .= "<u:GetVolume xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">";
  1615. $data .= "<InstanceID>0</InstanceID>";
  1616. $data .= "<Channel>Master</Channel>";
  1617. $data .= "</u:GetVolume>";
  1618. } elsif ($cmd eq "setVolume") {
  1619. $data .= "<u:SetVolume xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">";
  1620. $data .= "<InstanceID>0</InstanceID>";
  1621. $data .= "<Channel>Master</Channel>";
  1622. $data .= "<DesiredVolume>";
  1623. $data .= $value;
  1624. $data .= "</DesiredVolume>";
  1625. $data .= "</u:SetVolume>";
  1626. } elsif ($cmd eq "getMute") {
  1627. $data .= "<u:GetMute xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">";
  1628. $data .= "<InstanceID>0</InstanceID>";
  1629. $data .= "<Channel>Master</Channel>";
  1630. $data .= "</u:GetMute>";
  1631. } elsif ($cmd eq "setMute") {
  1632. $data .= "<u:SetMute xmlns:u=\"urn:schemas-upnp-org:service:RenderingControl:1\">";
  1633. $data .= "<InstanceID>0</InstanceID>";
  1634. $data .= "<Channel>Master</Channel>";
  1635. $data .= "<DesiredMute>";
  1636. $data .= $value;
  1637. $data .= "</DesiredMute>";
  1638. $data .= "</u:SetMute>";
  1639. }
  1640. $data .= "</s:Body>";
  1641. $data .= "</s:Envelope>";
  1642. return $data;
  1643. }
  1644. sub BRAVIA_CheckRegistration($) {
  1645. my ( $hash ) = @_;
  1646. my $name = $hash->{NAME};
  1647. if (ReadingsVal($name, "authCookie", "") ne "" and
  1648. ReadingsTimestamp($name, "authCookie", "") =~ m/^(\d{4})-(\d{2})-(\d{2}) ([0-2]\d):([0-5]\d):([0-5]\d)$/) {
  1649. my $time = fhemTimeLocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
  1650. # max age defaults to 14 days
  1651. my $maxAge = ReadingsNum($name, "authMaxAge", 1209600);
  1652. # renew registration after half period of validity
  1653. if ($time + $maxAge/2 < time()) {
  1654. Log3 $name, 3, "BRAVIA $name: renew registration";
  1655. BRAVIA_SendCommand( $hash, "register", "renew" );
  1656. }
  1657. }
  1658. }
  1659. sub BRAVIA_GetNormalizedName($) {
  1660. my ( $name ) = @_;
  1661. $name =~ s/^\s+//;
  1662. $name =~ s/\s+$//;
  1663. $name =~ s/\s/_/g;
  1664. $name =~ s/,/./g;
  1665. return $name;
  1666. }
  1667. 1;
  1668. =pod
  1669. =item summary controls a Sony TV device of series starting from 2011 via LAN
  1670. =begin html
  1671. <a name="BRAVIA"></a>
  1672. <h3>BRAVIA</h3>
  1673. <ul>
  1674. This module controls a Sony TV device over ethernet. Devices of series starting from 2011 are supported.
  1675. <br><br>
  1676. <b>Define</b>
  1677. <ul>
  1678. <code>define &lt;name&gt; BRAVIA &lt;ip-or-hostname&gt; [&lt;poll-interval&gt;]</code>
  1679. <br><br>
  1680. With definition of a BRAVIA device an internal task will be scheduled.
  1681. This task pulls frequently the status and other information from the TV.<br>
  1682. The intervall can be defined in seconds by an optional parameter &lt;poll-intervall&gt;.
  1683. The default value is 45 seconds.
  1684. <br><br>
  1685. After definition of a device using this module it has to be registered as a remote control
  1686. (<a href=#BRAVIAregister><code>set register</code></a>).
  1687. <br><br>
  1688. As long as readings are not among the usual AV readings they are clustered:
  1689. <table>
  1690. <tr><td>s_*</td><td>: status</td></tr>
  1691. <tr><td>ci_*</td><td>: content information</td></tr>
  1692. </table>
  1693. <br><br>
  1694. The module contains predefined layouts for <a href=#remotecontrol>remotecontrol</a> using PNG and SVG.
  1695. <br><br>
  1696. </ul>
  1697. <a name="BRAVIAset"></a>
  1698. <b>Set</b>
  1699. <ul>
  1700. <code>set &lt;name&gt; &lt;option&gt; &lt;value&gt;</code>
  1701. <br><br>
  1702. Options:
  1703. <ul>
  1704. <li><i>channel</i><br>
  1705. List of all known channels. The module collects all visited channels.
  1706. Channels can be loaded automtically with modells from 2013 and newer.
  1707. (number of channels, see <a href=#BRAVIAchannelsMax>channelsMax</a>).</li>
  1708. <li><i>channelDown</i><br>
  1709. Switches a channel back.</li>
  1710. <li><i>channelUp</i><br>
  1711. Switches a channel forward.</li>
  1712. <li><i>mute</i><br>
  1713. Set mute if <a href=#BRAVIAupnp>Upnp</a> is activated.</li>
  1714. <li><i>off</i><br>
  1715. Switches TV to off. State of device will have been set to "set_off" for 60 seconds or until off-status is pulled from TV.</li>
  1716. <li><a name="BRAVIAon"></a><i>on</i><br>
  1717. Switches TV to on, with modells from 2013 using WOL. State of device will have been set to "set_on" for 60 seconds or until on-status is pulled from TV.</li>
  1718. <li><i>pause</i><br>
  1719. Pauses a playing of a recording, of an internal App, etc.</li>
  1720. <li><i>play</i><br>
  1721. Starts playing of a recording, of an internal App, etc.</li>
  1722. <li><i>record</i><br>
  1723. Starts recording of current content.</li>
  1724. <li><a name="BRAVIAregister"></a><i>register</i><br>
  1725. One-time registration of Fhem as remote control in the TV.<br>
  1726. With <a href=#BRAVIArequestFormat>requestFormat</a> = "xml" registration works without parameter.<br>
  1727. With <a href=#BRAVIArequestFormat>requestFormat</a> = "json" registration has to be executed twice.<br>
  1728. The register option offers an additional input field:
  1729. <ol>
  1730. <li>Call with empty input. A PIN for registration has to be shown on the TV.</li>
  1731. <li>Insert PIN into input field and register again.</li></ol></li>
  1732. <li><a name="BRAVIArequestFormat"></a><i>requestFormat</i><br>
  1733. "xml" for xml based communication (modells from 2011 and 2012)<br>
  1734. "json" for communication with modells from 2013 and newer</li>
  1735. <li><i>remoteControl</i><br>
  1736. Sends command directly to TV.</li>
  1737. <li><i>statusRequest</i><br>
  1738. Retrieves current status information from TV.</li>
  1739. <li><i>stop</i><br>
  1740. Stops recording, playing of an internal App, etc.</li>
  1741. <li><i>toggle</i><br>
  1742. Toggles power status of TV.</li>
  1743. <li><i>tvpause</i><br>
  1744. Activates Timeshift mode.</li>
  1745. <li><a name="BRAVIAupnp"></a><i>upnp</i><br>
  1746. Activates Upnp service used to control volume.</li>
  1747. <li><i>volume</i><br>
  1748. Straight setting of volume. <a href=#BRAVIAupnp>Upnp</a> service has to be activated.</li>
  1749. <li><i>volumeDown</i><br>
  1750. Decreases volume.</li>
  1751. <li><i>volumeUp</i><br>
  1752. Increases volume.</li>
  1753. </ul>
  1754. </ul>
  1755. <br>
  1756. <a name="BRAVIAattr"></a>
  1757. <b>Attributes</b>
  1758. <ul>
  1759. <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
  1760. <br><br>
  1761. Attributes:
  1762. <ul>
  1763. <li><a name="BRAVIAchannelsMax"></a><i>channelsMax</i><br>
  1764. Maximum amount of channels to be displayed, default is 50.</li>
  1765. <li><a name="BRAVIAmacaddr"></a><i>macaddr</i><br>
  1766. Enables power on of TV using WOL.</li>
  1767. </ul>
  1768. </ul>
  1769. </ul>
  1770. =end html
  1771. =item summary_DE steuert Sony TVs der BRAVIA-Serien ab dem Modelljahr 2011, via LAN-Verbindung
  1772. =begin html_DE
  1773. <a name="BRAVIA"></a>
  1774. <h3>BRAVIA</h3>
  1775. <ul>
  1776. Diese Module dient zur Steuerung von Sony TVs der BRAVIA-Serien beginnend mit dem Modelljahr 2011.
  1777. <br><br>
  1778. <b>Define</b>
  1779. <ul>
  1780. <code>define &lt;name&gt; BRAVIA &lt;ip-or-hostname&gt; [&lt;poll-interval&gt;]</code>
  1781. <br><br>
  1782. Bei der Definition eines BRAVIA Gerätes wird ein interner Task eingeplant,
  1783. der regelmäßig den Status des TV prüft und weitere Informationen abruft.<br>
  1784. Das Intervall des Tasks kann durch den optionalen Parameter &lt;poll-intervall&gt; in Sekunden gesetzt werden.
  1785. Ansonsten wird der Task mit 45 Sekunden als Intervall definiert.
  1786. <br><br>
  1787. Nach der Definition eines Gerätes muss dieses einmalig im TV als Fernbedienung
  1788. registriert werden (<a href=#BRAVIAregister><code>set register</code></a>).
  1789. <br><br>
  1790. Soweit die Readings nicht den allgemeinen AV Readings entsprechen, sind sie gruppiert:
  1791. <table>
  1792. <tr><td>s_*</td><td>: Status</td></tr>
  1793. <tr><td>ci_*</td><td>: Inhaltsinfo</td></tr>
  1794. </table>
  1795. <br><br>
  1796. Das Modul enthält vorgefertigte Layouts für <a href=#remotecontrol>remotecontrol</a> mit PNG und SVG.
  1797. <br><br>
  1798. </ul>
  1799. <a name="BRAVIAset"></a>
  1800. <b>Set</b>
  1801. <ul>
  1802. <code>set &lt;name&gt; &lt;option&gt; &lt;value&gt;</code>
  1803. <br><br>
  1804. Optionen:
  1805. <ul>
  1806. <li><i>channel</i><br>
  1807. Liste alle bekannten Kanäle. Das Modul merkt sich alle aufgerufenen Kanäle.
  1808. Ab Modelljahr 2013 werden die Kanäle automatisch geladen
  1809. (Anzahl siehe <a href=#BRAVIAchannelsMax>channelsMax</a>).</li>
  1810. <li><i>channelDown</i><br>
  1811. Einen Kanal zurück schalten.</li>
  1812. <li><i>channelUp</i><br>
  1813. Einen Kanal weiter schalten.</li>
  1814. <li><i>mute</i><br>
  1815. Direkte Stummschaltung erfolgt nur per aktiviertem <a href=#BRAVIAupnp>Upnp</a>.</li>
  1816. <li><i>off</i><br>
  1817. Schaltet den TV aus. Der State des Gerätes wird auf "set_off" gesetzt. Dieser Wert wird nach 60 Sekunden wieder überschrieben oder sobald der TV entsprechend "off" meldet.</li>
  1818. <li><a name="BRAVIAon"></a><i>on</i><br>
  1819. Einschalten des TV, ab Modelljahr 2013 per WOL. Der State des Gerätes wird auf "set_on" gesetzt. Dieser Wert wird nach 60 Sekunden wieder überschrieben oder sobald der TV entsprechend "on" meldet.</li>
  1820. <li><i>pause</i><br>
  1821. Pausiert die Wiedergabe einer Aufnahme, einer internen App, etc.</li>
  1822. <li><i>play</i><br>
  1823. Startet die Wiedergabe einer Aufnahme, einer internen App, etc.</li>
  1824. <li><i>record</i><br>
  1825. Startet die Aufnahme des aktuellen Inhalts.</li>
  1826. <li><a name="BRAVIAregister"></a><i>register</i><br>
  1827. Einmalige Registrierung von FHEM als Fernbedienung im TV.<br>
  1828. Bei <a href=#BRAVIArequestFormat>requestFormat</a> = "xml" erfolgt die Registrierung ohne Parameter.<br>
  1829. Bei <a href=#BRAVIArequestFormat>requestFormat</a> = "json" ist die Registrierung zweistufig.<br>
  1830. Beim Aufruf des Setter gibt es ein Eingabefeld:
  1831. <ol>
  1832. <li>Aufruf mit leerem Eingabefeld. Auf dem TV sollte eine PIN zur Registrierung erscheinen.</li>
  1833. <li>PIN im Eingabefeld eintragen und Registrierung noch mal ausführen</li></ol></li>
  1834. <li><a name="BRAVIArequestFormat"></a><i>requestFormat</i><br>
  1835. "xml" für xml-basierte Kommunikation 2011er/2012er Geräte<br>
  1836. "json" für die Kommunikation seit der 2013er Generation</li>
  1837. <li><i>remoteControl</i><br>
  1838. Direktes Senden von Kommandos an den TV.</li>
  1839. <li><i>statusRequest</i><br>
  1840. Ruft die aktuellen Statusinformationen vom TV ab.</li>
  1841. <li><i>stop</i><br>
  1842. Stoppt die Wiedergabe einer Aufnahme, einer internen App, etc.</li>
  1843. <li><i>toggle</i><br>
  1844. Wechselt den Einschaltstatus des TV.</li>
  1845. <li><i>tvpause</i><br>
  1846. Aktiviert den Timeshift-Modus.</li>
  1847. <li><a name="BRAVIAupnp"></a><i>upnp</i><br>
  1848. Aktiviert Upnp zum Abfragen und Einstellen der Lautstärke.</li>
  1849. <li><i>volume</i><br>
  1850. Direktes Setzen der Lautstärke erfolgt nur per aktiviertem <a href=#BRAVIAupnp>Upnp</a>.</li>
  1851. <li><i>volumeDown</i><br>
  1852. Verringert die Lautstärke.</li>
  1853. <li><i>volumeUp</i><br>
  1854. Erhöht die Lautstärke.</li>
  1855. </ul>
  1856. </ul>
  1857. <br>
  1858. <a name="BRAVIAattr"></a>
  1859. <b>Attributes</b>
  1860. <ul>
  1861. <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
  1862. <br><br>
  1863. Attribute:
  1864. <ul>
  1865. <li><a name="BRAVIAchannelsMax"></a><i>channelsMax</i><br>
  1866. Maximale Anzahl der im FHEMWEB angezeigten Kanäle. Der Standartwert ist 50.</li>
  1867. <li><a name="BRAVIAmacaddr"></a><i>macaddr</i><br>
  1868. Ermöglicht das Einschalten des TV per WOL.</li>
  1869. </ul>
  1870. </ul>
  1871. </ul>
  1872. =end html_DE
  1873. =cut